Продолжаем неделю canvas на хабре.
Графические редакторы на флеше/сильверлайте в интернете не редкость. На canvas их намного меньше, но есть и довольно интересные. Вот пара примеров:В этом цикле статей я планирую рассказать о различных моментах, с которыми можно столкнуться при создании графического редактора на canvas. Тривиальные части затрагивать не буду, постараюсь описывать только самое интересное. В этой статье опишу примерный алгоритм создания кисти для скетчей
Итак, с чего начнем? Начнем с создания простого файлика с canvas внутри. Я постараюсь писать как можно компактнее без лишних фреймворков и прочего, чтобы сам алгоритм был как можно прозрачнее. Вот чистый файл в котором мы будем создавать кисть для скетчей:
Канва намеренно расположена в левом верхнем углу для того, чтобы координаты курсора на экране совпадали с координатами пикселей на канве (погрешностью в 1 пиксель бордера пренебрегаем для чистоты кода, но знаем и помним о ней).
Для создания механизма рисования нам нужны несколько обработчиков стандартных событий мышки (onmousedown, onmouseup,onmousemove). Создадим для каждого функцию.
Еще нам нужны несколько переменных: в action мы будем хранить текущее состояние левой кнопки мыши (нажата или нет), в ctx — 2d контекст для рисования на канве, в points — массив для хранения точек, в pointer — указатель на один из элементов этого массива.
Итак, в чем же заключается алгоритм? Понаблюдав как рисует скетчи мой приятель, я примерно представил себе механизм работы будущей кисти. Основные моменты которые нужно учитывать для создания такой кисти: скетчи рисуются штрихами, кисть должна быть слабоинтенсивной (мой приятель слабо давил на карандаш, но прорисовывал одно место несколько раз, поэтому линия получается мохнатой с различными хвостиками туда-сюда), на изгибах линия становится шире.
Моя версия алгоритма такова:
Первый и второй пункт для наглядности изображены на рисунке:
Итак, в массиве points храним координаты последних 10 точек (я выбрал число 10, вы можете поэкспериментировать с этим). Интенсивность убавляем с помощью прозрачности (выставляем 0.1 — это означает, что наша кисть будет на 90% прозрачна). Вот код объявления переменных и получения доступа к канве:
Вызов функции initcnvs() вешаем на onload у body. Остальные функции на соответствующие события:
Наконец сам алгоритм. При нажатии кнопки меняем action на down, кидаем первую точку в наш массив и выставляем указатель на нее.
При отпускании кнопки — очищаем массив и меняем action на up.
И наконец при движении (только если зажата мышка) мы определяем новую точку в массиве (если дошли до конца, то начинаем с начала). Далее рисуем обычный отрезок соединяющий предыдущую точку с новыми координатами курсора и, если массив уже достаточно наполнен, соединяем текущее положение курсора с самой старой точкой из массива, при этом добавляем рандомом погрешность в 5 пикселей. В конце сохраняем новые координаты в массив.
Вот как выглядит эта кисточка в движении:
Вы можете сами поэкспериментировать с параметрами, случайностью, интенсивностью и посмотреть на результаты. Вот исходный код всего примера, достаточно компактно вышло:
Вот и все, надеюсь, статья оказалась полезной для вас. В дальнейшем планирую рассказать об умной штриховке, кнопке «отмена» для канвы, трюке с размытой кисточкой и многом другом. Кроме того, если вас что-то интересует по теме — отпишите в комментариях, расскажу и об этом тоже. Ошибки в тексте — в приват, заранее спасибо.
P.S. Мои безумные умения с этой кисточкой:
Графические редакторы на флеше/сильверлайте в интернете не редкость. На canvas их намного меньше, но есть и довольно интересные. Вот пара примеров:В этом цикле статей я планирую рассказать о различных моментах, с которыми можно столкнуться при создании графического редактора на canvas. Тривиальные части затрагивать не буду, постараюсь описывать только самое интересное. В этой статье опишу примерный алгоритм создания кисти для скетчей
Итак, с чего начнем? Начнем с создания простого файлика с canvas внутри. Я постараюсь писать как можно компактнее без лишних фреймворков и прочего, чтобы сам алгоритм был как можно прозрачнее. Вот чистый файл в котором мы будем создавать кисть для скетчей:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
body {
margin: 0;
}
#cnvs {
border: #000000 1px solid;
}
</style>
</head>
<body>
<canvas id="cnvs" width="800" height="500"></canvas>
</body>
</html>
* This source code was highlighted with Source Code Highlighter.
Канва намеренно расположена в левом верхнем углу для того, чтобы координаты курсора на экране совпадали с координатами пикселей на канве (погрешностью в 1 пиксель бордера пренебрегаем для чистоты кода, но знаем и помним о ней).
Для создания механизма рисования нам нужны несколько обработчиков стандартных событий мышки (onmousedown, onmouseup,onmousemove). Создадим для каждого функцию.
function mDown(e){
};
function mUp(e){
};
function mMove(e){
};
* This source code was highlighted with Source Code Highlighter.
Еще нам нужны несколько переменных: в action мы будем хранить текущее состояние левой кнопки мыши (нажата или нет), в ctx — 2d контекст для рисования на канве, в points — массив для хранения точек, в pointer — указатель на один из элементов этого массива.
Итак, в чем же заключается алгоритм? Понаблюдав как рисует скетчи мой приятель, я примерно представил себе механизм работы будущей кисти. Основные моменты которые нужно учитывать для создания такой кисти: скетчи рисуются штрихами, кисть должна быть слабоинтенсивной (мой приятель слабо давил на карандаш, но прорисовывал одно место несколько раз, поэтому линия получается мохнатой с различными хвостиками туда-сюда), на изгибах линия становится шире.
Моя версия алгоритма такова:
- вслед за мышкой рисуем обычную линию, как если бы это был карандаш
- кроме того соединяем текущую точку с какой-нибудь давно нарисованной(это придаст толщины на изгибах)
- все это должно быть с небольшим элементом случайности (мохнатость)
- кисть будет частично прозрачной (слабая интенсивность)
Первый и второй пункт для наглядности изображены на рисунке:
Итак, в массиве points храним координаты последних 10 точек (я выбрал число 10, вы можете поэкспериментировать с этим). Интенсивность убавляем с помощью прозрачности (выставляем 0.1 — это означает, что наша кисть будет на 90% прозрачна). Вот код объявления переменных и получения доступа к канве:
var action = "up";
var ctx,points,pointer;
function initcnvs(){
ctx = document.getElementById('cnvs').getContext('2d');
ctx.globalAlpha = 0.1;
points = new Array(10);
};
* This source code was highlighted with Source Code Highlighter.
Вызов функции initcnvs() вешаем на onload у body. Остальные функции на соответствующие события:
<body onload="initcnvs()" onmousedown="mDown(event)" onmousemove="mMove(event)" onmouseup="mUp(event)">
* This source code was highlighted with Source Code Highlighter.
Наконец сам алгоритм. При нажатии кнопки меняем action на down, кидаем первую точку в наш массив и выставляем указатель на нее.
function mDown(e){
action = "down";
points[0] = [e.pageX, e.pageY];
pointer = 0;
};
* This source code was highlighted with Source Code Highlighter.
При отпускании кнопки — очищаем массив и меняем action на up.
function mUp(e){
points = new Array(10);
action = "up";
};
* This source code was highlighted with Source Code Highlighter.
И наконец при движении (только если зажата мышка) мы определяем новую точку в массиве (если дошли до конца, то начинаем с начала). Далее рисуем обычный отрезок соединяющий предыдущую точку с новыми координатами курсора и, если массив уже достаточно наполнен, соединяем текущее положение курсора с самой старой точкой из массива, при этом добавляем рандомом погрешность в 5 пикселей. В конце сохраняем новые координаты в массив.
function mMove(e){
if (action == "down") {
var nextpoint = pointer + 1;
if (nextpoint > 9) nextpoint = 0;
ctx.beginPath();
ctx.moveTo(points[pointer][0],points[pointer][1]);
ctx.lineTo(e.pageX, e.pageY);
if (points[nextpoint]) {
ctx.moveTo(points[nextpoint][0] + Math.round(Math.random()*10-5),points[nextpoint][1] + Math.round(Math.random()*10-5));
ctx.lineTo(e.pageX, e.pageY);
}
ctx.stroke();
pointer = nextpoint;
points[pointer] = [e.pageX, e.pageY];
}
};
* This source code was highlighted with Source Code Highlighter.
Вот как выглядит эта кисточка в движении:
Вы можете сами поэкспериментировать с параметрами, случайностью, интенсивностью и посмотреть на результаты. Вот исходный код всего примера, достаточно компактно вышло:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
body {
margin: 0;
}
#cnvs {
border: #000000 1px solid;
}
</style>
<script type="text/javascript">
var action = "up";
var ctx,points,pointer;
function initcnvs(){
ctx = document.getElementById('cnvs').getContext('2d');
ctx.globalAlpha = 0.1;
points = new Array(10);
};
function mDown(e){
action = "down";
points[0] = [e.pageX, e.pageY];
pointer = 0;
};
function mUp(e){
points = new Array(10);
action = "up";
};
function mMove(e){
if (action == "down") {
var nextpoint = pointer + 1;
if (nextpoint > 9) nextpoint = 0;
ctx.beginPath();
ctx.moveTo(points[pointer][0],points[pointer][1]);
ctx.lineTo(e.pageX, e.pageY);
if (points[nextpoint]) {
ctx.moveTo(points[nextpoint][0] + Math.round(Math.random()*10-5),points[nextpoint][1] + Math.round(Math.random()*10-5));
ctx.lineTo(e.pageX, e.pageY);
}
ctx.stroke();
pointer = nextpoint;
points[pointer] = [e.pageX, e.pageY];
}
};
</script>
</head>
<body onload="initcnvs()" onmousedown="mDown(event)" onmousemove="mMove(event)" onmouseup="mUp(event)">
<canvas id="cnvs" width="800" height="500"></canvas>
</body>
</html>
* This source code was highlighted with Source Code Highlighter.
Вот и все, надеюсь, статья оказалась полезной для вас. В дальнейшем планирую рассказать об умной штриховке, кнопке «отмена» для канвы, трюке с размытой кисточкой и многом другом. Кроме того, если вас что-то интересует по теме — отпишите в комментариях, расскажу и об этом тоже. Ошибки в тексте — в приват, заранее спасибо.
P.S. Мои безумные умения с этой кисточкой: