<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="http://wiki.linuxformat.ru/wiki/skins/common/feed.css?303"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://wiki.linuxformat.ru/wiki/index.php?action=history&amp;feed=atom&amp;title=LXF133%3Acanvas</id>
		<title>LXF133:canvas - История изменений</title>
		<link rel="self" type="application/atom+xml" href="http://wiki.linuxformat.ru/wiki/index.php?action=history&amp;feed=atom&amp;title=LXF133%3Acanvas"/>
		<link rel="alternate" type="text/html" href="http://wiki.linuxformat.ru/wiki/index.php?title=LXF133:canvas&amp;action=history"/>
		<updated>2026-05-13T08:42:47Z</updated>
		<subtitle>История изменений этой страницы в вики</subtitle>
		<generator>MediaWiki 1.19.20+dfsg-0+deb7u3</generator>

	<entry>
		<id>http://wiki.linuxformat.ru/wiki/index.php?title=LXF133:canvas&amp;diff=12403&amp;oldid=prev</id>
		<title>Crazy Rebel: викификация, оформление, иллюстрация</title>
		<link rel="alternate" type="text/html" href="http://wiki.linuxformat.ru/wiki/index.php?title=LXF133:canvas&amp;diff=12403&amp;oldid=prev"/>
				<updated>2011-07-14T08:35:21Z</updated>
		
		<summary type="html">&lt;p&gt;викификация, оформление, иллюстрация&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;: '''Canvas''' Продвинутые возможности HTML для ваших web-приложений&lt;br /&gt;
&lt;br /&gt;
==Canvas: Добавим Drag’n’drop==&lt;br /&gt;
&lt;br /&gt;
: Добавив к элементу '''&amp;lt;nowiki&amp;gt;&amp;lt;canvas&amp;gt;&amp;lt;/nowiki&amp;gt;''' способность реагировать на движение мышью, '''Иван Травкин''' сделает динамическую web-графику по-настоящему динамичной.&lt;br /&gt;
&lt;br /&gt;
Для изучения многих аспектов программирования сегодня достаточно текстового редактора и web-браузера: ряд вспомогательных инструментов перечислен во врезке внизу. Так, несколько лет назад в активе web-разработчиков появился новый элемент HTML – '''Canvas''', позволяющий рисовать изображения прямо на web-странице, используя сценарии ''JavaScript''. Ранее динамическая графика либо загружалась с сервера в готовом виде, либо требовала модулей расширения ''Flash'' или ''Java''.&lt;br /&gt;
&lt;br /&gt;
В [[LXF94|LXF94 (июль 2007)]] была опубликована отличная вводная статья Дэна Фроста [[LXF94:Графическое web-приложение|«Canvas: Холст для web-картин »]]; в ней подробно описан элемент '''Canvas''' и показаны примеры рисования фигур разной степени сложности: от контуров прямоугольников до фигур с градиентной заливкой. Мы пойдем несколько дальше: сделаем так, чтобы любую фигуру на поверхности '''Canvas''' можно было перетаскивать мышью.&lt;br /&gt;
&lt;br /&gt;
Изначально '''Canvas''' предлагает разработчику лишь набор команд для рисования примитивов (линии, квадраты, окружности) и для простых преобразований плоскости, таких как поворот. Нам придется самим научить его определять нажатие кнопки мыши и, при попадании в одну из фигур, обрабатывать ее перемещение до тех пор, пока пользователь не отпустит кнопку. Мы также введем несколько упрощений, о которых поговорим по ходу дела.&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF133_78_1.jpg|300px]] Инструменты разработчика в браузере ''Chrome''.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
===Объектная модель===&lt;br /&gt;
&lt;br /&gt;
Чтобы облегчить себе решение поставленной задачи, создадим простенькую объектную модель, похожую на предложенную в [[LXF94:Графическое web-приложение|LXF94]]. Ведь в случае сложных изображений оперировать абстрактными понятиями вроде «фигура» гораздо удобнее, чем 10–15 строками кода: «измени цвет пера на красный», «проведи линию в точку (x,y)», «закрась прямоугольную область» и так далее.&lt;br /&gt;
&lt;br /&gt;
Для начала создадим объектную «обертку» (иногда называемую «декоратором») для самого элемента '''Canvas'''. Объект '''Canvas''' (не путайте его с элементом!) станет основой нашей модели. Он будет «помнить» набор изображенных фигур, отвечать за их перерисовку и обрабатывать перемещение фигур курсором мыши.&lt;br /&gt;
&amp;lt;source lang=java&amp;gt;&lt;br /&gt;
 var Canvas = new Object();&lt;br /&gt;
 Canvas.shapes = [];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Перво-наперво нужно связать наш объект с неким элементом '''Canvas''', на поверхности которого и будут изображаться фигуры.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=java&amp;gt;&lt;br /&gt;
 Canvas.setCanvas = function ( aCanvas ) {&lt;br /&gt;
 	 this.canvas = aCanvas;&lt;br /&gt;
 	 this.context = aCanvas.getContext( '2d' );&lt;br /&gt;
 	 /* …пропуск… */&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Метод '''setCanvas()''' отвечает за «запоминание» рабочего элемента '''Canvas''', получение его контекста (нужного для рисования) и, что особо для нас важно, связывание событий мыши элемента с обработчиками, которые мы далее определим в объекте '''Canvas'''.&lt;br /&gt;
&lt;br /&gt;
Для запоминания набора фигур объект '''Canvas''' использует коллекцию '''shapes'''. Для добавления и удаления фигур в коллекцию, а также последующей перерисовки, применяются методы '''addShape()''' и '''removeShape()''', соответственно.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=java&amp;gt;&lt;br /&gt;
 Canvas.addShape = function ( aShape ) {&lt;br /&gt;
 	 this.shapes.push ( aShape );&lt;br /&gt;
 	 this.draw ();&lt;br /&gt;
 	 return this;&lt;br /&gt;
 }&lt;br /&gt;
 Canvas.removeShape = function ( aShape ) {&lt;br /&gt;
 	 /* …пропуск… */&lt;br /&gt;
 	 this.draw ();&lt;br /&gt;
 	 return this;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Шарм объектно-ориентированного программирования в том, что, распределив обязанности между объектами, можно затем просто раздавать им указания, почти не напрягая драгоценный мозг.&lt;br /&gt;
&lt;br /&gt;
Фигуры не рисуются непосредственно. Следуя общим принципам ООП, изложенным Аланом Найтом [Alan Knight] в прекрасной статье [[http://ru.smalltalk.wikia.com/wiki/%D0%9F%D0%A0%D0%98%D0%9D%D0%A6%D0%98%D0%9F%D0%AB_%D0%9E%D0%9E_%D0%94%D0%98%D0%97%D0%90%D0%99%D0%9D%D0%90|«Принципы ОО дизайна, или Всему, что я знаю о программировании, я научился у Дилберта»]], объект '''Canvas''', когда надо, просит каждую свою фигуру «нарисоваться» на холсте.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=java&amp;gt;&lt;br /&gt;
 Canvas.draw = function () {&lt;br /&gt;
 	 // очищаем поверхность элемента Canvas (маленький хак)&lt;br /&gt;
 	 this.canvas.width = this.canvas.width;&lt;br /&gt;
 	 // рисуем фигуры&lt;br /&gt;
 	 this.shapesDo ( function ( shape ) {&lt;br /&gt;
 		 shape.draw(); } );&lt;br /&gt;
 	 return this;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для упрощения работы с фигурами наш объект '''Canvas''' имеет метод-итератор '''shapesDo()''', позволяющий легко выполнять действия со всей коллекцией фигур разом, что мы и делаем во время прорисовки изображения.&lt;br /&gt;
&lt;br /&gt;
===Начинаем упрощать===&lt;br /&gt;
&lt;br /&gt;
{{Врезка|Содержание=[[Изображение:LXF133_79_1.jpg|300px]] На странице этого сделать не получится, но в web-браузере прямоугольники можно перетаскивать мышью.|Ширина=300px}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь скажем пару слов о самих фигурах. Здесь мы ограничимся одним-единственным типом – прямоугольниками. Для их рисования потребуется всего один примитив элемента '''Canvas'''. Не станем даже добавлять возможность закраски каким-либо цветом: вместо этого будем рисовать полупрозрачные (чтобы видеть места перекрытия фигур при перетаскивании) прямоугольники светло-синегоцвета. Наши прямоугольники будут отличаться друг от друга только начальным положением и размером, и уметь только рисовать себя на поверхности рабочего элемента '''Canvas''' (метод '''draw()''') да отвечать на вопрос, лежит ли точка с координатами (x,y) внутри данного прямоугольника (метод '''includesPoint()''') – чтобы определять при нажатии кнопки мыши, не попали ли мы курсором в данную фигуру.&lt;br /&gt;
&lt;br /&gt;
И еще одно упрощение. Определяя координаты курсора мыши, перемещаемого над элементом '''Canvas''', мы столкнемся вот с чем: координаты курсора отсчитываются от левого верхнего угла документа и смещены относительно координатной системы нашего холста. Решение – добавлять смещение, равное координатам самого элемента '''Canvas'''. Но мы поступим проще, совместив координаты документа с координатами элемента '''Canvas''': с помощью таблицы стилей уберем внутренние и внешние поля элемента '''Body''', поместив тем самым холст в верхний левый угол документа.&lt;br /&gt;
&lt;br /&gt;
===Хватай и тащи===&lt;br /&gt;
&lt;br /&gt;
Итак, все, что требовалось для решения поставленной задачи, готово; теперь начинается самое интересное. Выше мы указали, что обработкой событий мыши, связанных с рабочим элементом '''Canvas''', занимается объект-«обертка». Нам наиболее важны следующие три события: «нажатие» '''onmousedown''', «отпускание» '''onmouseup''' и «перемещение» '''onmousemove'''. В свете этих трех событий, объекту '''Canvas''' необходимо помнить две вещи: флажок '''isDragging''', равный '''true''' в тот момент, когда фигура «подцеплена», и '''draggingShape''' – ссылку на «подцепленную» фигуру.&lt;br /&gt;
&lt;br /&gt;
Далее все просто. При нажатии кнопки мыши определим, не находится ли в этот момент курсор мыши над одной из фигур (спросим каждую фигуру: попали координаты курсора в ее внутреннюю область?), и если да, поставим флажок '''isDragging=true''' и запомним текущие координаты мыши ('''mouseX, mouseY''') и фигуру, в которую мы «ткнули» ('''draggingShape'''). При отпускании кнопки мыши достаточно просто поставить флаг '''isDragging=false'''.&lt;br /&gt;
&lt;br /&gt;
За перемещение «подцепленной» фигуры отвечает событие '''onmousemove'''. При перемещении мыши достаточно проверить, стоит ли флажок '''isDraging=true'''. Если да – вычислим приращение координат курсора (используя последние запомненные координаты), придадим это приращение фигуре '''draggingShape''', снова запомним координаты и перерисуем изображение.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=java&amp;gt;&lt;br /&gt;
 Canvas.onmousemove = function ( event ) {&lt;br /&gt;
 	 if ( this.isDragging ) {&lt;br /&gt;
 		 this.dragginShape.x = this.dragginShape.x + (event.clientX - this.mouseX);&lt;br /&gt;
 		 this.dragginShape.y = this.dragginShape.y + (event.clientY - this.mouseY);&lt;br /&gt;
 		 this.mouseX = event.clientX;&lt;br /&gt;
 		 this.mouseY = event.clientY;&lt;br /&gt;
 		 this.draw ();&lt;br /&gt;
 	 }&lt;br /&gt;
 	 return this;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вот и все! Осталось лишь посмотреть, как это работает. Код HTML-документа будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=html4strict&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
 	 &amp;lt;title&amp;gt;Canvas и Drag'n'drop&amp;lt;/title&amp;gt;&lt;br /&gt;
 	 &amp;lt;script type=”text/javascript” src=”canvas.js”&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
 	 &amp;lt;script type=”text/javascript” src=”rect.js”&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
 &amp;lt;/head&amp;gt;&lt;br /&gt;
 &amp;lt;body style=”padding: 0; margin: 0;”&amp;gt;&lt;br /&gt;
 	 &amp;lt;canvas id=”canvas-test” width=”300” height=”300”&lt;br /&gt;
 		 style=”background: #afa;”&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
 	 &amp;lt;script type=”text/javascript”&amp;gt;&lt;br /&gt;
 		 Canvas&lt;br /&gt;
 				 .setCanvas ( document.getElementById('canvas-test') )&lt;br /&gt;
 				 .addShape( new Rect (10, 10, 30, 30) )&lt;br /&gt;
 				 .addShape( new Rect (50, 10, 50, 50) );&lt;br /&gt;
 	 &amp;lt;/script&amp;gt;&lt;br /&gt;
 &amp;lt;/body&amp;gt;&lt;br /&gt;
 &amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Что дальше?===&lt;br /&gt;
&lt;br /&gt;
Мы решили небольшую, но занятную задачу. Что дальше? Можно добавить цвет заливки прямоугольников. Или создать новые объекты для прочих фигур – скажем, для круга; для этого понадобится описать метод '''draw()''', который будет рисовать круг, а также метод '''includesPoint()''', определяющий принадлежность точки (x,y) внутренней области круга (например, по неравенству круга &amp;lt;math&amp;gt;(x-x_0)^2+(y-y_0)^2&amp;lt;/math&amp;gt; ≤ &amp;lt;math&amp;gt;r^2)&amp;lt;/math&amp;gt;. Можно анимировать фигуры. Круг задач неограничен – берите свою и наслаждайтесь поиском ее решений.&lt;br /&gt;
&lt;br /&gt;
===Проверим попадание===&lt;br /&gt;
&lt;br /&gt;
По мере добавления новых типов фигур может возникнуть проблема с вычислением попадания произвольной точки во внутреннюю область (метод '''includesPoint()'''). Для произвольных полигонов и фигур с кривыми можно использовать следующий метод: рисовать однотонную фигуру в невидимом буфере, и если цвет интересующей точки совпадет с цветом фигуры, информировать о попадании.&lt;br /&gt;
&lt;br /&gt;
===Инструментарий разработчика===&lt;br /&gt;
&lt;br /&gt;
* '''''Firebug''''' http://getfirebug.com/ Инструментарий в помощь web-разработчику, предпочитающему ''Firefox''. Упрощает отладку скриптов, таблиц стилей и даже AJAX-запросов.&lt;br /&gt;
* '''MozillaLabs''' https://mozillalabs.com Сайт, посвященный экспериментальным проектам, которые разрабатываются сообществом Mozilla. Передний край открытых web-технологий.&lt;br /&gt;
* '''''Bespin''''' http://mozillalabs.com/bespin Редактор кода, реализованный с использованием HTML5 и JavaScript. Работать с ''Bespin'' можно прямо в облаке Mozilla (https://bespin.mozillalabs.com/). ''Bespin'' прекрасно работает в ''Firefox, Chrome'' и ''Safari'' последних версий.&lt;/div&gt;</summary>
		<author><name>Crazy Rebel</name></author>	</entry>

	</feed>