Строим DOM
Интерфейс программы состоит из более чем 30 элементов DOM. Нужно их как-то собрать вместе.
Очевидным форматом для сложных структур DOM является HTML. Но разделять программу на HTML и скрипт неудобно – для элементов DOM понадобится множество обработчиков событий или других необходимых вещей, которые надо будет как-то обрабатывать из скрипта. Для этого придётся делать много вызовов querySelector
и им подобных, чтобы найти нужный элемент DOM для работы.
Было бы удобно определять части DOM рядом с теми частями кода JavaScript, которые ими управляют. Поэтому я решил создавать всю конструкцию DOM прямо в JavaScript. Как мы видели в главе 13, встроенный интерфейс для создания структур DOM ужасно многословен. Поскольку нам придётся создать много конструкций, нам понадобится вспомогательная функция.
Эта функция – расширенная версия функции elt
из главы 13. Она создаёт элемент с заданным именем и атрибутами, и добавляет все остальные аргументы, которые получает, в качестве дочерних узлов, автоматически преобразовывая строки в текстовые узлы.
function elt(name, attributes) {
var node = document.createElement(name);
if (attributes) {
for (var attr in attributes)
if (attributes.hasOwnProperty(attr))
node.setAttribute(attr, attributes[attr]);
}
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child == "string")
child = document.createTextNode(child);
node.appendChild(child);
}
return node;
}
Так мы легко и просто создаём элементы, не раздувая код до размеров лицензионного соглашения.
Основание
Ядро нашей программы – функция createPaint
, добавляющая интерфейс рисования к элементу DOM, который передаётся в качестве аргумента. Так как мы создаём программу последовательно, мы определяем объект controls
, который будет содержать функции для инициализации разных элементов управления под картинкой.
var controls = Object.create(null);
function createPaint(parent) {
var canvas = elt("canvas", {width: 500, height: 300});
var cx = canvas.getContext("2d");
var toolbar = elt("div", {class: "toolbar"});
for (var name in controls)
toolbar.appendChild(controls[name](cx));
var panel = elt("div", {class: "picturepanel"}, canvas);
parent.appendChild(elt("div", null, panel, toolbar));
}
У каждого элемента управления есть доступ к контексту рисования на холсте, а через него – к элементу <canvas>
. Основное состояние программы хранится в этом холсте – он содержит текущую картинку, выбранный цвет (в свойстве fillStyle
) и размер кисти (в свойстве lineWidth
).
Мы обернём холст и элементы управления в элементы <div>
с классами, чтобы можно было добавить им стили, например серую рамку вокруг картинки.
Выбор инструмента
Первый элемент управления, который мы добавим – элемент <select>
, позволяющий выбирать инструмент рисования. Как и в случае с controls
, мы будем использовать объект для сбора необходимых инструментов, чтобы не надо было описывать их работу в коде по отдельности, и чтобы можно было легко добавлять новые. Этот объект связывает названия инструментов с функцией, которая вызывается при их выборе и при клике на холсте.
var tools = Object.create(null);
controls.tool = function(cx) {
var select = elt("select");
for (var name in tools)
select.appendChild(elt("option", null, name));
cx.canvas.addEventListener("mousedown", function(event) {
if (event.which == 1) {
tools[select.value](event, cx);
event.preventDefault();
}
});
return elt("span", null, "Tooclass="underline" ", select);
};
В поле tool
есть элементы <option>
для всех определённых нами инструментов, а обработчик события "mousedown"
на холсте берёт на себя обязанность вызывать функцию текущего инструмента, передавая ей объекты event
и context
. Также он вызывает preventDefault
, чтобы зажатие и перетаскивание мыши не вызывало выделения участков страницы.
Самый простой инструмент – линия, который рисует линии за мышью. Чтобы рисовать линию, нам надо сопоставить координаты курсора мыши с координатами точек на холсте. Вскользь упомянутый в 13 главе метод getBoundingClientRect
может нам в этом помочь. Он говорит, где показывается элемент, относительно левого верхнего угла экрана. Свойства события мыши clientX
и clientY
также содержат координаты относительно этого угла, поэтому мы можем вычесть верхний левый угол холста из них и получить позицию относительно этого угла.