HTML5 Canvas - простая программа рисования
92Веб-программирование --- HTML5 --- Простая программа рисования
Кроме рассмотренных ранее, холст имеет много других возможностей. Но на данном этапе вы обладаете достаточным объемом знаний, чтоб создать простую программу рисования на основе холста.
Код JavaScript для этой программы более объемный, чем в предыдущих примерах, но все равно на удивление простой. Мы рассмотрим эту программу по частям в следующих разделах.
Подготовка к рисованию
При загрузке страницы код получает объект холста canvas и подключает к нему функции для обработки нескольких событий JavaScript для разных движений мыши: onMouseDown, onMouseUp, onMouseOut и onMouseMove. (Как мы увидим далее, эти события управляют процессом рисования.) В то же самое время страница сохраняет холст в глобальной переменной canvas, а контекст рисования — в другой глобальной переменной context. Таким образом, эти объекты будут доступны для остального кода:
var canvas;
var context;
window.onload = function() {
canvas = document.getElementById("drawingCanvas");
context = canvas.getContext("2d");
// Подключаем требуемые для рисования события
canvas.onmousedown = startDrawing;
canvas.onmouseup = stopDrawing;
canvas.onmouseout = stopDrawing;
canvas.onmousemove = draw;
}
Чтобы начать рисовать, пользователь выбирает цвет и толщину линии, щелкнув по требуемым значкам в панели инструментов вверху окна рисования. Эти панели инструментов создаются с помощью простых элементов <div>, отформатированных под светло-голубой фон и содержащих по несколько элементов <img>, активируемых щелчком мыши:
<div class="Toolbar">
- Цвет -<br>
<img id="redPen" src="http://professorweb.ru/downloads/pen_red.gif" alt="Красная кисть" onclick="changeColor('rgb(212,21,29)', this)">
<img id="greenPen" src="http://professorweb.ru/downloads/pen_green.gif" alt="Зеленая кисть" onclick="changeColor('rgb(131,190,61)', this)">
<img id="bluePen" src="http://professorweb.ru/downloads/pen_blue.gif" alt="Синяя кисть" onclick="changeColor('rgb(0,86,166)', this)">
</div>
<div class="Toolbar">
- Толщина -<br>
<img src="http://professorweb.ru/downloads/pen_thin.gif" alt="Тонкая кисть" onclick="changeThickness(1, this)">
<img src="http://professorweb.ru/downloads/pen_medium.gif" alt="Нормальная кисть" onclick="changeThickness(5, this)">
<img src="http://professorweb.ru/downloads/pen_thick.gif" alt="Толстая кисть" onclick="changeThickness(10, this)">
</div>
<div class="CanvasContainer">
<canvas id="drawingCanvas" width="500" height="300"></canvas>
</div>
<div class="Toolbar">
- Операции-<br>
<button onclick="saveCanvas()">Сохранить содержимое Canvas</button>
<button onclick="clearCanvas()">Очистить Canvas</button>
<div id="savedCopyContainer">
<img id="savedImageCopy"><br>
Щелкните правой кнопкой мыши для сохранения ...
</div>
</div>
body {
background: white;
}
.Toolbar {
float: left;
font-family: 'Trebuchet MS';
font-size: 14px;
font-variant: small-caps;
text-align: center;
background: #F2F7FE;
padding: 10px 15px 3px 10px;
margin-bottom: 1px;
margin-right: 1px;
border: 1px solid #7B899B;
}
.Toolbar button {
padding: 6px;
margin: 7px 2px;
font-variant: normal;
font-size: 12px;
}
.CanvasContainer {
clear: both;
}
canvas {
border: 1px solid #7B899B;
}
img {
padding: 2px;
border: 2px solid #F2F7FE;
}
img:hover {
border: 2px groove #E4F0FE;
background: white;
}
img.Selected {
border: 2px groove #E4F0FE;
}
#savedCopyContainer {
display: none;
}
#savedCopyContainer img {
width: 250px;
height: 150px;
}
Важной частью этой разметки является атрибут onclick элемента <img>. При щелчке пользователя по значку ручки определенного цвета элемент <img> вызывает функцию changeColor(). Эта функция принимает два параметра — новый цвет, который совпадает с цветом выбранного значка, и ссылки на элемент <img>, по которому щелкнули. Код функции выглядит так:
var previousColorElement;
function changeColor(color, imgElement)
{
// Меняем текущий цвет рисования
context.strokeStyle = color;
// Меняем стиль элемента <img>, по которому щелкнули
imgElement.className = "Selected";
// Возвращаем ранее выбранный элемент <img> в нормальное состояние
if (previousColorElement != null)
previousColorElement.className = "";
previousColorElement = imgElement;
}
Код функции changeColor() выполняет две основные задачи. Во-первых, он устанавливает свойство контекста strokeStyle в значение соответствующего нового цвета. Для этого требуется всего лишь одна строка кода. Во-вторых, код изменяет оформление элемента <img>, по которому щелкнули, заключая его в рамку, чтобы было видно, какой цвет является текущим. Выполнение этой операции требует больше работы, т.к. необходимо отслеживать цветовой элемент <img>, по которому ранее щелкнули, чтобы удалить его рамку.
Функция changeThickness() выполняет почти идентичную работу, только она изменяет значение свойства контекста lineWidth в соответствии с выбранной толщиной линии:
// Отслеживаем элемент <img> для толщины линии, по которому ранее щелкнули
var previousThicknessElement;
function changeThickness (thickness, imgElement)
{
// Изменяем текущую толщину линии
context.lineWidth = thickness;
// Меняем стиль элемента <img>, по которому щелкнули
imgElement.className = "Selected";
// Возвращаем ранее выбранный элемент <img> в нормальное состояние
if (previousThicknessElement != null)
previousThicknessElement.className = "";
previousThicknessElement = imgElement;
}
Рассмотренный выше код подготавливает холст для рисования, но рисовать еще рано. Для этого нужно добавить код, который собственно выполняет рисование, что и делается в следующем разделе.
Рисование на холсте
Процесс рисования начинается, когда пользователь щелкает мышью по холсту. Для отслеживания, когда осуществляется рисование, в программе используется глобальная переменная isDrawing, информирующая остальной код программы, можно ли работать с контекстом рисования.
Как мы видели ранее, событие onMouseDown связано с функцией startDrawing(). Эта функция устанавливает переменную isDrawing, создает новый путь, а потом устанавливает начальную позицию рисования, подготовив таким образом холст для рисования:
function startDrawing(e) {
// Начинаем рисовать
isDrawing = true;
// Создаем новый путь (с текущим цветом и толщиной линии)
context.beginPath();
// Нажатием левой кнопки мыши помещаем "кисть" на холст
context.moveTo(e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop);
}
Чтобы наша программа работала корректно, рисование должно начинаться в текущей позиции, т.е. там, где находится указатель мыши, когда пользователь нажимает левую кнопку. Но задача получения правильных координат этой точки несколько сложновата.
Событие onMouseDown предоставляет координаты курсора мыши (через свойства pageX и pageY, показанные в этом примере), но это координаты относительно всей страницы. Чтобы вычислить соответствующие координаты холста, необходимо от координат левого верхнего угла окна браузера отнять расстояние до левого верхнего угла холста.
Собственно рисование происходит, когда пользователь двигает мышь, удерживая нажатой левую кнопку. При каждом перемещении мыши, даже на один пиксель, активируется событие onMouseMove и исполняется код функции draw(). Если переменная isDrawing установлена, код вычисляет текущие координаты холста (т.е координаты точки, в которой в данный момент находится указатель мыши), а потом вызывает метод lineTo(), который добавляет путь соответствующего отрезка линии, после чего вызывается метод stroke(), который прорисовывает эту линию:
function draw(e) {
if (isDrawing == true)
{
// Определяем текущие координаты указателя мыши
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
// Рисуем линию до новой координаты
context.lineTo(x, y);
context.stroke();
}
}
Если пользователь продолжает перемещать мышь, снова вызывается функция draw(), опять добавляя отрезок к уже нарисованной линии. Этот отрезок настолько короткий — длиной всего лишь в один или два пиксела, что он даже не выглядит, как прямая линия в начале рисования.
Наконец, когда пользователь отпускает кнопку мыши или выводит курсор за пределы холста, срабатывает событие onMouseUp или onMouseOut соответственно. Оба эти события активируют функцию stopDrawing(), которая указывает приложению прекратить рисование:
function stopDrawing() {
isDrawing = false;
}
На данном этапе мы рассмотрели все аспекты рисования:
Теперь перейдем к обсуждению функций очистки холста или сохранения созданного рисунка. Для этих целей предназначены две кнопки на панели инструментов внизу холста. Нажатие кнопки "Очистить Canvas" вызывает функцию clearCanvas(), которая полностью очищает поверхность холста с помощью метода контекста clearRect():
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
Операция сохранения содержимого холста более сложна, и мы посвятим ей отдельный раздел.
Сохранение содержимого Canvas
Задача сохранения содержимого холста требует рассмотрения множества опций. Прежде всего, нужно решить, каким образом получить данные рисунка. Для решения этой задача холст предоставляет три возможных подхода:
- Использовать URL данных
При этом подходе содержимое холста преобразуется в файл изображения, которое потом переводится в последовательность символов, оформленных в виде URL. Это позволяет получить аккуратный и компактный способ для перемещения данных изображения (например, его можно передать элементу <img> и отослать на веб-сервер). В нашей программе рисования будем использовать этот подход.
- Использовать метод getImageData()
Этот подход позволяет получить "сырые" пиксельные данные, которыми можно потом манипулировать как угодно.
- Сохранять список "шагов".
Например, можно организовать массив, содержащий список всех линий, нарисованных на холсте. Эти данные можно потом сохранить и использовать для воспроизведения изображения. Данный подход требует меньше места для хранения изображения, а также предоставляет большую гибкость для последующей работы с ним.
Но это еще не все. Определившись с типом содержимого для сохранения, нужно решить, где это содержимое сохранить. Возможны, среди прочих, следующие опции:
В файле изображения. Например, содержимое холста сохраняется в виде файла формата PNG или JPEG на локальном жестком диске художника. Это подход, который применяется в нашей программе рисования, и мы его рассмотрим далее.
В локальной системе хранения.
На веб-сервере. После передачи данных веб-серверу последний может сохранить их в файле или базе данных и предоставить при следующем посещении страницы пользователем.
Для сохранения содержимого холста в нашей программе рисования применяется возможность, которая называется URL данных (data URL). Чтобы получить URL для текущих данных, мы просто используем метод холста toDataURL(). Если вызвать метод toDataURL(), не передав ему никаких параметров, то получим изображение в формате PNG. Альтернативно, методу можно указать требуемый формат изображения:
var url = canvas.toDataURL("image/jpeg");
Но если браузер не может предоставить требуемый формат, он опять выдаст изображение в формате PNG, преобразованное в длинную строку.
Что же собой представляет URL данных? Технически это просто длинная строка символов, закодированных алгоритмом Base64, которая начинается с текста: data:image/png;base64. Это выглядит как бессмыслица, по крайней мере, для людей, т.к. эти данные предназначены для понимания компьютерными программами, например браузерами. URL данных для содержимого холста выглядит примерно таким образом:
data:image/png;base64, iVBORwOKGgoAAAANSUhEUgAAAfQAAAEsCAYAAAA2uOHXAAAAA ...
Таким образом, задача преобразования данных изображения в URL данных не представляет никаких трудностей. Но что можно делать дальше с этим URL данных? Можно отправить его на веб-сервер для длительного хранения. Пример выполнения этой операции с помощью кода PHP представлен на веб-странице HTML5: Saving Canvas Image Data Using PHP And Ajax.
Но возможности для сохранения данных на стороне клиента несколько ограничены. Некоторые браузеры позволяют перейти непосредственно к URL данных. Это означает, что для перехода к изображению можно использовать следующий код:
window.location = canvas.toDataURL();
Более надежным методом будет передать URL данных в элемент <img>, что и делает наша программа рисования:
function saveCanvas() {
// Находим элемент <img>
var imageCopy = document.getElementById("savedImageCopy");
// Отображаем данные холста в элементе <img>
imageCopy.src = canvas.toDataURL();
// Показываем элемент <div>, делая изображение видимым
// делая изображение видимым
var imageContainer = document.getElementById("savedCopyContainer");
imageContainer.style.display = "block";
}
Этот код не совсем сохраняет данные изображения, т.к. изображение еще не сохранено в файле. Но для этого требуется всего лишь один шаг — просто щелкнуть правой кнопкой мыши по уменьшенному изображению в панели под холстом и в открывшемся контекстном меню выбрать команду "Сохранить картинку как". Это не так удобно, как закачка файла или диалоговое окно Сохранить, но единственный способ, который надежно работает во всех браузерах.