Выразительный JavaScript (Хавербеке) - страница 206

>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, "Tool: ", select);

>};

В поле >tool есть элементы > для всех определённых нами инструментов, а обработчик события >"mousedown" на холсте берёт на себя обязанность вызывать функцию текущего инструмента, передавая ей объекты >event и >context. Также он вызывает >preventDefault, чтобы зажатие и перетаскивание мыши не вызывало выделения участков страницы.

Самый простой инструмент – линия, который рисует линии за мышью. Чтобы рисовать линию, нам надо сопоставить координаты курсора мыши с координатами точек на холсте. Вскользь упомянутый в 13 главе метод >getBoundingClientRect может нам в этом помочь. Он говорит, где показывается элемент, относительно левого верхнего угла экрана. Свойства события мыши >clientX и >clientY также содержат координаты относительно этого угла, поэтому мы можем вычесть верхний левый угол холста из них и получить позицию относительно этого угла.

>function relativePos(event, element) {

>  var rect = element.getBoundingClientRect();

>  return {x: Math.floor(event.clientX - rect.left),

>          y: Math.floor(event.clientY - rect.top)};

>}

Несколько инструментов рисования должны слушать событие >"mousemove", пока кнопка мыши нажата. Функция >trackDrag регистрирует и убирает событие для данных ситуаций.

>function trackDrag(onMove, onEnd) {

>  function end(event) {

>    removeEventListener("mousemove", onMove);

>    removeEventListener("mouseup", end);

>    if (onEnd)

>      onEnd(event);

>  }

>  addEventListener("mousemove", onMove);

>  addEventListener("mouseup", end);

>}

У неё два аргумента. Один – функция, которая вызывается при каждом событии >"mousemove", а другая – функция, которая вызывается при отпускании кнопки. Каждый аргумент может быть не задан.

Инструмент для рисования линий использует две вспомогательные функции для самого рисования.

>tools.Line = function(event, cx, onEnd) {

>  cx.lineCap = "round";


>  var pos = relativePos(event, cx.canvas);

>  trackDrag(function(event) {

>    cx.beginPath();

>    cx.moveTo(pos.x, pos.y);

>    pos = relativePos(event, cx.canvas);

>    cx.lineTo(pos.x, pos.y);

>    cx.stroke();

>  }, onEnd);

>};

Функция сначала устанавливает свойство контекста >lineCap в >“round”, из-за чего концы нарисованного пути становятся закруглёнными, а не квадратными, как это происходит по умолчанию. Этот трюк обеспечивает непрерывность линий, когда они нарисованы в несколько приёмов. Если рисовать линии большой ширины, вы увидите разрывы в углах линий, если будете использовать установку