playable.js
Version:
A lightweight HTML5 game engine.
179 lines (160 loc) • 5.1 kB
text/typescript
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;
}
}
}