canvas paint рисовалка

HTML5 — несомненный тренд. Причем, не столько новая спецификация с новыми тегами, сколько гармоничное и могучее сочетание HTML + CSS + JS, изящно утирающее нос Flash.

Уже сейчас можно наблюдать красивые HTML-5 сайты без мегабайтов изображений и без сотен килобайт скриптов. Например, фон вот этого сайта сделан при помощи JavaScript Canvas.

Как видите, JS Canvas действительно хорошая вещь. Именно ей я хочу посвятить первый пост свежеоткрытой рубрики «HTML5″. В нем я опишу процесс создания простенькой рисовалки на HTML5 с единственным инструментом «Pencil». Вот такую.

Общие сведения

Canvas позволяет рисовать в специальном элементе документа <canvas> линии, дуги, прямоугольники, окружности, заливать пространство, манипулировать текстом и картинками. Всё это прекрасно описано в «Canvas Tutorial« Mozilla Developers Center с примерами и картинками, прям как мне нравится.


Мы же напишем рисовалку, управлять которой будем при помощи специального объекта Canva, а сам инструмент «карандаш» (ну или «кисть») будет задан объектом Pencil.

Canva

Canva — управляющий объект. С его помощью мы переопределим реакцию страницы на события <canvas>, будем выбирать нужный инструмент, будем задавать свойства типа цвета заливки и толщины линий и пр. Вот его реализация:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
var Canva = {};
 
// Инициализация объекта
Canva.init = function(id, width, height)
{
    var canv = document.getElementById(id);
    canv.width = width;
    canv.height = height;
 
    this.canvasId = id;
 
    this.ctx = canv.getContext("2d");
    // Свойства
    this.selectedColor = '#000000';
    this.selectedFillColor = '#FFFFFF';
    this.selectedWidth = 1;
    this.tool = Pencil; // Выбранный инструмент
    this.drawing = false; // true - если зажата кнопка мыши
 
    // Кнопка мыши зажата, рисуем
    canv.onmousedown = function(e)
    {
        var evnt = ie_event(e);
        Canva.tool.start(evnt);
    };
 
    // Кнопка мыши отпущена, рисование прекращаем
    canv.onmouseup = function(e)
    {
        if (Canva.drawing)
        {
            var evnt = ie_event(e);
            Canva.tool.finish(evnt);
        }
    };
 
    // процесс рисования
    canv.onmousemove = function(e)
    {
        if (Canva.drawing)
        {
            var evnt = ie_event(e);
            Canva.tool.move(evnt);
        }
    };
};
 
Canva.setTool = function(t) // Задать инструмент
{
    Canva.tool = t;
};
 
Canva.setWidth = function(width) // Задать толщину линий
{
    Canvas.selectedWidth = width;
};
 
Canva.setColor = function(color) // Задать текущий цвет
{
    Canva.selectedColor = color;
};
 
Canva.clear = function() // Очистить рисовалку
{
    var canv = document.getElementById(Canva.canvasId);
    Canva.ctx.clearRect(0, 0, canvas.width, canvas.height);
};

При инициализации объекта ему передается в качестве параметров id canvas, необходимые ширина и высота рисовалки.

функция ie_event — специальный костыль для IE (см. эту запись), вот ее реализация:

1
2
3
4
5
6
function ie_event(e)
{
    if (e === undefined)
        { return window.event; };
    return e;
}

Pencil

Наш инструмент, которым мы будем рисовать, должен иметь функции move, start и finish. Принцип полиморфизма в действии.

Все нарисованное этим инструментом будет представлять собой последовательность прямых линий, коротких и не очень. Тут уже зависит от того, насколько быстр браузер пользователя.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var Pencil = {};
 
// Начинаем рисование
Pencil.start = function(evnt)
{
	// Текущее положение мыши - начальные координаты
    Pencil.x = evnt.clientX;
    Pencil.y = evnt.clientY;
 
    Canva.ctx.beginPath();
    // Свойства рисования
    Canva.ctx.strokeStyle = Canva.selectedColor;
    Canva.ctx.lineWidth = Canva.selectedWidth;
    Canva.ctx.moveTo(Pencil.x, Pencil.y); // Курсор на начальную позицию
 
    Canva.drawing = true; // Начато рисование
};
 
// Рисование закончили
Pencil.finish = function(evnt)
{   
    Pencil.x = evnt.clientX;
    Pencil.y = evnt.clientY;
    Canva.ctx.lineTo(Pencil.x, Pencil.y); // Дорисовываем последнюю линию
 
    Canva.drawing = false;
};
 
// Рисование в разгаре
Pencil.move = function(evnt)
{   
    Pencil.x = evnt.clientX;
    Pencil.y = evnt.clientY;
    Canva.ctx.lineTo(Pencil.x, Pencil.y); // Дорисовываем начатую линию
    Canva.ctx.stroke();
    // Начинаем рисованть новую линию из той же точки.
    Canva.ctx.moveTo(Pencil.x, Pencil.y); 
};

Принцип такой: на нажатие кнопки мыши начинаем линию. На событие onmousemove (движение мыши) начатую линию дорисовываем и тут же начинаем новую. Так повторяется до тех пор, пока кнопка мыши не будет отпущена.

Использование

В header документа подключаем файл, содержащий весь приведенный выше код. В теле документа (после <canvas>!) вставляем JS-код с инициализацией объекта Canva:

1
2
3
4
<script type="text/javascript">
    // Сделаем рисовалку во всю страницу
    Canva.init('canvas', window.innerWidth, window.innerHeight);
</script>

Можно посмотреть рисовалку в действии: демо.

Internet Explorer

Поддержки canvas в IE 8 и младше нет. Но у доброго Google есть проект explorercanvas, который должен подружить IE с canvas. К сожалению, в моем случае этот костыль почему-то не работает, так что пользователи IE ничего порисовать не смогут. Сами виноваты.

Сохранение изображения на стороне клиента

Чтобы сохранить нарисованное в canvas как изображение на стороне клиента, можно сделать специальную кнопку и на onclick ей повесить событие

window.open(document.getElementById("canvas").toDataURL("image/png"), "new_window_name");

При нажатии на кнопку будет открыто новое окно (вкладка) с PNG-изображением.

Сохранение изображения на сервере

Так как предыдущий вариант работает через GET, зачастую он не применим для сохранения изображения на сервере из-за ограничения на длину URL. Тогда на помощь приходит вот эта библиотека.


Заметили неточность? Черкните мне пару слов на feedback@modusponens.info.
Есть вопросы? Пишите в комментарии.