Pinch support and general improvements for mobile touch interface (#9)

This commit is contained in:
gavin
2018-08-01 01:06:02 +00:00
committed by Gitea
parent 5cb4308251
commit 584e917fc6
4 changed files with 152 additions and 49 deletions

View File

@@ -18,6 +18,8 @@ export default class Cartographer {
'move', 'move',
'_checkMove', '_checkMove',
'pinch',
'zoom', 'zoom',
].map(method => this[method] = this[method].bind(this)); ].map(method => this[method] = this[method].bind(this));
@@ -120,10 +122,33 @@ export default class Cartographer {
} }
} }
zoom(event) { pinch(event) {
let scaleOrig = this.scale; const scaleOrig = this.scale;
let scaleTemp = parseInt((scaleOrig + (event.deltaY / -10)) * 1000) / 1000; let scaleTemp = scaleOrig * event.scaleStep;
scaleTemp = Math.max(scaleTemp, this.scaleMin);
scaleTemp = Math.min(scaleTemp, this.scaleMax);
if (scaleTemp !== scaleOrig) {
this.scale = scaleTemp;
// zoom to the current mouse location
this.move({
deltaX: (((event.offsetX - this.originX) / scaleOrig) * (scaleOrig - scaleTemp)),
deltaY: (((event.offsetY - this.originY) / scaleOrig) * (scaleOrig - scaleTemp)),
target: {
offsetWidth: event.target.offsetWidth,
offsetHeight: event.target.offsetHeight,
},
});
}
}
zoom(event) {
const scaleOrig = this.scale;
let scaleTemp = scaleOrig + (event.deltaY / -10);
// make sure 'scale' doesn't get too small nor too big // make sure 'scale' doesn't get too small nor too big
if (scaleTemp < this.scaleMin) if (scaleTemp < this.scaleMin)
@@ -131,7 +156,7 @@ export default class Cartographer {
else if (scaleTemp > this.scaleMax) else if (scaleTemp > this.scaleMax)
scaleTemp = this.scaleMax; scaleTemp = this.scaleMax;
if (scaleOrig != scaleTemp) { if (scaleOrig !== scaleTemp) {
this.scale = scaleTemp; this.scale = scaleTemp;

View File

@@ -68,7 +68,7 @@ class Demo {
const key = `${ x },${ z != null ? z : y }`; const key = `${ x },${ z != null ? z : y }`;
this.map[key].pips = Tessellate.utils.random(1,9); this.map[key].pips = Tessellate.utils.random(1,7);
console.log(this.map[key].pips); console.log(this.map[key].pips);
// console.log(tap.tile.getPoint()); // console.log(tap.tile.getPoint());

View File

@@ -7,8 +7,6 @@ export default class OnTap {
this.debug = false; this.debug = false;
this.state = { this.state = {
clickStartX: null,
clickStartY: null,
clickStartTime: null clickStartTime: null
}; };
@@ -24,29 +22,41 @@ export default class OnTap {
threshold: settings.doubletapThreshold || 500, // less than in milliseconds threshold: settings.doubletapThreshold || 500, // less than in milliseconds
callback: settings.doubletap || noop callback: settings.doubletap || noop
}, },
pinch: {
callback: settings.pinch || noop
},
press: { press: {
threshold: settings.pressThreshold || 1000, // greater than or equal to in milliseconds threshold: settings.pressThreshold || 1000, // greater than or equal to in milliseconds
callback: settings.press || noop callback: settings.press || noop
}, },
zoom: { zoom: {
callback: settings.zoom || noop callback: settings.zoom || noop
} },
}; };
this.events = ['mousedown', 'mouseup', 'mousemove', 'touchstart', 'touchend', 'touchmove', 'touchcancel', 'wheel']; [
'mousedown',
'mouseup',
'mousemove',
this.events.map(method => { 'touchstart',
'touchend',
'touchmove',
'touchcancel',
'wheel',
].map(method => {
this[method] = this[method].bind(this); this[method] = this[method].bind(this);
this.element.addEventListener(method, this[method]); this.element.addEventListener(method, this[method]);
}); });
} }
mousedown(event) { mousedown(event) {
if (this.debug) console.dir('onTap.mousedown', event); if (this.debug) console.debug('onTap.mousedown', event);
if (!this.state.clickStartTime) { if (!this.state.clickStartTime) {
this.state.clickStartX = event.pageX; this.state.lastX = event.offsetX;
this.state.clickStartY = event.pageY; this.state.lastY = event.offsetY;
this.state.clickStartTime = event.timeStamp; this.state.clickStartTime = event.timeStamp;
} }
} }
@@ -54,17 +64,26 @@ export default class OnTap {
touchstart(event) { touchstart(event) {
event.preventDefault(); event.preventDefault();
if (this.debug) console.dir('onTap.touchstart', event); if (this.debug) console.debug('onTap.touchstart', event);
const touches = [...event.touches];
event.offsetX = touches.reduce((memo, touch) => memo + touch.pageX, 0) / touches.length;
event.offsetY = touches.reduce((memo, touch) => memo + touch.pageY, 0) / touches.length;
this.state.lastX = event.offsetX;
this.state.lastY = event.offsetY;
if (event.touches.length > 1) {
this.state.pinching = true;
}
if (!this.state.clickStartTime) { if (!this.state.clickStartTime) {
this.state.clickStartX = event.pageX;
this.state.clickStartY = event.pageY;
this.state.clickStartTime = event.timeStamp; this.state.clickStartTime = event.timeStamp;
} }
} }
mouseup(event) { mouseup(event) {
if (this.debug) console.dir('onTap.mouseup', event); if (this.debug) console.debug('onTap.mouseup', event);
if (!this.state.moving) { if (!this.state.moving) {
this.actions.tap.callback(event); this.actions.tap.callback(event);
@@ -73,51 +92,63 @@ export default class OnTap {
this.state.moving = null; this.state.moving = null;
this.state.lastX = null; this.state.lastX = null;
this.state.lastY = null; this.state.lastY = null;
this.state.clickStartX = null;
this.state.clickStartY = null;
this.state.clickStartTime = null; this.state.clickStartTime = null;
} }
touchend(event) { touchend(event) {
event.preventDefault(); event.preventDefault();
if (this.debug) console.dir('onTap.touchend', event); if (this.debug) console.debug('onTap.touchend', event);
if (!this.state.moving) { const touches = [...event.touches];
event.offsetX = event.offsetX || event.pageX - event.target.offsetLeft;
event.offsetY = event.offsetY || event.pageY - event.target.offsetTop;
if (touches.length) {
event.offsetX = touches.reduce((memo, touch) => memo + touch.pageX, 0) / touches.length;
event.offsetY = touches.reduce((memo, touch) => memo + touch.pageY, 0) / touches.length;
this.state.lastX = event.offsetX;
this.state.lastY = event.offsetY;
}
else {
event.offsetX = this.state.lastX;
event.offsetY = this.state.lastY;
}
if (!(this.state.moving || this.state.pinching)) {
this.actions.tap.callback(event); this.actions.tap.callback(event);
} }
if (event.touches.length <= 1) {
this.state.lastPinch = null;
}
if (event.touches.length === 0) {
this.state.pinching = false;
this.state.moving = null; this.state.moving = null;
this.state.lastX = null; this.state.lastX = null;
this.state.lastY = null; this.state.lastY = null;
this.state.clickStartX = null;
this.state.clickStartY = null;
this.state.clickStartTime = null; this.state.clickStartTime = null;
} }
}
mousemove(event) { mousemove(event) {
if (this.debug) console.dir('onTap.mousemove', event); if (this.debug) console.debug('onTap.mousemove', event);
if (this.state.clickStartTime) { if (this.state.clickStartTime) {
if (!this.state.moving) { if (!this.state.moving) {
if ((Math.abs(event.pageX - this.state.clickStartX) > this.actions.move.threshold) if ((Math.abs(event.offsetX - this.state.lastX) > this.actions.move.threshold)
|| (Math.abs(event.pageY - this.state.clickStartY) > this.actions.move.threshold)) { || (Math.abs(event.offsetY - this.state.lastY) > this.actions.move.threshold)) {
this.state.moving = true; this.state.moving = true;
this.state.lastX = this.state.clickStartX;
this.state.lastY = this.state.clickStartY;
} }
} }
if (this.state.moving) { if (this.state.moving) {
event.deltaX = event.pageX - this.state.lastX, event.deltaX = event.offsetX - this.state.lastX,
event.deltaY = event.pageY - this.state.lastY event.deltaY = event.offsetY - this.state.lastY
this.actions.move.callback(event); this.actions.move.callback(event);
this.state.lastX = event.pageX; this.state.lastX = event.offsetX;
this.state.lastY = event.pageY; this.state.lastY = event.offsetY;
} }
} }
} }
@@ -125,25 +156,36 @@ export default class OnTap {
touchmove(event) { touchmove(event) {
event.preventDefault(); event.preventDefault();
if (this.debug) console.dir('onTap.touchmove', event); if (this.debug) console.debug('onTap.touchmove', event);
if (this.state.clickStartTime) { if (this.state.clickStartTime) {
const touches = [...event.touches];
event.offsetX = touches.reduce((memo, touch) => memo + touch.pageX, 0) / touches.length;
event.offsetY = touches.reduce((memo, touch) => memo + touch.pageY, 0) / touches.length;
if (this.state.pinching) {
event.scaleStep = event.scale / (this.state.lastPinch || 1);
this.actions.pinch.callback(event);
this.state.lastPinch = event.scale;
}
if (!this.state.moving) { if (!this.state.moving) {
if ((Math.abs(event.pageX - this.state.clickStartX) > this.actions.move.threshold) if ((Math.abs(event.offsetX - this.state.lastX) > this.actions.move.threshold)
|| (Math.abs(event.pageY - this.state.clickStartY) > this.actions.move.threshold)) { || (Math.abs(event.offsetY - this.state.lastY) > this.actions.move.threshold)) {
this.state.moving = true; this.state.moving = true;
this.state.lastX = this.state.clickStartX;
this.state.lastY = this.state.clickStartY;
} }
} }
if (this.state.moving) { if (this.state.moving) {
event.deltaX = event.pageX - this.state.lastX, event.deltaX = event.offsetX - this.state.lastX,
event.deltaY = event.pageY - this.state.lastY event.deltaY = event.offsetY - this.state.lastY
this.actions.move.callback(event); this.actions.move.callback(event);
this.state.lastX = event.pageX; this.state.lastX = event.offsetX;
this.state.lastY = event.pageY; this.state.lastY = event.offsetY;
} }
} }
} }
@@ -153,7 +195,7 @@ export default class OnTap {
} }
wheel(event) { wheel(event) {
if (this.debug) console.dir('onTap.wheel', event); if (this.debug) console.debug('onTap.wheel', event);
this.actions.zoom.callback(event); this.actions.zoom.callback(event);
} }

View File

@@ -73,6 +73,7 @@ export class Tessellate {
'checkSettings', 'checkSettings',
'tap', 'tap',
'move', 'move',
'pinch',
'zoom', 'zoom',
'pixelToTile', 'pixelToTile',
'tileToPixel', 'tileToPixel',
@@ -90,8 +91,11 @@ export class Tessellate {
this.onTap = new OnTap({ this.onTap = new OnTap({
element: this.settings.element, element: this.settings.element,
tap: this.tap, tap: this.tap,
doubletap: this.doubletap,
hold: this.hold,
move: this.move, move: this.move,
zoom: this.zoom pinch: this.pinch,
zoom: this.zoom,
}); });
const cartographer = selectCartographer(this.settings.board, this.settings.orientation); const cartographer = selectCartographer(this.settings.board, this.settings.orientation);
@@ -131,10 +135,42 @@ export class Tessellate {
this.settings.tap(tap); this.settings.tap(tap);
} }
doubletap(event) {
let point = new Point(event.offsetX, event.offsetY);
let tile = this.cartographer.pixelToTile(point);
let tap = {
event,
point,
tile
};
console.log('DOUBLETAP');
console.log(tap);
}
hold(event) {
let point = new Point(event.offsetX, event.offsetY);
let tile = this.cartographer.pixelToTile(point);
let tap = {
event,
point,
tile
};
console.log('HOLD');
console.log(tap);
}
move(event) { move(event) {
this.cartographer.move(event); this.cartographer.move(event);
} }
pinch(event) {
this.cartographer.pinch(event);
}
zoom(event) { zoom(event) {
this.cartographer.zoom(event); this.cartographer.zoom(event);
} }