Source: js/plugins/Drag.js

js/plugins/Drag.js

import { Point } from '@pixi/math';
import { Plugin } from './Plugin';
const DEFAULT_DRAG_OPTIONS = {
    direction: 'all',
    pressDrag: true,
    wheel: true,
    wheelScroll: 1,
    reverse: false,
    clampWheel: false,
    underflow: 'center',
    factor: 1,
    mouseButtons: 'all',
    keyToPress: null,
    ignoreKeyToPressOnTouch: false,
    lineHeight: 20,
    wheelSwapAxes: false,
};
/**
 * Plugin to enable panning/dragging of the viewport to move around.
 *
 * @public
 */
export class Drag extends Plugin {
    /**
     * This is called by {@link Viewport.drag}.
     */
    constructor(parent, options = {}) {
        super(parent);
        /** Array of event-handlers for window */
        this.windowEventHandlers = new Array();
        this.options = Object.assign({}, DEFAULT_DRAG_OPTIONS, options);
        this.moved = false;
        this.reverse = this.options.reverse ? 1 : -1;
        this.xDirection = !this.options.direction || this.options.direction === 'all' || this.options.direction === 'x';
        this.yDirection = !this.options.direction || this.options.direction === 'all' || this.options.direction === 'y';
        this.keyIsPressed = false;
        this.parseUnderflow();
        this.mouseButtons(this.options.mouseButtons);
        if (this.options.keyToPress) {
            this.handleKeyPresses(this.options.keyToPress);
        }
    }
    /**
     * Handles keypress events and set the keyIsPressed boolean accordingly
     *
     * @param {array} codes - key codes that can be used to trigger drag event
     */
    handleKeyPresses(codes) {
        const keydownHandler = (e) => {
            if (codes.includes(e.code)) {
                this.keyIsPressed = true;
            }
        };
        const keyupHandler = (e) => {
            if (codes.includes(e.code)) {
                this.keyIsPressed = false;
            }
        };
        this.addWindowEventHandler("keyup", keyupHandler);
        this.addWindowEventHandler("keydown", keydownHandler);
    }
    addWindowEventHandler(event, handler) {
        window.addEventListener(event, handler);
        this.windowEventHandlers.push({ event, handler });
    }
    destroy() {
        this.windowEventHandlers.forEach(({ event, handler }) => {
            window.removeEventListener(event, handler);
        });
    }
    /**
     * initialize mousebuttons array
     * @param {string} buttons
     */
    mouseButtons(buttons) {
        if (!buttons || buttons === 'all') {
            this.mouse = [true, true, true];
        }
        else {
            this.mouse = [
                buttons.indexOf('left') !== -1,
                buttons.indexOf('middle') !== -1,
                buttons.indexOf('right') !== -1
            ];
        }
    }
    parseUnderflow() {
        const clamp = this.options.underflow.toLowerCase();
        if (clamp === 'center') {
            this.underflowX = 0;
            this.underflowY = 0;
        }
        else {
            if (clamp.includes('left')) {
                this.underflowX = -1;
            }
            else if (clamp.includes('right')) {
                this.underflowX = 1;
            }
            else {
                this.underflowX = 0;
            }
            if (clamp.includes('top')) {
                this.underflowY = -1;
            }
            else if (clamp.includes('bottom')) {
                this.underflowY = 1;
            }
            else {
                this.underflowY = 0;
            }
        }
    }
    /**
     * @param {PIXI.InteractionEvent} event
     * @returns {boolean}
     */
    checkButtons(event) {
        const isMouse = event.data.pointerType === 'mouse';
        const count = this.parent.input.count();
        if ((count === 1) || (count > 1 && !this.parent.plugins.get('pinch', true))) {
            if (!isMouse || this.mouse[event.data.button]) {
                return true;
            }
        }
        return false;
    }
    /**
     * @param {PIXI.InteractionEvent} event
     * @returns {boolean}
     */
    checkKeyPress(event) {
        return (!this.options.keyToPress
            || this.keyIsPressed
            || (this.options.ignoreKeyToPressOnTouch && event.data.pointerType === 'touch'));
    }
    down(event) {
        if (this.paused || !this.options.pressDrag) {
            return false;
        }
        if (this.checkButtons(event) && this.checkKeyPress(event)) {
            this.last = { x: event.data.global.x, y: event.data.global.y };
            this.current = event.data.pointerId;
            return true;
        }
        this.last = null;
        return false;
    }
    get active() {
        return this.moved;
    }
    move(event) {
        if (this.paused || !this.options.pressDrag) {
            return false;
        }
        if (this.last && this.current === event.data.pointerId) {
            const x = event.data.global.x;
            const y = event.data.global.y;
            const count = this.parent.input.count();
            if (count === 1 || (count > 1 && !this.parent.plugins.get('pinch', true))) {
                const distX = x - this.last.x;
                const distY = y - this.last.y;
                if (this.moved
                    || ((this.xDirection && this.parent.input.checkThreshold(distX))
                        || (this.yDirection && this.parent.input.checkThreshold(distY)))) {
                    const newPoint = { x, y };
                    if (this.xDirection) {
                        this.parent.x += (newPoint.x - this.last.x) * this.options.factor;
                    }
                    if (this.yDirection) {
                        this.parent.y += (newPoint.y - this.last.y) * this.options.factor;
                    }
                    this.last = newPoint;
                    if (!this.moved) {
                        this.parent.emit('drag-start', {
                            event,
                            screen: new Point(this.last.x, this.last.y),
                            world: this.parent.toWorld(new Point(this.last.x, this.last.y)),
                            viewport: this.parent
                        });
                    }
                    this.moved = true;
                    this.parent.emit('moved', { viewport: this.parent, type: 'drag' });
                    return true;
                }
            }
            else {
                this.moved = false;
            }
        }
        return false;
    }
    up(event) {
        if (this.paused) {
            return false;
        }
        const touches = this.parent.input.touches;
        if (touches.length === 1) {
            const pointer = touches[0];
            if (pointer.last) {
                this.last = { x: pointer.last.x, y: pointer.last.y };
                this.current = pointer.id;
            }
            this.moved = false;
            return true;
        }
        else if (this.last) {
            if (this.moved) {
                const screen = new Point(this.last.x, this.last.y);
                this.parent.emit('drag-end', {
                    event, screen,
                    world: this.parent.toWorld(screen),
                    viewport: this.parent,
                });
                this.last = null;
                this.moved = false;
                return true;
            }
        }
        return false;
    }
    wheel(event) {
        if (this.paused) {
            return false;
        }
        if (this.options.wheel) {
            const wheel = this.parent.plugins.get('wheel', true);
            if (!wheel || (!wheel.options.wheelZoom && !event.ctrlKey)) {
                const step = event.deltaMode ? this.options.lineHeight : 1;
                const deltas = [event.deltaX, event.deltaY];
                const [deltaX, deltaY] = this.options.wheelSwapAxes ? deltas.reverse() : deltas;
                if (this.xDirection) {
                    this.parent.x += deltaX * step * this.options.wheelScroll * this.reverse;
                }
                if (this.yDirection) {
                    this.parent.y += deltaY * step * this.options.wheelScroll * this.reverse;
                }
                if (this.options.clampWheel) {
                    this.clamp();
                }
                this.parent.emit('wheel-scroll', this.parent);
                this.parent.emit('moved', { viewport: this.parent, type: 'wheel' });
                if (!this.parent.options.passiveWheel) {
                    event.preventDefault();
                }
                if (this.parent.options.stopPropagation) {
                    event.stopPropagation();
                }
                return true;
            }
        }
        return false;
    }
    resume() {
        this.last = null;
        this.paused = false;
    }
    clamp() {
        const decelerate = this.parent.plugins.get('decelerate', true) || {};
        if (this.options.clampWheel !== 'y') {
            if (this.parent.screenWorldWidth < this.parent.screenWidth) {
                switch (this.underflowX) {
                    case -1:
                        this.parent.x = 0;
                        break;
                    case 1:
                        this.parent.x = (this.parent.screenWidth - this.parent.screenWorldWidth);
                        break;
                    default:
                        this.parent.x = (this.parent.screenWidth - this.parent.screenWorldWidth) / 2;
                }
            }
            else if (this.parent.left < 0) {
                this.parent.x = 0;
                decelerate.x = 0;
            }
            else if (this.parent.right > this.parent.worldWidth) {
                this.parent.x = (-this.parent.worldWidth * this.parent.scale.x) + this.parent.screenWidth;
                decelerate.x = 0;
            }
        }
        if (this.options.clampWheel !== 'x') {
            if (this.parent.screenWorldHeight < this.parent.screenHeight) {
                switch (this.underflowY) {
                    case -1:
                        this.parent.y = 0;
                        break;
                    case 1:
                        this.parent.y = (this.parent.screenHeight - this.parent.screenWorldHeight);
                        break;
                    default:
                        this.parent.y = (this.parent.screenHeight - this.parent.screenWorldHeight) / 2;
                }
            }
            else {
                if (this.parent.top < 0) {
                    this.parent.y = 0;
                    decelerate.y = 0;
                }
                if (this.parent.bottom > this.parent.worldHeight) {
                    this.parent.y = (-this.parent.worldHeight * this.parent.scale.y) + this.parent.screenHeight;
                    decelerate.y = 0;
                }
            }
        }
    }
}