Блог Людмилы Третьяковой

Пиксельный редактор

2018-07-18 15:43

Следующий проект для изучения возможностей React+Redux - пиксельный редактор. PixelEditor

В пиксельном редакторе есть следующие инструменты для рисования:

А также дополнительные возможности:

Размер области для рисования - 25 х 28 пикселей.

Область рисования редактора первоначально была реализована путем добавления сетки из div-компонентов, где каждый div - представлял собой один пиксель будущего изображения. От этого способа пришлось отказаться, т.к. при рисовании прямоугольника стала заметна задержка рисования. Чтобы найти проблему, я измерила время обработки каждого события с помощью React Developer Tools.

Оказалось, что одно событиие mousemove и связанная с ним пачка событий выполняются примерно 311ms, что довольно много. При этом 290ms осуществляется обновление и перерисовка компонента Canvas (который на самом деле div) и его дочерних компонентов Cell (тех самых div-пикселей). PixelEditor

После перехода к обрасти рисования на канве (canvas), скорость отрисовки графических примитивов возросла в несколько раз. Это отражено и при замере скорости в React Developer Tools.

PixelEditor Сетка канвы для визуализации пикселей выполнена в CSS. Размер в backgroundSize - размер ячейки.

    const canvasStyle = {
        backgroundSize: `${CELL_WIDTH}px ${CELL_WIDTH}px`
    }
  background: linear-gradient(to right, #464646 1px, transparent 1px), 
                    linear-gradient(to bottom, #464646 1px, transparent 1px);

Для рисования графических примитивов реализованы следующие алгоритмы:

Кусок кода для расчета координат прямой линии по алгоритму Брезенхема:

    ...
    if (Math.abs(startX - endX) > Math.abs(startY - endY)) {
        const angularRatio = Math.abs((endY - startY) / (endX - startX));
        const xCoords = getCoordsOffset(startX, endX);
        const yCoords = bresenhemLine(startY, endY, angularRatio, xCoords.length);

        return filterCanvasBorder(
                    yCoords.map((item, inx) => ([item, xCoords[inx]])))
    ...

    const bresenhemLine = (start, end, angularRatio, len) => {
    let err = 0;
    let coord = start;
    return [start].concat(
        Array(len - 1)
            .fill()
            .map((_) => {
                err += angularRatio;
                if (err >= 0.5) {
                    coord += (end - start) > 0 && (end - start) !== 0 ? 1 : -1;
                    err -= 1;
                }
                return coord
            })
    )
}

Нарисованное изображение сохраняется на компьютере пользователя в формате PNG.

PixelEditor

Ссылка на репозиторий

Хотите порисовать?