<rss version="2.0" xmlns:yandex="http://news.yandex.ru" xmlns:media="http://search.yahoo.com/mrss/" xmlns:turbo="http://turbo.yandex.ru" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Михаил Юдин</title><link>https://mikhail-yudin.ru/projects/</link><description/><generator>Hugo -- gohugo.io</generator><language>ru</language><lastBuildDate>Sat, 02 May 2020 00:00:00 +0000</lastBuildDate><image><url>https://mikhail-yudin.ru/images/brand/favicon.png</url><title>Михаил Юдин</title><link>https://mikhail-yudin.ru/</link></image><atom:link href="https://mikhail-yudin.ru/projects/index.xml" rel="self" type="application/rss+xml"/><item turbo="true"><title>Блокнот онлайн</title><link>https://mikhail-yudin.ru/projects/notepad/</link><pubDate>Sat, 02 May 2020 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/projects/notepad/</guid><description>Онлайн блокнот В этом простом онлайн блоконте вы можете делать заметки, составлять списки, которые сохранятся в вашем браузере.
Чтоб не искать блокнот, добавьте на рабочий стол его для быстрого доступа через меню в браузере или в закладки.
Пока блокнот работает онлайн, но скоро добавится возможность оставлять заметки без доступа к интернету как в обычном мобильном приложении.
В блокноте добавлена поддержка истории, пока без периодического сохранения &amp;mdash; вручную, т.к. ещё не решил, как часто стоит это делать.</description><enclosure url="https://mikhail-yudin.ru/projects/notepad/pencil-notepad-notebook-paper.jpg" type="image/jpeg"/><turbo:content><![CDATA[
      		<header>
      			<h1>Блокнот онлайн</h1></header>
            <div class='entry-content'>
              <script src="/theme-switcher.js"></script>
<link rel="stylesheet"
			href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap"/>
<link rel="stylesheet" href="/notepad.css"/>
<script src="https://code.iconify.design/1/1.0.6/iconify.min.js"></script>
<div id="notepad" tabindex="1" contenteditable spellcheck="false">
	<h1>Онлайн блокнот</h1>
	<p>В этом простом <b>онлайн блоконте</b> вы можете делать <b>заметки</b>, составлять <b>списки</b>, которые <i>сохранятся
		в вашем браузере</i>.</p>
	<p>Чтоб не искать <b>блокнот</b>, <b>добавьте на рабочий стол</b> его для быстрого доступа через меню в браузере или
		<b>в закладки</b>.</p>
	<p>Пока <i>блокнот работает онлайн</i>, но скоро добавится возможность оставлять заметки без доступа к интернету как в
		обычном мобильном приложении.</p>
	<p>В блокноте добавлена поддержка <b>истории</b>, пока без периодического <b>сохранения</b> &mdash; вручную, т.к.
		ещё не решил, как часто стоит это делать.</p>
	<p>Для обсуждения функционала онлайн блокнота обращайтесь ко мне в любой мессенджер: <a
			href="https://mikhail-yudin.ru/contact">https://mikhail-yudin.ru/contact</a>.
	</p>
</div>
<ul id="menu" class="m mv">
<li class="m__item no-close" data-action="toggle" title="Text format">
<i class="iconify" data-icon="mdi-format-size"></i>
<ul class="sm">
<li class="m__item" data-action="format" data-command="removeformat" title="Clear format">
<i class="iconify" data-icon="mdi-format-clear"></i>
</li>
<li class="m__item" data-action="format" data-command="bold" title="Bold">
<i class="iconify" data-icon="mdi-format-bold"></i>
</li>
<li class="m__item" data-action="format" data-command="justifyFull" title="Align justify">
<i class="iconify" data-icon="mdi-format-align-justify"></i>
</li>
<li class="m__item" data-action="format" data-command="justifyLeft" title="Align left">
<i class="iconify" data-icon="mdi-format-align-left"></i>
</li>
<li class="m__item" data-action="format" data-command="justifyCenter" title="Align center">
<i class="iconify" data-icon="mdi-format-align-center"></i>
</li>
<li class="m__item" data-action="format" data-command="justifyRight" title="Align right">
<i class="iconify" data-icon="mdi-format-align-right"></i>
</li>
<li class="m__item" data-action="format" data-command="insertOrderedList" title="Numbered list">
<i class="iconify" data-icon="mdi-format-list-numbered"></i>
</li>
<li class="m__item" data-action="format" data-command="insertUnorderedList" title="Unordered list">
<i class="iconify" data-icon="mdi-format-list-bulleted"></i>
</li>
</ul>
</li>
<li class="m__item m__history" data-action="toggle" title="History">
<i class="iconify" data-icon="mdi-history"></i>
<div class="sm mv">
<ul class="history-wrapper mh">
<li class="m__item" data-action="historyAdd" title="Add to history">
<i class="iconify" data-icon="mdi-database-plus"></i>
</li>
<li class="m__item" data-action="historyClear" title="Clear history">
<i class="iconify" data-icon="mdi-database-remove"></i>
</li>
</ul>
<ul id="history"></ul>
</div>
</li>
<li class="m__item" data-action="toggleFullScreen" title="Toggle full screen">
<i class="iconify" data-icon="mdi-arrow-expand-all"></i>
</li>
<li class="m__item" data-action="themeSwitch" title="Switch theme">
<i class="iconify" data-icon="mdi-theme-light-dark"></i>
</li>
</ul>
<script src="https://cdn.jsdelivr.net/npm/vanilla-caret-js@1.0.1/dist/VanillaCaret.min.js"></script>
<script src="/notepad.js"></script>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Сменить раскладку онлайн с английского на русский и наоборот</title><link>https://mikhail-yudin.ru/projects/text-layout-change/</link><pubDate>Mon, 12 Aug 2019 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/projects/text-layout-change/</guid><description>Набрали текст в другой раскладке, забыли сменить язык вовремя, а набирать текст на другом языке заново долго?
Или начали печатать в другой раскладке, а затем сменили на другую не глядя?
&amp;nbsp; Поменять раскладку! Воспользуйтесь этим сервисом для перевода раскладки online.
Просто вставьте текст в неправильной раскладке и нажмите кнопку "сменить раскладку". Текст полностью заменится на такой, будто был набран в нужной раскладке.</description><enclosure url="https://mikhail-yudin.ru/projects/text-layout-change/text-layout-change.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Сменить раскладку онлайн с английского на русский и наоборот</h1></header>
            <div class='entry-content'>
              <p>Набрали текст в другой раскладке, забыли сменить язык вовремя, а набирать текст на другом языке заново долго?</p>
<p>Или начали печатать в другой раскладке, а затем сменили на другую не глядя?</p>
<div class="form">
  <textarea id="badLayoutText" rows="10" cols="80" placeholder="Текст в неправильной раскладке сюда!"></textarea>
  <footer class="form__footer">
    &nbsp;
    <button id="convertlayout">Поменять раскладку!</button>
  </footer>
</div>
<p>Воспользуйтесь этим сервисом для перевода раскладки online.</p>
<p>Просто вставьте текст в неправильной раскладке и нажмите кнопку "сменить раскладку".<br/>
  Текст полностью заменится на такой, будто был набран в нужной раскладке.</p>
<script src="/layout-change.js"></script>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Игра про уничтожение блоков</title><link>https://mikhail-yudin.ru/projects/block-destruction-game/</link><pubDate>Sat, 01 Nov 2008 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/projects/block-destruction-game/</guid><description>Задача игрока — убрать все блоки, или оставить как можно меньше. За уничтоженный блок начисляется очко. Всегда максимальное количество возможных очков равно площади поля. Поэтому даже если убраны не все блоки, можно соревноваться набирая большее количество очков.
Предыстория
Репозиторий с иходником
canvas{outline:1px solid #ccc; background-color: #eee}.game-wrapper{text-align:center} //user utils function Point(x,y){this.x=x; this.y=y;} function p2ij(p){ with(Math) return {i:floor((cv.height-p.y)/bs),j:floor(p.x/bs)}; } function p2c(p){ var ij=p2ij(p); if(ij.i=height||ij.j=width||!arr[ij.i][ij.j])return -1; return arr[ij.i][ij.j].color; } function c2p(row,col){ return new Point(col*bs,cv.</description><enclosure url="https://mikhail-yudin.ru/projects/block-destruction-game/block-destruction-game.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Игра про уничтожение блоков</h1></header>
            <div class='entry-content'>
              <p>Задача игрока — убрать все блоки, или оставить как можно меньше. За уничтоженный блок начисляется очко. Всегда максимальное количество возможных очков равно площади поля. Поэтому даже если убраны не все блоки, можно соревноваться набирая большее количество очков.</p>
<p><a href="https://mikhail-yudin.ru/frontend/html5-canvas-block-destruction-game" target=_blank rel="noopener noreferrer">Предыстория</a></p>
<p><a href="https://github.com/fagci/block-destruction-game" target=_blank rel="noopener noreferrer">Репозиторий с иходником</a></p>
<style>canvas{outline:1px solid #ccc; background-color: #eee}.game-wrapper{text-align:center}</style>
<div class="game-wrapper">
<canvas id="cv" width="300" height="400"></canvas>
</div>
<script>
//user utils
function Point(x,y){this.x=x; this.y=y;}
function p2ij(p){
with(Math) return {i:floor((cv.height-p.y)/bs),j:floor(p.x/bs)};
}
function p2c(p){
var ij=p2ij(p);
if(ij.i<0||ij.j<0||ij.i>=height||ij.j>=width||!arr[ij.i][ij.j])return -1;
return arr[ij.i][ij.j].color;
}
function c2p(row,col){
return new Point(col*bs,cv.height-row*bs-bs);
}
//addition for canvas
if(!window.requestAnimationFrame){
window.requestAnimationFrame=(function(){
return window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(cb,el){
window.setTimeout(cb,1000/60);
};
} )();
}
var CanvasBuffer=function(w,h){
this.cv=document.createElement('canvas');
this.cv.width=w;
this.cv.height=h;
this.ct=this.cv.getContext('2d');
this.clear=function(other){
if(!other)this.ct.clearRect(0,0,cv.width,cv.height);
else this.cv.width=this.cv.width;
};
this.render=function(ct,x,y){
ct.drawImage(this.cv,x,y);
};
};
var cv=document.getElementById('cv'),
ct=cv.getContext('2d'),
cb=new CanvasBuffer(cv.width,cv.height);
ct.textBaseline='top';
ct.font='11px Arial';
cb.ct.textBaseline='top';
cb.ct.font='11px Arial';
//vars
var bs=15,
colors=4,
colorsArr=[
'lightblue', 
'rgb(120,150,170)', 
'rgb(120,170,150)',
'pink',
'gray',
],
height=Math.round((cv.height-50)/bs),width=Math.round(cv.width/bs),moves='',
totalBlocks=0,
mouse=new Point(0,0),
arr=[],
todel=[],
changes={},
score=0;
var filled=height;
function fixChange(i,j){
if(!changes[i]) changes[i]=[];
if(changes[i].indexOf(j)==-1)changes[i].push(j);
}
function clearChanges(){changes={};}
function changeAll(){
arr.iterate(fixChange);
}
function changesGetLRM(){
var left=width,right=0,bottom=height,cur;
with(Math){
for(var k in changes){
bottom=min(bottom,k);
for(var t in changes[k]){
cur=changes[k][t];
left=min(left,cur);
right=max(right,cur);
}
}
}
return {l:left,r:right,m:bottom};
}
arr.iterate=function(f,skipEmpty){
for(var row=0; row<arr.length; row++){
for(var col=0; col<arr[row].length; col++){
if(skipEmpty && !arr[row][col])continue;
if(f(row,col,arr[row][col])) return;
}
}
};
arr.appendLine=function(bottom){
if(arr[height-1])for(var i=0;i<width;i++)if(arr[height-1][i])return 0;
if(bottom)arr.pop();
for(var j=0, line=[]; j<width; j++){
fixChange(height-1,j);
with(Math)line.push({color:round(random()*(colors-1))});
}
if(bottom){
arr.unshift(line);
}
else arr.push(line);
totalBlocks+=width;
return 1;
}
for(var i=0; i<height; i++){
if(i<filled)arr.appendLine();
else{
for(var j=0, line=[]; j<width; j++) line.push(undefined);
arr.push(line);
}
}
function del(color,i,j){
if(i<0||j<0||i>=height||j>=width||!arr[i][j])return;
if(arr[i][j].todel || arr[i][j].color!==color)return;
arr[i][j].todel=true;
todel.push({i:i,j:j});
del(color,i-1,j);
del(color,i,j-1);
del(color,i+1,j);
del(color,i,j+1);
}
function anyMoves(){
moves=false;
arr.iterate(function(i,j,current){
todel=[];
del(current.color,i,j);
resetStates();
if(todel.length>2){ moves=true; return true; }
},true);
}
function deleteItems(mouse){
var c,ij,count;
c=p2c(mouse);
if(c===-1)return;
ij=p2ij(mouse);
todel=[];
del(c,ij.i,ij.j);
if(todel.length<3){ resetStates(); return; }
count=todel.length;
for(var i=0;i<todel.length;i++){ delete arr[todel[i].i][todel[i].j]; fixChange(todel[i].i,todel[i].j); }
return count;
}
function fall(){
//move down
var lr=changesGetLRM();
console.time('down');
var pos;
arr.iterate(function(i,j,el){
if(j<lr.l||j>lr.r||i<lr.m)return;
if(el)return;
pos=i;
for(var t=i+1;t<height;t++){
if(arr[pos][j] || !arr[t][j])continue;
arr[pos][j]=arr[t][j];
fixChange(pos,j);
fixChange(t,j);
delete arr[t][j];
break;
}
});
console.timeEnd('down');
//if gaps
console.time('gaps');
//if gaps
for(var col=lr.l;col<width;col++){
if(arr[0][col])continue; //no gap
for(var ccol=col+1;ccol<width;ccol++){
if(!arr[0][ccol])continue;
//not empty, move
for(var i=0;i<height;i++){
if(!arr[i][ccol])break; //if on top
arr[i][col]=arr[i][ccol];
fixChange(i,col);
fixChange(i,ccol);
delete arr[i][ccol];
}
break;
}
}
console.timeEnd('gaps');
}
function resetStates(){
for(var i=0;i<todel.length;i++)arr[todel[i].i][todel[i].j].todel=false;
}
function update(){
//cb.clear();
var group=[];
for(var i=0;i<colors;i++)group.push([]);
for(var k in changes){ 
for(var t in changes[k]){
var row=k,col=changes[k][t];
var el=arr[row][col];
var p=c2p(row,col);
if(!el){cb.ct.clearRect(p.x,p.y,bs,bs); continue;}
group[el.color].push(p);
}
}
for(var i=0;i<colors;i++){
cb.ct.fillStyle=colorsArr[i];
cb.ct.beginPath();
for(var j=0;j<group[i].length;j++){
cb.ct.rect(group[i][j].x,group[i][j].y,bs,bs);
}
cb.ct.fill();
}
cb.ct.fillStyle='#000';
cb.ct.clearRect(0,0,150,20);
cb.ct.fillText('Blocks: '+totalBlocks,0,0);
cb.ct.fillText('Moves: '+moves,80,0);
clearChanges();
}
cv.onmouseup=function(e){
console.clear();
mouse=new Point(e.pageX-this.offsetLeft, e.pageY-this.offsetTop);
//console.time('delete');
var cnt=deleteItems(mouse);
if(cnt){
totalBlocks-=cnt;
score+=cnt;
}
//console.timeEnd('delete');
//console.time('fall');
fall();
//console.timeEnd('fall');
//console.time('anymoves');
anyMoves();
//console.timeEnd('anymoves');
//console.time('update');
update();
//console.timeEnd('update');
};
/*
var level=1;
var tmr=setTimeout(pushIt,2000);
function pushIt(){
if(!arr.appendLine(true)){ //gameover
clearInterval(tmr);
alert('GAME OVER. Your score: '+score);
return;
}
tmr=setTimeout(pushIt,3000-level++*10);
changeAll();
anyMoves();
update();
}
*/
var ttd=false;
var draw=function(){
requestAnimationFrame(draw,cv);
ct.clearRect(0,0,cv.width,cv.height);
cb.render(ct,0,0);
};
changeAll();
update();
draw();
</script>

            </div>
      	]]></turbo:content></item></channel></rss>