diff --git a/src/cartographer.js b/src/cartographer.js index 2342dcb..ab00808 100644 --- a/src/cartographer.js +++ b/src/cartographer.js @@ -18,6 +18,8 @@ export default class Cartographer { 'move', '_checkMove', + 'pinch', + 'zoom', ].map(method => this[method] = this[method].bind(this)); @@ -120,10 +122,33 @@ export default class Cartographer { } } - zoom(event) { - let scaleOrig = this.scale; + pinch(event) { + 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 if (scaleTemp < this.scaleMin) @@ -131,7 +156,7 @@ export default class Cartographer { else if (scaleTemp > this.scaleMax) scaleTemp = this.scaleMax; - if (scaleOrig != scaleTemp) { + if (scaleOrig !== scaleTemp) { this.scale = scaleTemp; diff --git a/src/main.js b/src/main.js index 51fcacf..1b1ee29 100644 --- a/src/main.js +++ b/src/main.js @@ -68,7 +68,7 @@ class Demo { 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(tap.tile.getPoint()); diff --git a/src/onTap.js b/src/onTap.js index db86e3e..1d4c58c 100644 --- a/src/onTap.js +++ b/src/onTap.js @@ -7,8 +7,6 @@ export default class OnTap { this.debug = false; this.state = { - clickStartX: null, - clickStartY: null, clickStartTime: null }; @@ -24,29 +22,41 @@ export default class OnTap { threshold: settings.doubletapThreshold || 500, // less than in milliseconds callback: settings.doubletap || noop }, + pinch: { + callback: settings.pinch || noop + }, press: { threshold: settings.pressThreshold || 1000, // greater than or equal to in milliseconds callback: settings.press || noop }, zoom: { 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.element.addEventListener(method, this[method]); }); } mousedown(event) { - if (this.debug) console.dir('onTap.mousedown', event); + if (this.debug) console.debug('onTap.mousedown', event); if (!this.state.clickStartTime) { - this.state.clickStartX = event.pageX; - this.state.clickStartY = event.pageY; + this.state.lastX = event.offsetX; + this.state.lastY = event.offsetY; this.state.clickStartTime = event.timeStamp; } } @@ -54,17 +64,26 @@ export default class OnTap { touchstart(event) { 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) { - this.state.clickStartX = event.pageX; - this.state.clickStartY = event.pageY; this.state.clickStartTime = event.timeStamp; } } mouseup(event) { - if (this.debug) console.dir('onTap.mouseup', event); + if (this.debug) console.debug('onTap.mouseup', event); if (!this.state.moving) { this.actions.tap.callback(event); @@ -73,51 +92,63 @@ export default class OnTap { this.state.moving = null; this.state.lastX = null; this.state.lastY = null; - this.state.clickStartX = null; - this.state.clickStartY = null; this.state.clickStartTime = null; } touchend(event) { event.preventDefault(); - if (this.debug) console.dir('onTap.touchend', event); + if (this.debug) console.debug('onTap.touchend', event); - if (!this.state.moving) { - event.offsetX = event.offsetX || event.pageX - event.target.offsetLeft; - event.offsetY = event.offsetY || event.pageY - event.target.offsetTop; + const touches = [...event.touches]; + 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.state.moving = null; - this.state.lastX = null; - this.state.lastY = null; - this.state.clickStartX = null; - this.state.clickStartY = null; - this.state.clickStartTime = null; + if (event.touches.length <= 1) { + this.state.lastPinch = null; + } + + if (event.touches.length === 0) { + this.state.pinching = false; + this.state.moving = null; + this.state.lastX = null; + this.state.lastY = null; + this.state.clickStartTime = null; + } } 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.moving) { - if ((Math.abs(event.pageX - this.state.clickStartX) > this.actions.move.threshold) - || (Math.abs(event.pageY - this.state.clickStartY) > this.actions.move.threshold)) { + if ((Math.abs(event.offsetX - this.state.lastX) > this.actions.move.threshold) + || (Math.abs(event.offsetY - this.state.lastY) > this.actions.move.threshold)) { this.state.moving = true; - this.state.lastX = this.state.clickStartX; - this.state.lastY = this.state.clickStartY; } } if (this.state.moving) { - event.deltaX = event.pageX - this.state.lastX, - event.deltaY = event.pageY - this.state.lastY + event.deltaX = event.offsetX - this.state.lastX, + event.deltaY = event.offsetY - this.state.lastY this.actions.move.callback(event); - this.state.lastX = event.pageX; - this.state.lastY = event.pageY; + this.state.lastX = event.offsetX; + this.state.lastY = event.offsetY; } } } @@ -125,25 +156,36 @@ export default class OnTap { touchmove(event) { event.preventDefault(); - if (this.debug) console.dir('onTap.touchmove', event); + if (this.debug) console.debug('onTap.touchmove', event); 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 ((Math.abs(event.pageX - this.state.clickStartX) > this.actions.move.threshold) - || (Math.abs(event.pageY - this.state.clickStartY) > this.actions.move.threshold)) { + if ((Math.abs(event.offsetX - this.state.lastX) > this.actions.move.threshold) + || (Math.abs(event.offsetY - this.state.lastY) > this.actions.move.threshold)) { this.state.moving = true; - this.state.lastX = this.state.clickStartX; - this.state.lastY = this.state.clickStartY; } } if (this.state.moving) { - event.deltaX = event.pageX - this.state.lastX, - event.deltaY = event.pageY - this.state.lastY + event.deltaX = event.offsetX - this.state.lastX, + event.deltaY = event.offsetY - this.state.lastY + this.actions.move.callback(event); - this.state.lastX = event.pageX; - this.state.lastY = event.pageY; + this.state.lastX = event.offsetX; + this.state.lastY = event.offsetY; } } } @@ -153,7 +195,7 @@ export default class OnTap { } wheel(event) { - if (this.debug) console.dir('onTap.wheel', event); + if (this.debug) console.debug('onTap.wheel', event); this.actions.zoom.callback(event); } diff --git a/src/tessellate.js b/src/tessellate.js index db4db53..4cd2438 100644 --- a/src/tessellate.js +++ b/src/tessellate.js @@ -73,6 +73,7 @@ export class Tessellate { 'checkSettings', 'tap', 'move', + 'pinch', 'zoom', 'pixelToTile', 'tileToPixel', @@ -90,8 +91,11 @@ export class Tessellate { this.onTap = new OnTap({ element: this.settings.element, tap: this.tap, + doubletap: this.doubletap, + hold: this.hold, move: this.move, - zoom: this.zoom + pinch: this.pinch, + zoom: this.zoom, }); const cartographer = selectCartographer(this.settings.board, this.settings.orientation); @@ -131,10 +135,42 @@ export class Tessellate { 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) { this.cartographer.move(event); } + pinch(event) { + this.cartographer.pinch(event); + } + zoom(event) { this.cartographer.zoom(event); }