В этом разделе мы добавляем возможность выбирать цвета и рисовать линии, круги и квадраты на холсте HTML5!

Как работает процесс рисования

Чтобы рисовать на холсте, нам нужно знать

  • Каким цветом мы рисуем
  • Какую форму мы рисуем
  • Рисуем ли мы первую или вторую точку фигуры
  • Где находятся начальная и конечная точки

Добавьте эти свойства в конструктор класса Canvas:

this.activeColor = '#000000';
this.startPoint = null;
this.endPoint = null;
this.pointMode = 'start';
this.mode = 'Line';

Поэтому по умолчанию мы рисуем черную линию. Теперь мы добавляем функцию, которая позволяет нам изменить цвет, которым мы рисуем, как метод класса Canvas:

setColor(color) {
    this.activeColor = color;
    this.ctx.strokeStyle = color;
    this.ctx.fillStyle = color;
}

Как заставить цветовую палитру работать

Теперь, когда у нас есть метод setColor, мы можем закончить метод Palette.draw(). Мы добавляем объект холста в качестве параметра и прикрепляем обработчик события onclick к каждому квадрату палитры:

draw(canvas) {
    const row1 = document.querySelectorAll('#row-1 .palette');
    const row2 = document.querySelectorAll('#row-2 .palette');
    row1.forEach((div, idx) => {
        div.style.backgroundColor = this.colors[0][idx];
        div.onclick = e => canvas.setColor(this.colors[0][idx]);
    });
    row2.forEach((div, idx) => {
        div.style.backgroundColor = this.colors[1][idx];
        div.onclick = e => canvas.setColor(this.colors[1][idx]);
    });
}

Поскольку мы добавили его здесь в качестве параметра, нам нужно установить его в файле index.html:

palette.draw(canvas);

Выбор формы для рисования

Этот проект позволяет рисовать 5 фигур: линию, полый круг, заполненный круг, полый прямоугольник и заполненный прямоугольник. По умолчанию мы используем линию по умолчанию. Чтобы изменить форму (называемую в коде mode), сначала добавьте этот метод к Canvas:

setMode(mode) {
    this.mode = mode;
}

Затем в HTML-коде под цветовой палитрой добавьте ряд кнопок, позволяющих нам выбирать, какую фигуру мы хотим нарисовать:

<div id="draw-methods">
    <button onclick="canvas.setMode('Line')">Line</button>
    <button onclick="canvas.setMode('Hollow Rectangle')">Hollow Rectangle</button>
    <button onclick="canvas.setMode('Filled Rectangle')">Filled Rectangle</button>
    <button onclick="canvas.setMode('Hollow Circle')">Hollow Circle</button>
    <button onclick="canvas.setMode('Filled Circle')">Filled Circle</button>
</div>

Обработка щелчков на холсте для рисования

Вот метод handleDraw для Canvas . Я дам вам код, а затем объясню, что он делает, потому что он сложнее того, что мы видели до сих пор.

handleDraw(e) {
    const rect = this.canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    if (this.pointMode == 'start') {
        this.startPoint = [x, y];
        this.pointMode = 'end';
    } else if (this.pointMode == 'end') {
        this.pointMode = 'start';
        this.endPoint = [x, y];
        // do the drawing
        if (this.mode == 'Line') {
            this.drawLine(this.startPoint, this.endPoint);
        } else if (this.mode == 'Hollow Rectangle') {
            this.drawHollowRectangle(this.startPoint, this.endPoint);
        } else if (this.mode == 'Filled Rectangle') {
            this.drawFilledRectangle(this.startPoint, this.endPoint);
        } else if (this.mode == 'Hollow Circle') {
            this.drawHollowCircle(this.startPoint, this.endPoint);
        } else if (this.mode == 'Filled Circle') {
            this.drawFilledCircle(this.startPoint, this.endPoint);
        }
        this.startPoint = null;
        this.endPoint = null;
    }
}

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

getBoundingRectClient возвращает значения, например, насколько далеко влево и вниз находится холст от этого верхнего левого угла. Верхний левый угол страницы равен (0, 0), и чем дальше вправо и вниз вы идете, тем больше значения. e — это параметр, передаваемый функции, представляющий событие щелчка мыши. clientX и clientY обозначают место на странице, на которое вы нажали. Вычитание смещения элемента холста дает вам положение мыши внутри элемента холста.

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

Методы рисования фигур, возможно, кажутся более сложными, чем они есть на самом деле. Для drawLine все просто:

drawLine(startPoint, endPoint) {
    this.ctx.beginPath();
    this.ctx.moveTo(startPoint[0], startPoint[1]);
    this.ctx.lineTo(endPoint[0], endPoint[1]);
    this.ctx.stroke();
}

Для рисования полых и заполненных прямоугольников требуется просто вычислить две точки, которые не были нажаты:

drawHollowRectangle(startPoint, endPoint) {
    this.ctx.beginPath();
    this.ctx.strokeRect(
        startPoint[0],
        startPoint[1],
        endPoint[0] - startPoint[0],
        endPoint[1] - startPoint[1]
    );
}
drawFilledRectangle(startPoint, endPoint) {
    this.ctx.beginPath();
    this.ctx.fillRect(
        startPoint[0],
        startPoint[1],
        endPoint[0] - startPoint[0],
        endPoint[1] - startPoint[1]
    );
}

Методы рисования кругов требуют вычисления радиуса по формуле расстояния, а затем рисования дуги в 360 градусов.

drawHollowCircle(startPoint, endPoint) {
    const x = startPoint[0] - endPoint[0];
    const y = startPoint[1] - endPoint[1];
    const radius = Math.sqrt(x * x + y * y);
    this.ctx.beginPath();
    this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
    this.ctx.stroke();
}
drawFilledCircle(startPoint, endPoint) {
    const x = startPoint[0] - endPoint[0];
    const y = startPoint[1] - endPoint[1];
    const radius = Math.sqrt(x * x + y * y);
    this.ctx.beginPath();
    this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
    this.ctx.fill();
}

Присоединение прослушивателя событий Canvas

Чтобы холст действительно реагировал на нажатие, необходимо добавить эти две строки в конструктор:

this.handleDraw = this.handleDraw.bind(this);
this.canvas.addEventListener('click', this.handleDraw);

Те из вас, кто работал с React, могут узнать этот шаблон. Но зачем это нужно? Это связано с тем, как работает прослушиватель событий. Без привязки в handleDraw переменная контекста this указывала бы на HTML-элемент, по которому был сделан щелчок. В данном случае это элемент холста. Но это означало бы, что у нас нет доступа к Canvas объекту, у которого есть нужные нам методы. Используя привязку, мы заставляем this ссылаться на объект.

Собираем все вместе

Вот как должны выглядеть файлы, измененные на этом этапе:

После обновления страницы попробуйте изменить цвет, указать, какую фигуру рисовать, и начать нажимать на холст. Вот небольшое тестирование, которое я провел, чтобы убедиться, что оно работает:

В следующем разделе мы добавим некоторые удобные функции, которые показывают, какая фигура выбрана, и краткие инструкции о том, что приложение ожидает от пользователя дальше.

Серия приложений для совместного рисования