Drag and drop

Paper.js предоставляет мощные средства для работы с векторной графикой на канвасе, включая реализацию drag and drop для объектов. В Paper.js любой объект типа Path, Shape, Raster или Group может быть перемещён мышью при помощи событий mouseDown, mouseDrag и mouseUp.

Связь объектов с событиями мыши

Каждый объект в Paper.js может быть связан с обработчиками событий мыши. Основные события для реализации drag and drop:

  • onMouseDown(event) — фиксирует момент начала перетаскивания.
  • onMouseDrag(event) — вызывается при перемещении мыши с зажатой кнопкой.
  • onMouseUp(event) — фиксирует окончание перетаскивания.
var circle = new Path.Circle({
    center: [100, 100],
    radius: 50,
    fillColor: 'red'
});

circle.onMouseD own = function(event) {
    this.data.dragging = true;
    this.data.startPosition = this.position.clone();
    this.data.offset = event.point.subtract(this.position);
};

circle.onMouseD rag = function(event) {
    if (this.data.dragging) {
        this.position = event.point.subtract(this.data.offset);
    }
};

circle.onMous eUp = function(event) {
    this.data.dragging = false;
};

Использование свойства data

Свойство data объекта Paper.js позволяет хранить пользовательские данные. Для drag and drop часто используют:

  • dragging — логическая переменная, указывающая на активность перетаскивания.
  • startPosition — начальная позиция объекта перед перемещением.
  • offset — смещение курсора относительно центра объекта.

Это обеспечивает плавное перемещение без “скачков” при клике в произвольной точке объекта.

Перетаскивание группы объектов

Объединение нескольких элементов в Group позволяет перемещать их как единое целое:

var rect1 = new Path.Rectangle([0,0], [50,50]);
rect1.fillColor = 'blue';
var rect2 = new Path.Rectangle([60,0], [50,50]);
rect2.fillColor = 'green';

var group = new Group([rect1, rect2]);

group.onMouseD own = function(event) {
    this.data.dragging = true;
    this.data.offset = event.point.subtract(this.position);
};

group.onMouseD rag = function(event) {
    if (this.data.dragging) {
        this.position = event.point.subtract(this.data.offset);
    }
};

group.onMous eUp = function(event) {
    this.data.dragging = false;
};

Группы упрощают управление сложными сценами и позволяют сохранять относительное расположение объектов.

Ограничение области перемещения

Можно ограничить перемещение объекта рамкой или определённой областью:

circle.onMouseD rag = function(event) {
    var newPos = event.point.subtract(this.data.offset);
    if (newPos.x < 50) newPos.x = 50;
    if (newPos.y < 50) newPos.y = 50;
    if (newPos.x > 450) newPos.x = 450;
    if (newPos.y > 450) newPos.y = 450;
    this.position = newPos;
};

Таким образом создаётся эффект “ограниченной зоны” для drag and drop.

Перетаскивание с анимацией

Для более плавного перемещения можно использовать свойства tweening:

circle.onMouseD rag = function(event) {
    var target = event.point.subtract(this.data.offset);
    this.tween(
        { position: this.position },
        { position: target },
        0.2 // длительность анимации в секундах
    );
};

Paper.js не имеет встроенного tweening API, но можно реализовать простую интерполяцию с помощью view.onFrame:

circle.onMouseD rag = function(event) {
    this.data.target = event.point.subtract(this.data.offset);
};

view.onFr ame = function() {
    if (circle.data.target) {
        circle.position = circle.position.add(
            circle.data.target.subtract(circle.position).multiply(0.2)
        );
    }
};

Drag and Drop с изменением слоя

Для случаев, когда объекты накладываются друг на друга, важно изменять z-index через bringToFront():

circle.onMouseD own = function(event) {
    this.bringToFront();
    this.data.dragging = true;
    this.data.offset = event.point.subtract(this.position);
};

Это гарантирует, что перемещаемый объект всегда виден поверх остальных элементов.

Реализация перетаскивания нескольких объектов

Paper.js позволяет отслеживать, какие объекты выделены, и перемещать их вместе:

var selectedItems = [];

function select(item) {
    if (!selectedItems.includes(item)) {
        selectedItems.push(item);
        item.strokeColor = 'orange';
    }
}

function deselect(item) {
    selectedItems = selectedItems.filter(i => i !== item);
    item.strokeColor = 'black';
}

view.onMouseD own = function(event) {
    var hit = project.hitTest(event.point);
    if (hit) select(hit.item);
};

view.onMouseD rag = function(event) {
    selectedItems.forEach(item => {
        item.position = item.position.add(event.delta);
    });
};

Такой подход упрощает создание интерактивных редакторов и игр, где требуется перемещать несколько объектов одновременно.

Взаимодействие с HTML событиями

Paper.js интегрируется с обычными событиями DOM. Для сложных интерфейсов можно использовать комбинацию событий канваса и addEventListener:

var tool = new Tool();

tool.onMouseD own = function(event) {
    console.log("Клик в позиции:", event.point);
};

tool.onMouseD rag = function(event) {
    console.log("Перемещение на:", event.delta);
};

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

Итоговые рекомендации

  • Всегда хранить смещение курсора через offset для плавного drag and drop.
  • Для групповых объектов использовать Group и единые обработчики событий.
  • Ограничивать область перемещения для точного контроля пользовательского интерфейса.
  • Использовать bringToFront() при наложении объектов для правильного отображения.
  • При необходимости применять интерполяцию для создания анимаций движения.

Эти подходы формируют основу интерактивного управления объектами в Paper.js, позволяя создавать полноценные редакторы, визуализации и игровые интерфейсы.