Рисовалки и редакторы

Интерактивные графические редакторы на базе Paper.js строятся вокруг сцены (Project), слоя (Layer) и элементов (Item). Каждое действие пользователя преобразуется в операции над векторными объектами: создание, трансформация, удаление или модификация.

Ключевую роль играют инструменты (Tool), которые позволяют организовать поведение ввода:

  • onMouseDown — начало действия
  • onMouseDrag — процесс изменения
  • onMouseUp — завершение

Состояние редактора обычно хранится в виде текущего режима (например, рисование, выделение, перемещение) и набора активных объектов.


Реализация базового инструмента рисования

Свободное рисование (freehand) реализуется через динамическое создание пути (Path) и добавление сегментов:

let path;

tool.onMouseD own = function(event) {
    path = new Path();
    path.strokeColor = 'black';
    path.add(event.point);
};

tool.onMouseD rag = function(event) {
    path.add(event.point);
};

tool.onMous eUp = function(event) {
    path.simplify();
};

Метод simplify() уменьшает количество точек, сглаживая линию и оптимизируя производительность.


Кисти и стилизация линий

Визуальное разнообразие достигается за счёт настройки параметров:

  • strokeWidth — толщина
  • strokeColor — цвет
  • strokeCap — форма концов (round, square, butt)
  • dashArray — пунктир

Пример имитации кисти:

path.strokeWidth = 10;
path.strokeCap = 'round';
path.opacity = 0.5;

Для более сложных кистей применяются группы (Group) или клонирование объектов вдоль траектории.


Инструменты фигур

Создание геометрических фигур строится на основе начальной и конечной точки:

let shape;

tool.onMouseD own = function(event) {
    shape = new Path.Rectangle({
        point: event.point,
        size: [0, 0],
        strokeColor: 'black'
    });
};

tool.onMouseD rag = function(event) {
    shape.remove();
    shape = new Path.Rectangle({
        from: event.downPoint,
        to: event.point,
        strokeColor: 'black'
    });
};

Аналогично реализуются:

  • круги (Path.Circle)
  • эллипсы (Path.Ellipse)
  • линии (Path.Line)

Выделение и манипуляция объектами

Для работы с объектами применяется хит-тестирование (hitTest):

tool.onMouseD own = function(event) {
    let hitResult = project.hitTest(event.point);
    if (hitResult) {
        selectedItem = hitResult.item;
    }
};

Перемещение:

tool.onMouseD rag = function(event) {
    if (selectedItem) {
        selectedItem.position += event.delta;
    }
};

Масштабирование и вращение реализуются через трансформации:

selectedItem.scale(1.1);
selectedItem.rotate(10);

Работа с несколькими объектами

Группировка (Group) позволяет объединять элементы:

let group = new Group([path1, path2]);
group.rotate(45);

Слои (Layer) используются для управления порядком рендеринга:

let layer = new Layer();
layer.activate();

Ластик и редактирование контуров

Ластик реализуется несколькими способами:

1. Удаление объектов:

if (hitResult) {
    hitResult.item.remove();
}

2. Булевы операции:

let result = path1.subtract(eraserPath);
path1.remove();

3. Частичное редактирование сегментов:

path.segments[0].point += [10, 0];

История действий (Undo/Redo)

Редактор требует системы отката:

  • стек действий (undoStack)
  • стек возврата (redoStack)

Простейшая реализация:

let undoStack = [];

function saveState() {
    undoStack.push(project.exportJSON());
}

function undo() {
    if (undoStack.length > 0) {
        let state = undoStack.pop();
        project.clear();
        project.importJSON(state);
    }
}

Работа с цветом и заливкой

Цвет задаётся через Color:

path.fillColor = 'red';

Поддерживаются:

  • RGB
  • HSL
  • HEX
  • градиенты (Gradient)

Пример градиента:

path.fillColor = {
    gradient: {
        stops: ['yellow', 'red']
    },
    origin: path.bounds.leftCenter,
    destination: path.bounds.rightCenter
};

Текстовые элементы

Paper.js поддерживает текст:

let text = new PointText({
    point: [100, 100],
    content: 'Hello',
    fillColor: 'black',
    fontSize: 20
});

Редакторы добавляют:

  • изменение содержимого
  • выбор шрифта
  • масштабирование

Слои интерфейса

Редактор обычно делится на:

  • холст (Canvas)
  • панель инструментов
  • панель свойств

Состояние инструментов управляется через переменные:

let currentTool = 'brush';

И переключается:

if (currentTool === 'rectangle') {
    // логика прямоугольника
}

Экспорт и импорт

Сохранение работы:

let json = project.exportJSON();

Загрузка:

project.importJSON(json);

Экспорт в SVG:

let svg = project.exportSVG({ asString: true });

Оптимизация производительности

При большом количестве объектов важно:

  • использовать simplify()
  • избегать частых пересозданий объектов
  • группировать элементы
  • отключать лишние перерисовки

Пример оптимизации:

view.autoUpdate = false;
// изменения
view.update();

Расширение редактора

Функциональность легко расширяется:

  • добавление новых инструментов
  • подключение горячих клавиш
  • интеграция с сервером
  • сохранение в облако

Обработка клавиатуры:

tool.onKeyD own = function(event) {
    if (event.key === 'z') {
        undo();
    }
};

Организация кода

Рекомендуемая структура:

  • модуль инструментов
  • модуль состояния
  • модуль рендеринга
  • модуль истории

Пример:

const tools = {
    brush: new Tool(),
    rectangle: new Tool()
};

Каждый инструмент инкапсулирует свою логику, что делает редактор масштабируемым и удобным для поддержки.


Поведенческие паттерны

Для сложных редакторов применяются:

  • Command Pattern (для undo/redo)
  • Observer (реакция интерфейса)
  • State Machine (режимы инструментов)

Это позволяет поддерживать предсказуемое поведение при большом количестве функций.


Обработка событий и UX

Качественный редактор требует:

  • визуальной обратной связи
  • подсветки выделения
  • ограничения действий (snap, grid)

Пример сетки:

let gridSize = 10;

function snap(point) {
    return new Point(
        Math.round(point.x / gridSize) * gridSize,
        Math.round(point.y / gridSize) * gridSize
    );
}

Работа с курсором

Изменение курсора повышает удобство:

canvas.style.cursor = 'crosshair';

Комплексные редакторы

На базе Paper.js реализуются:

  • редакторы схем
  • графические редакторы
  • инструменты аннотаций
  • простые аналоги Photoshop/Illustrator

Ключевая идея — управление векторной сценой через события и трансформации, где каждый элемент остаётся редактируемым объектом, а не просто пикселями.