Source: js/plugins/Bounce.js

js/plugins/Bounce.js

import { Point } from '@pixi/math';
import { Plugin } from './Plugin';
import ease from '../ease';
const DEFAULT_BOUNCE_OPTIONS = {
    sides: 'all',
    friction: 0.5,
    time: 150,
    ease: 'easeInOutSine',
    underflow: 'center',
    bounceBox: null
};
/**
 * @fires bounce-start-x
 * @fires bounce.end-x
 * @fires bounce-start-y
 * @fires bounce-end-y
 * @public
 */
export class Bounce extends Plugin {
    /**
     * This is called by {@link Viewport.bounce}.
     */
    constructor(parent, options = {}) {
        super(parent);
        this.options = Object.assign({}, DEFAULT_BOUNCE_OPTIONS, options);
        this.ease = ease(this.options.ease, 'easeInOutSine');
        if (this.options.sides) {
            if (this.options.sides === 'all') {
                this.top = this.bottom = this.left = this.right = true;
            }
            else if (this.options.sides === 'horizontal') {
                this.right = this.left = true;
                this.top = this.bottom = false;
            }
            else if (this.options.sides === 'vertical') {
                this.left = this.right = false;
                this.top = this.bottom = true;
            }
            else {
                this.top = this.options.sides.indexOf('top') !== -1;
                this.bottom = this.options.sides.indexOf('bottom') !== -1;
                this.left = this.options.sides.indexOf('left') !== -1;
                this.right = this.options.sides.indexOf('right') !== -1;
            }
        }
        else {
            this.left = this.top = this.right = this.bottom = false;
        }
        const clamp = this.options.underflow.toLowerCase();
        if (clamp === 'center') {
            this.underflowX = 0;
            this.underflowY = 0;
        }
        else {
            // eslint-disable-next-line no-nested-ternary
            this.underflowX = (clamp.indexOf('left') !== -1) ? -1 : (clamp.indexOf('right') !== -1) ? 1 : 0;
            // eslint-disable-next-line no-nested-ternary
            this.underflowY = (clamp.indexOf('top') !== -1) ? -1 : (clamp.indexOf('bottom') !== -1) ? 1 : 0;
        }
        this.reset();
    }
    isActive() {
        return this.toX !== null || this.toY !== null;
    }
    down() {
        this.toX = this.toY = null;
        return false;
    }
    up() {
        this.bounce();
        return false;
    }
    update(elapsed) {
        if (this.paused) {
            return;
        }
        this.bounce();
        if (this.toX) {
            const toX = this.toX;
            toX.time += elapsed;
            this.parent.emit('moved', { viewport: this.parent, type: 'bounce-x' });
            if (toX.time >= this.options.time) {
                this.parent.x = toX.end;
                this.toX = null;
                this.parent.emit('bounce-x-end', this.parent);
            }
            else {
                this.parent.x = this.ease(toX.time, toX.start, toX.delta, this.options.time);
            }
        }
        if (this.toY) {
            const toY = this.toY;
            toY.time += elapsed;
            this.parent.emit('moved', { viewport: this.parent, type: 'bounce-y' });
            if (toY.time >= this.options.time) {
                this.parent.y = toY.end;
                this.toY = null;
                this.parent.emit('bounce-y-end', this.parent);
            }
            else {
                this.parent.y = this.ease(toY.time, toY.start, toY.delta, this.options.time);
            }
        }
    }
    /** @internal */
    calcUnderflowX() {
        let x;
        switch (this.underflowX) {
            case -1:
                x = 0;
                break;
            case 1:
                x = (this.parent.screenWidth - this.parent.screenWorldWidth);
                break;
            default:
                x = (this.parent.screenWidth - this.parent.screenWorldWidth) / 2;
        }
        return x;
    }
    /** @internal */
    calcUnderflowY() {
        let y;
        switch (this.underflowY) {
            case -1:
                y = 0;
                break;
            case 1:
                y = (this.parent.screenHeight - this.parent.screenWorldHeight);
                break;
            default:
                y = (this.parent.screenHeight - this.parent.screenWorldHeight) / 2;
        }
        return y;
    }
    oob() {
        const box = this.options.bounceBox;
        if (box) {
            const x1 = typeof box.x === 'undefined' ? 0 : box.x;
            const y1 = typeof box.y === 'undefined' ? 0 : box.y;
            const width = typeof box.width === 'undefined' ? this.parent.worldWidth : box.width;
            const height = typeof box.height === 'undefined' ? this.parent.worldHeight : box.height;
            return {
                left: this.parent.left < x1,
                right: this.parent.right > width,
                top: this.parent.top < y1,
                bottom: this.parent.bottom > height,
                topLeft: new Point(x1 * this.parent.scale.x, y1 * this.parent.scale.y),
                bottomRight: new Point((width * this.parent.scale.x) - this.parent.screenWidth, (height * this.parent.scale.y) - this.parent.screenHeight)
            };
        }
        return {
            left: this.parent.left < 0,
            right: this.parent.right > this.parent.worldWidth,
            top: this.parent.top < 0,
            bottom: this.parent.bottom > this.parent.worldHeight,
            topLeft: new Point(0, 0),
            bottomRight: new Point((this.parent.worldWidth * this.parent.scale.x) - this.parent.screenWidth, (this.parent.worldHeight * this.parent.scale.y) - this.parent.screenHeight)
        };
    }
    bounce() {
        var _a, _b;
        if (this.paused) {
            return;
        }
        let oob;
        let decelerate = this.parent.plugins.get('decelerate', true);
        if (decelerate && (decelerate.x || decelerate.y)) {
            if ((decelerate.x && decelerate.percentChangeX === ((_a = decelerate.options) === null || _a === void 0 ? void 0 : _a.friction))
                || (decelerate.y && decelerate.percentChangeY === ((_b = decelerate.options) === null || _b === void 0 ? void 0 : _b.friction))) {
                oob = this.oob();
                if ((oob.left && this.left) || (oob.right && this.right)) {
                    decelerate.percentChangeX = this.options.friction;
                }
                if ((oob.top && this.top) || (oob.bottom && this.bottom)) {
                    decelerate.percentChangeY = this.options.friction;
                }
            }
        }
        const drag = this.parent.plugins.get('drag', true) || {};
        const pinch = this.parent.plugins.get('pinch', true) || {};
        decelerate = decelerate || {};
        if (!(drag === null || drag === void 0 ? void 0 : drag.active) && !(pinch === null || pinch === void 0 ? void 0 : pinch.active) && ((!this.toX || !this.toY) && (!decelerate.x || !decelerate.y))) {
            oob = oob || this.oob();
            const topLeft = oob.topLeft;
            const bottomRight = oob.bottomRight;
            if (!this.toX && !decelerate.x) {
                let x = null;
                if (oob.left && this.left) {
                    x = (this.parent.screenWorldWidth < this.parent.screenWidth) ? this.calcUnderflowX() : -topLeft.x;
                }
                else if (oob.right && this.right) {
                    x = (this.parent.screenWorldWidth < this.parent.screenWidth) ? this.calcUnderflowX() : -bottomRight.x;
                }
                if (x !== null && this.parent.x !== x) {
                    this.toX = { time: 0, start: this.parent.x, delta: x - this.parent.x, end: x };
                    this.parent.emit('bounce-x-start', this.parent);
                }
            }
            if (!this.toY && !decelerate.y) {
                let y = null;
                if (oob.top && this.top) {
                    y = (this.parent.screenWorldHeight < this.parent.screenHeight) ? this.calcUnderflowY() : -topLeft.y;
                }
                else if (oob.bottom && this.bottom) {
                    y = (this.parent.screenWorldHeight < this.parent.screenHeight) ? this.calcUnderflowY() : -bottomRight.y;
                }
                if (y !== null && this.parent.y !== y) {
                    this.toY = { time: 0, start: this.parent.y, delta: y - this.parent.y, end: y };
                    this.parent.emit('bounce-y-start', this.parent);
                }
            }
        }
    }
    reset() {
        this.toX = this.toY = null;
        this.bounce();
    }
}