Drag and drop реализация

В PixiJS объекты сцены являются интерактивными спрайтами (PIXI.Sprite) или контейнерами (PIXI.Container). Для реализации механизма Drag and Drop необходимо использовать систему событий PixiJS, которая позволяет отслеживать действия мыши и касаний. Основные события:

  • pointerdown — нажатие на объект.
  • pointerup — отпускание объекта.
  • pointerupoutside — отпускание за пределами сцены.
  • pointermove — перемещение указателя.

Чтобы объект реагировал на события, нужно включить его интерактивность:

sprite.interactive = true;
sprite.buttonMode = true; // Курсор изменяется при наведении

Захват объекта

При событии pointerdown фиксируется начальная позиция указателя и объекта. Это позволяет корректно смещать объект без “скачков”:

sprite.on('pointerdown', onDragStart);

function onDragStart(event) {
    this.data = event.data; // Сохраняем данные события
    this.dragging = true;    // Флаг состояния
    this.dragOffset = {
        x: event.data.getLocalPosition(this.parent).x - this.x,
        y: event.data.getLocalPosition(this.parent).y - this.y
    };
}

Использование getLocalPosition(this.parent) важно для учета координат родительского контейнера.

Перемещение объекта

Обновление позиции происходит в обработчике pointermove. Перемещение выполняется только если объект находится в состоянии dragging:

sprite.on('pointermove', onDragMove);

function onDragMove() {
    if (this.dragging) {
        const newPosition = this.data.getLocalPosition(this.parent);
        this.x = newPosition.x - this.dragOffset.x;
        this.y = newPosition.y - this.dragOffset.y;
    }
}

Такой подход обеспечивает плавное перемещение и правильное смещение относительно точки захвата.

Отпускание объекта

При событии pointerup или pointerupoutside объект прекращает перемещаться. Необходимо очистить данные и флаг:

sprite.on('pointerup', onDragEnd);
sprite.on('pointerupoutside', onDragEnd);

function onDragEnd() {
    this.dragging = false;
    this.data = null;
}

Ограничения движения

Для ограничения перемещения объекта внутри границ сцены или контейнера используют проверку координат при обновлении:

function onDragMove() {
    if (!this.dragging) return;
    const pos = this.data.getLocalPosition(this.parent);

    const minX = 0, maxX = app.screen.width - this.width;
    const minY = 0, maxY = app.screen.height - this.height;

    this.x = Math.min(Math.max(pos.x - this.dragOffset.x, minX), maxX);
    this.y = Math.min(Math.max(pos.y - this.dragOffset.y, minY), maxY);
}

Такой код предотвращает выход объекта за пределы видимой области.

Drag and Drop нескольких объектов

Для работы с множеством объектов удобно создавать функцию и применять ее к каждому спрайту:

function makeDraggable(sprite) {
    sprite.interactive = true;
    sprite.buttonMode = true;
    sprite.on('pointerdown', onDragStart)
          .on('pointermove', onDragMove)
          .on('pointerup', onDragEnd)
          .on('pointerupoutside', onDragEnd);
}

spritesArray.forEach(makeDraggable);

Это позволяет централизованно управлять поведением без дублирования кода.

Перенос в контейнеры

Drag and Drop работает не только со спрайтами, но и с контейнерами. При этом важно учитывать координаты локальной системы:

container.on('pointerdown', onDragStart)
         .on('pointermove', onDragMove)
         .on('pointerup', onDragEnd)
         .on('pointerupoutside', onDragEnd);

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

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

  • Использовать один обработчик pointermove для всех объектов через делегирование при большом количестве спрайтов.
  • Минимизировать создание новых объектов внутри цикла pointermove.
  • Для сложных сцен применять interaction.moveWhenInside = true, чтобы отслеживать движения только внутри канвы.

Взаимодействие с другими событиями

Drag and Drop часто сочетается с кликами или hover-эффектами. Важно корректно разделять состояния:

sprite.on('pointertap', () => {
    if (!sprite.dragging) {
        console.log('Объект кликнут');
    }
});

Проверка dragging предотвращает срабатывание клика во время перемещения.

Пример комплексной реализации

const app = new PIXI.Application({ width: 800, height: 600 });
document.body.appendChild(app.view);

const sprite = PIXI.Sprite.from('image.png');
sprite.x = 100;
sprite.y = 100;

makeDraggable(sprite);
app.stage.addChild(sprite);

Этот код создает интерактивный спрайт с возможностью перетаскивания, учитывая точку захвата и ограничения по экрану, что формирует базовую основу для любых Drag and Drop интерфейсов в PixiJS.