248 строки
8.5 KiB
TypeScript
248 строки
8.5 KiB
TypeScript
///<reference path='refs.ts'/>
|
|
|
|
module TDev {
|
|
export class DragToScrollHandler
|
|
{
|
|
private posX = 0;
|
|
private posY = 0;
|
|
private theHistory = [];
|
|
public vertical = true;
|
|
public horizontal = false;
|
|
private seenTouchEvent = false;
|
|
private captureingMouse = false;
|
|
private inertialAnimation : Animation;
|
|
|
|
constructor(public helt:HTMLElement) {
|
|
}
|
|
public unhook()
|
|
{
|
|
var t = <any>this;
|
|
if (Browser.touchStart) {
|
|
this.helt.removeEventListener("touchstart", t, false);
|
|
this.helt.removeEventListener("touchmove", t, false);
|
|
this.helt.removeEventListener("touchend", t, false);
|
|
} else {
|
|
this.helt.removeEventListener("mousedown", t, false);
|
|
this.helt.removeEventListener("mouseup", t, false);
|
|
this.helt.removeEventListener("mouseout", t, false);
|
|
this.helt.removeEventListener("mouseleave", t, false);
|
|
this.helt.removeEventListener("mousemove", t, false);
|
|
}
|
|
}
|
|
|
|
public init()
|
|
{
|
|
(<any>this.helt).dragToScroll = this;
|
|
var t = <any>this;
|
|
if (Browser.touchStart) {
|
|
this.helt.addEventListener("touchstart", t, false);
|
|
this.helt.addEventListener("touchmove", t, false);
|
|
this.helt.addEventListener("touchend", t, false);
|
|
} else {
|
|
this.helt.addEventListener("mousedown", t, false);
|
|
this.helt.addEventListener("mouseup", t, false);
|
|
this.helt.addEventListener("mouseout", t, false);
|
|
this.helt.addEventListener("mouseleave", t, false);
|
|
this.helt.addEventListener("mousemove", t, false);
|
|
}
|
|
}
|
|
|
|
private record(pos:any)
|
|
{
|
|
//var n = pos.timestamp;
|
|
//if (!n)
|
|
var n = new Date().getTime();
|
|
// TODO keep the history small?
|
|
this.theHistory.push(
|
|
{ x: pos.pageX,
|
|
y: pos.pageY,
|
|
t: n });
|
|
}
|
|
|
|
private stopInertialAnimation()
|
|
{
|
|
if (!this.inertialAnimation) return;
|
|
this.inertialAnimation.complete();
|
|
this.inertialAnimation = null;
|
|
}
|
|
|
|
|
|
private begin(pos:any)
|
|
{
|
|
this.posX = this.helt.scrollLeft + pos.pageX;
|
|
this.posY = this.helt.scrollTop + pos.pageY;
|
|
this.stopInertialAnimation();
|
|
this.theHistory = [];
|
|
this.record(pos);
|
|
}
|
|
|
|
private scrollTo(x:number, y:number)
|
|
{
|
|
if (this.horizontal) {
|
|
this.helt.scrollLeft = x;
|
|
}
|
|
|
|
if (this.vertical) {
|
|
this.helt.scrollTop = y;
|
|
}
|
|
}
|
|
|
|
private move(pos:any)
|
|
{
|
|
this.scrollTo(this.posX - pos.pageX, this.posY - pos.pageY);
|
|
this.record(pos);
|
|
}
|
|
|
|
private log()
|
|
{
|
|
var n = new Date().getTime();
|
|
var k = this.theHistory.slice(this.theHistory.length - 5).map((h) => h.x + "," + h.y + "," + (h.t - n)).join(" : ");
|
|
Util.log(k);
|
|
}
|
|
|
|
private end(pos:any)
|
|
{
|
|
if (!!pos) this.record(pos);
|
|
if (this.theHistory.length == 0) return;
|
|
var last = this.theHistory[this.theHistory.length - 1];
|
|
var beg = last.t - 200;
|
|
var first = last;
|
|
for (var i = 0; i < this.theHistory.length; ++i) {
|
|
if (this.theHistory[i].t >= beg) {
|
|
first = this.theHistory[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
var dt = last.t - first.t;
|
|
if (dt > 0) {
|
|
var m = 150;
|
|
var dx = (last.x - first.x) / dt * m;
|
|
var dy = (last.y - first.y) / dt * m;
|
|
var speed = Math.abs(dx) + Math.abs(dy);
|
|
if (speed < 50) return;
|
|
|
|
this.inertialAnimation =
|
|
new Animation((p:number) => {
|
|
this.scrollTo(this.posX - (last.x + dx * p), this.posY - (last.y + dy * p));
|
|
});
|
|
this.inertialAnimation.duration = 700;
|
|
this.inertialAnimation.begin();
|
|
}
|
|
}
|
|
|
|
public handleEvent(e:Event)
|
|
{
|
|
try {
|
|
var now = new Date().getTime();
|
|
switch (e.type) {
|
|
case "mousedown":
|
|
if (!this.seenTouchEvent && (<MouseEvent> e).button == 0) {
|
|
this.captureingMouse = true;
|
|
this.begin(e);
|
|
}
|
|
break;
|
|
case "mousemove":
|
|
if (this.captureingMouse) {
|
|
this.move(e);
|
|
//e.preventDefault();
|
|
}
|
|
break;
|
|
case "mouseleave":
|
|
this.end(e);
|
|
this.captureingMouse = false;
|
|
break;
|
|
case "mouseout":
|
|
// Chrome and FF don't do mouseleave
|
|
// instead they have this silly bubbling mouseout
|
|
if (this.captureingMouse) {
|
|
var me = <MouseEvent> e;
|
|
if (!!me.relatedTarget) {
|
|
var elts = this.helt.getElementsByTagName((<HTMLElement> me.relatedTarget).nodeName);
|
|
for (var i = 0; i < elts.length; ++i)
|
|
if (elts[i] == me.relatedTarget)
|
|
return;
|
|
}
|
|
this.end(e);
|
|
this.captureingMouse = false;
|
|
}
|
|
break;
|
|
case "mouseup":
|
|
this.end(e);
|
|
this.captureingMouse = false;
|
|
break;
|
|
|
|
case "touchstart":
|
|
this.seenTouchEvent = true;
|
|
this.begin((<any> e).touches[0]);
|
|
break;
|
|
case "touchmove":
|
|
e.preventDefault(); // otherwise the entire page will scroll
|
|
this.move((<any> e).touches[0]);
|
|
break;
|
|
case "touchend":
|
|
this.end((<any> e).touches[0]);
|
|
break;
|
|
}
|
|
} catch (err) {
|
|
Util.reportError("dragToScroll", err);
|
|
}
|
|
}
|
|
}
|
|
|
|
export module Util {
|
|
export function setupDragToScroll(e:HTMLElement)
|
|
{
|
|
(<any>e).scrollEnabled = true;
|
|
if (Browser.isMobileSafari) {
|
|
e.style.boxSizing = "border-box";
|
|
e.style.overflowY = "scroll";
|
|
e.className += " iOSScroll";
|
|
} else if (Browser.builtinTouchToPan) {
|
|
e.style.boxSizing = "border-box";
|
|
e.style.overflowY = "auto";
|
|
} else {
|
|
e.style.overflowY = "hidden";
|
|
var d = new DragToScrollHandler(e);
|
|
d.init();
|
|
}
|
|
}
|
|
|
|
export function resetDragToScroll(e:HTMLElement)
|
|
{
|
|
(<any>e).scrollEnabled = false;
|
|
if (Browser.builtinTouchToPan || Browser.isMobileSafari) {
|
|
e.style.boxSizing = "";
|
|
e.style.overflowX = "";
|
|
e.style.overflowY = "";
|
|
if (Browser.isMobileSafari)
|
|
e.className = e.className.replace(/iOSScroll/g, "");
|
|
} else {
|
|
var d:DragToScrollHandler = (<any>e).dragToScroll;
|
|
if (d) d.unhook();
|
|
}
|
|
}
|
|
|
|
export function setupHDragToScroll(e:HTMLElement)
|
|
{
|
|
(<any>e).scrollEnabled = true;
|
|
if (Browser.isMobileSafari) {
|
|
e.style.boxSizing = "border-box";
|
|
e.style.overflowY = "auto";
|
|
e.className += " iOSScroll";
|
|
} else if (Browser.builtinTouchToPan) {
|
|
e.style.boxSizing = "border-box";
|
|
e.style.overflowX = "auto";
|
|
if (Browser.isTrident)
|
|
e.style.msScrollTranslation = 'vertical-to-horizontal';
|
|
} else {
|
|
e.style.overflowX = "hidden";
|
|
var d = new DragToScrollHandler(e);
|
|
d.horizontal = true;
|
|
d.vertical = false;
|
|
d.init();
|
|
}
|
|
}
|
|
}
|
|
}
|