Выбрать главу

Теперь наша программа для рисования готова. Запустите код и попробуйте.

<link rel="stylesheet" href="css/paint.css">

<body>

  <script>createPaint(document.body);</script>

</body>

Упражнения

В программе ещё очень много чего можно улучшить. Давайте добавим ей возможностей.

Прямоугольники

Определите инструмент Rectangle, заполняющий прямоугольник (см. метод fillRect из главы 16) текущим цветом. Прямоугольник должен появляться из той точки, где пользователь нажал кнопку мыши, и до той точки, где он отпустил кнопку. Заметьте, что последнее действие может произойти левее или выше первого.

Когда это заработает, вы заметите, что изображение прямоугольника дрожит и его плохо видно. Можете ли вы придумать способ показа прямоугольника во время движения мыши, но чтобы он не выводился на холст, пока кнопка не отпущена?

Если не придумаете, вспомните о стиле position: absolute, который мы обсуждали в главе 13, который можно использовать, чтобы выводить узел поверх остального документа. Свойства pageX и pageY событий мыши можно использовать для точного расположения элемента под мышью, записывая нужные значения в стили left, top, width и height.

<script>

  tools.Rectangle = function(event, cx) {

    // Ваш код

  };

</script>

<link rel="stylesheet" href="css/paint.css">

<body>

  <script>createPaint(document.body);</script>

</body>

Выбор цвета

Ещё один часто встречающийся инструмент – выбор цвета, который позволяет щелчком мыши на картинке выбрать цвет, который находится под курсором. Сделайте такой инструмент.

Для его изготовления понадобится доступ к содержимому холста. Метод toDataURL примерно это и делал, но получить информацию о пикселе из URL с данными сложно. Вместо этого мы возьмём метод контекста getImageData, возвращающий прямоугольный кусок картинки в виде объекта со свойствами width, height и data. В свойстве data содержится массив чисел от 0 до 255, и для каждого пикселя хранится четыре номера — red, green, blue и alpha (прозрачность).

Этот пример получает числа из одного пикселя холста, один раз, когда тот пуст (все пиксели – прозрачные чёрные), и один раз, когда пиксель окрашен в красный цвет.

function pixelAt(cx, x, y) {

  var data = cx.getImageData(x, y, 1, 1);

  console.log(data.data);

}

var canvas = document.createElement("canvas");

var cx = canvas.getContext("2d");

pixelAt(cx, 10, 10);

// → [0, 0, 0, 0]

cx.fillStyle = "red";

cx.fillRect(10, 10, 1, 1);

pixelAt(cx, 10, 10);

// → [255, 0, 0, 255]

Аргументы getImageData показывают начальные координаты прямоугольника x и y, которые нам надо получить, за которыми идут ширина и высота.

Игнорируйте прозрачность в этом упражнении, работайте только с первыми тремя цифрами для заданного пикселя. Также не волнуйтесь по поводу обновления поля color при выборе цвета. Просто убедитесь, что fillStyle и strokeStyle контекста установлены в цвет, оказавшийся под курсором.

Помните, что эти свойства принимают любой цвет, который понимает CSS, включая запись вида rgb(R, G, B), которую вы видели в главе 15.

Метод getImageData имеет те же ограничения, что и toDataURL – он выдаст ошибку, когда на холсте содержатся пиксели картинки, скачанной с другого домена. Используйте запись try/catch для сообщения об этих ошибках через окно alert.

<script>

  tools["Pick color"] = function(event, cx) {

    // Your code here.

  };

</script>

<link rel="stylesheet" href="css/paint.css">

<body>

  <script>createPaint(document.body);</script>

</body>

Заливка

Это упражнение более сложное, чем предыдущие, и оно потребует разработки нетривиального решения хитрой задачи. Убедитесь, что у вас есть свободное время и терпение перед началом работы, и не отчаивайтесь, если сразу у вас что-то не будет получаться.

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