Интерактивность и анимация

Все здорово, но без возможности динамически менять содержимое и взаимодействовать с пользователем все эти графические возможности существенно теряют значимость. Все-таки современный веб — это живая интерактивная среда. На первый взгляд, с последним явно существуют проблемы — достучаться методами DHTML до линий и фигур, нарисованных на холсте, не представляется возможным. Да и изменить что-либо, уже нанесенное на холст, нельзя. Но давайте вспомним, что сам canvas и, следовательно, контекст рисования являются DOM-объектами, а значит, они для внешних сценариев будут вполне доступны. Давайте сейчас попробуем использовать это.

Свой Paint

Начнем с того, что напишем с помощью canvas API собственный графический редактор. Да-да, вот так, никак не меньше. Ну ладно, назовем нашу программу просто «рисовалкой», конкурировать с Adobe Photoshop мы не собираемся. По крайней мере, пока. Но написать что-то, близкое по функциональности к Microsoft Paint, мы уже можем. Не верите? Давайте начнем. Прежде всего готовим холст — рабочую среду нашей «рисовалки»:

<html>

<head>

<title>Canvas</title>

<meta http-equiv=”content-type” content=”text/html; charset=ISO-8859-1” /> <script src=”../jquery-1.4.4.min. js”></script>

<script>

$(function(){

Var canvas = document. getElementById(”myCanvas”); var context = canvas. getContext(”2d”); context. strokeRect(0, 0,500,300):

});

</script>

</head>

<body>

<canvas id=”myCanvas” width=”500” height=”300”>

Your browser does not support HTML5 Canvas.

</canvas>

</body>

</html>

Теперь добавим возможность, двигая мышью с нажатой левой кнопкой, оставлять в пределах холста (как и положено в программе-«рисовалке»). Для этого привяжем команду lineTo к соответствующим событиям:

Context. strokeRect(0, 0,500,300); canvas. addEvent Listener("mousedown", function(e){ context. beginPath(); context. moveTo(e. offsetX, e. offsetY); canvas. addEventListener("mousemove", function(event){ context. lineTo(event. offsetX, event. offsetY); context. stroke();

}

});

});

Все, теперь можно рисовать (рис. 49). Правда, осталась проблема — нужно прекратить рисование при отпускании клавиши. Поэтому введем флаг для необходимости рисования и еще один обработчик события:

Context. strokeRect(0, 0,500,300); var flag = 0;

Canvas. addEvent Listener("mousedown", function(e){

Canvas. add EventListener("mousemove", function(event){ if(flag == 1){

Context. lineTo(event. offsetX, event. offsetY);

});

Canvas. add Event Listener("mouseup", function(e){ flag = 0;

});

});

Все! Движок нашей «рисовалки» готов! Теперь можно позаботиться о выборе цвета, внедрении инструментов и т. д. Сейчас этим заниматься не будем — главное мы уже сделали.

Как нам организовать анимацию?

Я думаю, все уже видели эффектное представление технологии Canvas — анимированные сюжеты, интерактивные игры, позволяющие говорить о скорой и мучительной смерти технологии Adobe Flash.

После того как мы прошли основы рисования, у многих появится вопрос: как? Как организовать анимацию? Как заставить эти рисунки двигаться? Ведь на самом деле, один раз трансформировав холст и нарисовав фигуры, у нас просто нет способа их изменить впоследствии. А из внешнего сценария все эти дуги и квадраты будут тем более недоступны.

Решение на самом деле довольно просто. Достаточно вспомнить, как рождалась реальная анимация. Движение заключается в непрерывном перерисовывании картинки. Звучит, конечно, не очень вдохновляюще, но на практике это не совсем страшно. Давайте попробуем заставить нашу зеленую рожицу вращаться. Для этого весь код, ее вырисовывающий, заключим в функцию:

Function draw() {

Var canvas = document. getElementById("canvas"); var context = canvas. getContext("2d"); context. fillStyle = "green"; context. lineWidth = 10; context. strokeStyle = "green"; context. lineCap = ’round’; context. beginPath():

Context. moveTo(240,130); context. arc(140,130,100,0,7,0); context. stroke(); context. beginPath(): context. moveTo(112,100); context. arc(100,100,12,0,7,0); context. moveTo(192,100); context. arc(180,100,12,0,7,0): context. fill(); context. beginPath(): context. moveTo(190,150); context. arc(140,150,50, 0, 3, 0); context. moveTo(140,130); context. lineTo(140,150); context. stroke();

}

Теперь напишем функцию init(), основная задача которой — вызывать draw() через определенный промежуток времени:

Function init(){

SetInterval(draw,100);

}

Init();

Последней строчкой мы запускаем всю конструкцию при загрузке страницы. Ничего не происходит? Ну, было бы странно, если бы случилось иное, ведь никаких преобразований картинки мы не задали. Исправим эту ситуацию. Для начала введем глобальную переменную и установим ей значение:

Var r;

Function init(){ r = 0.1;

SetInterval(draw,100);

}

Init();

Тут г — значение угла поворота в радианах. Теперь введем нужную трансформацию в draw():

Context. lineCap = ’round’; context. translate(145,145); угопл поворота

Context. rotate(xr): context. translate(-145,-145): xr = xr +0.1 ; context. beginPath():

Идея состоит в том, чтобы при каждом запуске draw() угол поворота увеличивался на 0,1 радиана.

Все работает, но не совсем тем образом, которым мы хотели (рис. 50).

В чем дело? Да все просто. При очередном запуске функции draw() результат работы предыдущего никуда не исчезает. Естественно, через некоторое время на холсте будут только сплошные зеленые круги.

Нужно очищать холст между за — Рис. 50. Анимация — пусками! Штатная функция для этой  гюрвая попытка

Операции clearRect не отличается гибкостью реализации, но нам вполне подходит:

Function draw() {

Var canvas = document. getElementById(”canvas”); var context = canvas. getContext(”2d”); context. clearRect(0, 0, 800, 600): context. fillStyle = ”green”; context. lineWidth = 10;

Начнем жизнь с чистого листа!

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

Можно, конечно, попытаться учитывать накопленные изменения при расчете каждой последующей трансформации, но такой стиль очень плохо масштабируется и на сложных проектах неизбежно закончится тупиком. Лучше воспользоваться еще одной возможностью canvas API — сохранением состояния:

Context. lineCap = ’round context. save():

Context. translate(145,145); context. rotate(xr): context. translate(-145,-145);

Так мы сохраняем контекст, а в конце выполнения функции сбросим все проведенные трансформации:

Context. stroke(); context. restore():

}

Теперь все в порядке (правда, на бумаге это отобразить затруднительно, на всякий случай — рис. 51).

Рис. 51. Все в порядке — рожица вертится

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *