UNPKG

playable.js

Version:

A lightweight HTML5 game engine.

179 lines (160 loc) 5.1 kB
import {Layer} from './Layer'; import {Ease} from '../tween/Ease'; import {Tween} from '../tween/Tween'; import {Matrix} from '../geom/Matrix'; import {TouchEvent} from '../event/TouchEvent'; export class Scroller extends Layer { protected static scrollingView: Scroller; protected $scrollTop: number = 0; protected $scrollLeft: number = 0; protected $scrollWidth: number = 0; protected $scrollHeight: number = 0; protected $touchingX: number = null; protected $touchingY: number = null; protected $touchingId: number = null; protected $touchingTime: number = null; protected $velocitiesX: Array<number> = []; protected $velocitiesY: Array<number> = []; protected $inertiaTween: Tween = null; public constructor() { super(); this.width = 200; this.height = 200; this.on(TouchEvent.TOUCH_START, this.$onTouchStart); this.on(TouchEvent.TOUCH_MOVE, this.$onTouchMove); this.on(TouchEvent.TOUCH_END, this.$onTouchEnd); this.on(TouchEvent.TOUCH_CANCEL, this.$onTouchCancel); } public get scrollTop(): number { return this.$scrollTop; } public set scrollTop(scrollTop: number) { let bounds = this.$getContentBounds(); let maxScrollTop = this.$scrollHeight - this.$height; scrollTop = Math.max(0, Math.min(scrollTop, maxScrollTop)); if (scrollTop !== this.$scrollTop) { this.$scrollTop = scrollTop; this.$markDirty(); } bounds.release(); } public get scrollLeft(): number { return this.$scrollLeft; } public set scrollLeft(scrollLeft: number) { let bounds = this.$getContentBounds(); let maxScrollLeft = this.$scrollWidth - this.width; scrollLeft = Math.max(0, Math.min(scrollLeft, maxScrollLeft)); if (scrollLeft !== this.$scrollLeft) { this.$scrollLeft = scrollLeft; this.$markDirty(); } bounds.release(); } public get scrollWidth(): number { return this.$scrollWidth; } public get scrollHeight(): number { return this.$scrollHeight; } protected $getChildTransform(child: Layer): Matrix { let matrix = super.$getChildTransform(child); matrix.translate(-this.$scrollLeft, -this.$scrollTop); return matrix; } protected $resizeCanvas(): void { super.$resizeCanvas(); let bounds = this.$getContentBounds(); this.$scrollWidth = this.$scrollLeft + bounds.right + this.$anchorX; this.$scrollHeight = this.$scrollTop + bounds.bottom + this.$anchorY; this.scrollTop = this.$scrollTop; this.scrollLeft = this.$scrollLeft; } protected $onTouchStart(e: TouchEvent): void { this.$touchingX = e.localX; this.$touchingY = e.localY; this.$velocitiesX.length = 0; this.$velocitiesY.length = 0; this.$touchingTime = Date.now(); this.$touchingId = e.identifier; if (this.$inertiaTween) { this.$inertiaTween.pause(); this.$inertiaTween = null; } } protected $onTouchMove(e: TouchEvent): void { if (e.identifier !== this.$touchingId) { return; } let now = Date.now(); let scrollTop = this.scrollTop; let scrollLeft = this.scrollLeft; let dt = now - this.$touchingTime; let velocitiesX = this.$velocitiesX; let velocitiesY = this.$velocitiesY; let offsetX = e.localX - this.$touchingX; let offsetY = e.localY - this.$touchingY; let scrollingView = Scroller.scrollingView || this; velocitiesX.push(offsetX / dt); velocitiesY.push(offsetY / dt); if (velocitiesX.length > 5) { velocitiesX.shift(); velocitiesY.shift(); } this.$touchingX = e.localX; this.$touchingY = e.localY; if (scrollingView === this) { this.$touchingTime = now; this.scrollTop -= offsetY; this.scrollLeft -= offsetX; if (this.$scrollLeft !== scrollLeft || this.$scrollTop !== scrollTop) { Scroller.scrollingView = this; } } } protected $onTouchEnd(e: TouchEvent): void { if (e.identifier !== this.$touchingId) { return; } if (Scroller.scrollingView === this) { Scroller.scrollingView = null; } else { return; } let sumVelocityX = 0; let sumVelocityY = 0; let scrollTop = this.$scrollTop; let scrollLeft = this.$scrollLeft; let velocitiesX = this.$velocitiesX; let velocitiesY = this.$velocitiesY; let numVelocities = velocitiesX.length; for (let i = 0; i < numVelocities; ++i) { sumVelocityX += velocitiesX[i]; sumVelocityY += velocitiesY[i]; } let velocityX = sumVelocityX / numVelocities; let velocityY = sumVelocityY / numVelocities; let absVelocityX = Math.abs(velocityX); let absVelocityY = Math.abs(velocityY); if (absVelocityX > 0.01 || absVelocityY > 0.01) { let duration = Math.max(absVelocityX, absVelocityY, 1) * 1000; this.$inertiaTween = Tween.get(this).to({ scrollTop: scrollTop - velocityY * (absVelocityY + 1) * 200, scrollLeft: scrollLeft - velocityX * (absVelocityX + 1) * 200 }, duration, Ease.easeOutQuart).play(); } this.$touchingId = null; } protected $onTouchCancel(e: TouchEvent): void { if (e.identifier === this.$touchingId) { this.$touchingId = null; } } protected $emitRemovedFromStage(): void { super.$emitRemovedFromStage(); if (this.$inertiaTween) { this.$inertiaTween.pause(); this.$inertiaTween = null; } } }