A Pressing Matter (#11)
This commit is contained in:
115
src/main.js
115
src/main.js
@@ -1,3 +1,6 @@
|
||||
const ONE_SECOND = 1000;
|
||||
const PRESS_RIPPLE = ONE_SECOND / 3;
|
||||
|
||||
const DEFAULTS = {
|
||||
board: Tessellate.BOARD_STYLES.HEX,
|
||||
style: Tessellate.DRAW_STYLES.FILL,
|
||||
@@ -5,11 +8,26 @@ const DEFAULTS = {
|
||||
tile: Tessellate.TILE_STYLES.HEX,
|
||||
};
|
||||
|
||||
const pressRipple = function() {
|
||||
const sinStart = 2 * Math.PI;
|
||||
const halfPi = Math.PI / 2;
|
||||
|
||||
return pressFactor => Math.sin(sinStart + (pressFactor * halfPi)) + 1;
|
||||
}();
|
||||
|
||||
const pressFade = function() {
|
||||
const halfPi = Math.PI / 2;
|
||||
|
||||
return pressFactor => Math.sin(Math.PI + (pressFactor * halfPi)) + 1;
|
||||
}();
|
||||
|
||||
class Demo {
|
||||
constructor() {
|
||||
[
|
||||
'setOriginTile',
|
||||
'onTap',
|
||||
'tap',
|
||||
'pressStart',
|
||||
'press',
|
||||
'createTile',
|
||||
'drawTile',
|
||||
'draw',
|
||||
@@ -21,11 +39,14 @@ class Demo {
|
||||
|
||||
this.map = {};
|
||||
this.taps = [];
|
||||
this.ripples = [];
|
||||
this.setOriginTile();
|
||||
|
||||
this.tessellate = new Tessellate(Object.assign({
|
||||
element: '#container',
|
||||
tap: this.onTap,
|
||||
tap: this.tap,
|
||||
pressStart: this.pressStart,
|
||||
press: this.press,
|
||||
draw: this.draw,
|
||||
}, this.settings));
|
||||
}
|
||||
@@ -62,7 +83,7 @@ class Demo {
|
||||
});
|
||||
}
|
||||
|
||||
onTap(tap) {
|
||||
tap(tap) {
|
||||
const {x, y, z} = tap.tile.getPoint();
|
||||
console.log(x, y, z);
|
||||
|
||||
@@ -70,40 +91,77 @@ class Demo {
|
||||
|
||||
this.map[key].pips = Tessellate.utils.random(1,7);
|
||||
console.log(this.map[key].pips);
|
||||
|
||||
// console.log(tap.tile.getPoint());
|
||||
//
|
||||
// this.taps.push(this.createTile(
|
||||
// tap.tile.x,
|
||||
// tap.tile.y,
|
||||
//
|
||||
// Tessellate.utils.random(Tessellate.DRAW_STYLES),
|
||||
// Tessellate.utils.random(Tessellate.TILE_STYLES),
|
||||
// Tessellate.utils.random(Tessellate.TILE_ORIENTATIONS),
|
||||
// ));
|
||||
}
|
||||
|
||||
createTile(x, y, drawStyle, tileStyle, orientation) {
|
||||
pressStart(tap) {
|
||||
this.ripples.push({
|
||||
timestamp: tap.event.timeStamp,
|
||||
|
||||
cell: this.createTile({
|
||||
x: tap.tile.x,
|
||||
y: tap.tile.y,
|
||||
|
||||
scale: 1.0,
|
||||
|
||||
drawStyle: Tessellate.DRAW_STYLES.FILL,
|
||||
tileStyle: Tessellate.TILE_STYLES.CIRCLE,
|
||||
orientation: Tessellate.TILE_ORIENTATIONS.FLAT,
|
||||
|
||||
red: 255,
|
||||
green: 127,
|
||||
blue: 127,
|
||||
alpha: 0.5,
|
||||
})
|
||||
});
|
||||
|
||||
this.taps.push(this.createTile({
|
||||
x: tap.tile.x,
|
||||
y: tap.tile.y,
|
||||
|
||||
drawStyle: Tessellate.utils.random(Tessellate.DRAW_STYLES),
|
||||
tileStyle: Tessellate.utils.random(Tessellate.TILE_STYLES),
|
||||
orientation: Tessellate.utils.random(Tessellate.TILE_ORIENTATIONS),
|
||||
}));
|
||||
}
|
||||
|
||||
press(tap) {
|
||||
console.log('PRESS END');
|
||||
}
|
||||
|
||||
createTile({x, y,
|
||||
drawStyle, tileStyle, orientation,
|
||||
scale = Tessellate.utils.random(7, 9) / 10,
|
||||
red = Tessellate.utils.random(255),
|
||||
green = Tessellate.utils.random(255),
|
||||
blue = Tessellate.utils.random(255),
|
||||
alpha = Tessellate.utils.random(25, 75) / 100,
|
||||
}) {
|
||||
|
||||
return new Tessellate.Cell({
|
||||
x, y,
|
||||
|
||||
scale: Tessellate.utils.random(7, 9) / 10,
|
||||
scale,
|
||||
|
||||
drawStyle,
|
||||
tileStyle,
|
||||
orientation,
|
||||
|
||||
red: Tessellate.utils.random(255),
|
||||
green: Tessellate.utils.random(255),
|
||||
blue: Tessellate.utils.random(255),
|
||||
alpha: Tessellate.utils.random(25,75)/100
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
alpha,
|
||||
});
|
||||
}
|
||||
|
||||
drawTile({x, y, z}, context, scale) {
|
||||
const key = `${ x },${ z != null ? z : y }`;
|
||||
|
||||
this.map[key] = this.map[key] || this.createTile(x, y, this.settings.style, this.settings.tile, this.settings.orientation);
|
||||
this.map[key] = this.map[key] || this.createTile({
|
||||
x, y,
|
||||
drawStyle: this.settings.style,
|
||||
tileStyle: this.settings.tile,
|
||||
orientation: this.settings.orientation,
|
||||
});
|
||||
|
||||
const tile = this.map[key];
|
||||
const pixelPoint = this.tessellate.tileToPixel(x, y, z);
|
||||
@@ -115,6 +173,21 @@ class Demo {
|
||||
draw({context, scale, tilePoints}) {
|
||||
tilePoints.forEach(tilePoint => this.drawTile(tilePoint, context, scale));
|
||||
|
||||
const now = Date.now();
|
||||
this.ripples.forEach(({timestamp, cell}) => {
|
||||
let pressFactor = (now - timestamp) / PRESS_RIPPLE;
|
||||
pressFactor = pressFactor > 1 ? 1 : pressFactor;
|
||||
|
||||
Object.assign(cell, {
|
||||
scale: pressRipple(pressFactor),
|
||||
alpha: pressFade(pressFactor),
|
||||
});
|
||||
|
||||
const pixelPoint = this.tessellate.tileToPixel(cell.x, cell.y);
|
||||
Tessellate.TILES[cell.tileStyle][cell.drawStyle](context, scale, pixelPoint.getX(), pixelPoint.getY(), cell);
|
||||
});
|
||||
this.ripples = this.ripples.filter(ripple => (ripple.timestamp + PRESS_RIPPLE) > now);
|
||||
|
||||
this.taps.forEach(cell => {
|
||||
const pixelPoint = this.tessellate.tileToPixel(cell.x, cell.y);
|
||||
Tessellate.TILES[cell.tileStyle][cell.drawStyle](context, scale, pixelPoint.getX(), pixelPoint.getY(), cell);
|
||||
|
||||
143
src/onTap.js
143
src/onTap.js
@@ -2,7 +2,24 @@
|
||||
import {noop} from './utils.js';
|
||||
|
||||
const DEFAULTS = {
|
||||
debug: false,
|
||||
|
||||
element: document.body,
|
||||
|
||||
desktopPress: false,
|
||||
|
||||
tap: noop,
|
||||
tapStart: noop,
|
||||
move: noop,
|
||||
doubletap: noop,
|
||||
press: noop,
|
||||
pressStart: noop,
|
||||
zoom: noop,
|
||||
|
||||
moveThreshold: 5,
|
||||
doubletapThreshold: 500,
|
||||
pressThreshold: 1000,
|
||||
|
||||
wheelFactor: -100,
|
||||
};
|
||||
|
||||
@@ -10,33 +27,15 @@ export default class OnTap {
|
||||
constructor(settings) {
|
||||
this.settings = Object.assign({}, DEFAULTS, settings);
|
||||
|
||||
this.debug = false;
|
||||
this.state = {
|
||||
clickStartTime: null
|
||||
};
|
||||
|
||||
this.actions = {
|
||||
tap: {
|
||||
callback: settings.tap || noop
|
||||
},
|
||||
move: {
|
||||
threshold: settings.moveThreshold || 5, // greater than in pixels
|
||||
callback: settings.move || noop
|
||||
},
|
||||
doubletap: {
|
||||
threshold: settings.doubletapThreshold || 500, // less than in milliseconds
|
||||
callback: settings.doubletap || noop
|
||||
},
|
||||
press: {
|
||||
threshold: settings.pressThreshold || 1000, // greater than or equal to in milliseconds
|
||||
callback: settings.press || noop
|
||||
},
|
||||
zoom: {
|
||||
callback: settings.zoom || noop
|
||||
},
|
||||
tapStartTime: null,
|
||||
};
|
||||
|
||||
[
|
||||
// TODO: don't set up listeners for these two
|
||||
'tapStart',
|
||||
'pressStart',
|
||||
|
||||
'mousedown',
|
||||
'mouseup',
|
||||
'mousemove',
|
||||
@@ -47,26 +46,40 @@ export default class OnTap {
|
||||
'touchcancel',
|
||||
|
||||
'wheel',
|
||||
].map(method => {
|
||||
].forEach(method => {
|
||||
this[method] = this[method].bind(this);
|
||||
this.settings.element.addEventListener(method, this[method]);
|
||||
});
|
||||
}
|
||||
|
||||
mousedown(event) {
|
||||
if (this.debug) console.debug('onTap.mousedown', event);
|
||||
tapStart(event, mobile = false) {
|
||||
if (this.settings.debug) console.debug('onTap.tapStart', event);
|
||||
|
||||
if (!this.state.clickStartTime) {
|
||||
this.state.lastX = event.offsetX;
|
||||
this.state.lastY = event.offsetY;
|
||||
this.state.clickStartTime = event.timeStamp;
|
||||
if (!this.state.tapStartTime) {
|
||||
this.state.tapStartTime = event.timeStamp;
|
||||
|
||||
if (mobile || this.settings.desktopPress) {
|
||||
clearTimeout(this.state.pressTO);
|
||||
this.state.pressTO = setTimeout(this.pressStart, this.settings.pressThreshold);
|
||||
}
|
||||
|
||||
this.settings.tapStart(event);
|
||||
}
|
||||
}
|
||||
|
||||
mousedown(event) {
|
||||
if (this.settings.debug) console.debug('onTap.mousedown', event);
|
||||
|
||||
this.state.lastX = event.offsetX;
|
||||
this.state.lastY = event.offsetY;
|
||||
|
||||
this.tapStart(event, false);
|
||||
}
|
||||
|
||||
touchstart(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.debug) console.debug('onTap.touchstart', event);
|
||||
if (this.settings.debug) console.debug('onTap.touchstart', event);
|
||||
|
||||
const touches = [...event.touches];
|
||||
event.offsetX = touches.reduce((memo, touch) => memo + touch.pageX, 0) / touches.length;
|
||||
@@ -77,30 +90,44 @@ export default class OnTap {
|
||||
|
||||
if (event.touches.length > 1) {
|
||||
this.state.pinching = true;
|
||||
clearTimeout(this.state.pressTO);
|
||||
}
|
||||
|
||||
if (!this.state.clickStartTime) {
|
||||
this.state.clickStartTime = event.timeStamp;
|
||||
}
|
||||
this.tapStart(event, true);
|
||||
}
|
||||
|
||||
pressStart(event = {timeStamp: Date.now(), offsetX: this.state.lastX, offsetY: this.state.lastY}) {
|
||||
if (this.settings.debug) console.debug('onTap.pressStart', event);
|
||||
|
||||
this.settings.pressStart(event);
|
||||
}
|
||||
|
||||
mouseup(event) {
|
||||
if (this.debug) console.debug('onTap.mouseup', event);
|
||||
if (this.settings.debug) console.debug('onTap.mouseup', event);
|
||||
|
||||
if (!this.state.moving) {
|
||||
this.actions.tap.callback(event);
|
||||
event.duration = event.timeStamp - this.state.tapStartTime;
|
||||
|
||||
if (this.settings.desktopPress && event.duration >= this.settings.pressThreshold) {
|
||||
this.settings.press(event);
|
||||
}
|
||||
else {
|
||||
clearTimeout(this.state.pressTO);
|
||||
this.settings.tap(event);
|
||||
}
|
||||
}
|
||||
|
||||
this.state.moving = null;
|
||||
this.state.lastX = null;
|
||||
this.state.lastY = null;
|
||||
this.state.clickStartTime = null;
|
||||
this.state.tapStartTime = null;
|
||||
clearTimeout(this.state.pressTO);
|
||||
}
|
||||
|
||||
touchend(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.debug) console.debug('onTap.touchend', event);
|
||||
if (this.settings.debug) console.debug('onTap.touchend', event);
|
||||
|
||||
const touches = [...event.touches];
|
||||
|
||||
@@ -117,7 +144,15 @@ export default class OnTap {
|
||||
}
|
||||
|
||||
if (!(this.state.moving || this.state.pinching)) {
|
||||
this.actions.tap.callback(event);
|
||||
event.duration = event.timeStamp - this.state.tapStartTime;
|
||||
|
||||
if (event.duration >= this.settings.pressThreshold) {
|
||||
this.settings.press(event);
|
||||
}
|
||||
else {
|
||||
clearTimeout(this.state.pressTO);
|
||||
this.settings.tap(event);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.touches.length <= 1) {
|
||||
@@ -129,25 +164,26 @@ export default class OnTap {
|
||||
this.state.moving = null;
|
||||
this.state.lastX = null;
|
||||
this.state.lastY = null;
|
||||
this.state.clickStartTime = null;
|
||||
this.state.tapStartTime = null;
|
||||
}
|
||||
}
|
||||
|
||||
mousemove(event) {
|
||||
if (this.debug) console.debug('onTap.mousemove', event);
|
||||
if (this.settings.debug) console.debug('onTap.mousemove', event);
|
||||
|
||||
if (this.state.clickStartTime) {
|
||||
if (this.state.tapStartTime) {
|
||||
if (!this.state.moving) {
|
||||
if ((Math.abs(event.offsetX - this.state.lastX) > this.actions.move.threshold)
|
||||
|| (Math.abs(event.offsetY - this.state.lastY) > this.actions.move.threshold)) {
|
||||
if ((Math.abs(event.offsetX - this.state.lastX) > this.settings.moveThreshold)
|
||||
|| (Math.abs(event.offsetY - this.state.lastY) > this.settings.moveThreshold)) {
|
||||
this.state.moving = true;
|
||||
clearTimeout(this.state.pressTO);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.moving) {
|
||||
event.deltaX = event.offsetX - this.state.lastX,
|
||||
event.deltaY = event.offsetY - this.state.lastY
|
||||
this.actions.move.callback(event);
|
||||
this.settings.move(event);
|
||||
|
||||
this.state.lastX = event.offsetX;
|
||||
this.state.lastY = event.offsetY;
|
||||
@@ -158,9 +194,9 @@ export default class OnTap {
|
||||
touchmove(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.debug) console.debug('onTap.touchmove', event);
|
||||
if (this.settings.debug) console.debug('onTap.touchmove', event);
|
||||
|
||||
if (this.state.clickStartTime) {
|
||||
if (this.state.tapStartTime) {
|
||||
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;
|
||||
@@ -168,15 +204,16 @@ export default class OnTap {
|
||||
if (this.state.pinching) {
|
||||
event.scaleStep = event.scale / (this.state.lastPinch || 1);
|
||||
|
||||
this.actions.zoom.callback(event);
|
||||
this.settings.zoom(event);
|
||||
|
||||
this.state.lastPinch = event.scale;
|
||||
}
|
||||
|
||||
if (!this.state.moving) {
|
||||
if ((Math.abs(event.offsetX - this.state.lastX) > this.actions.move.threshold)
|
||||
|| (Math.abs(event.offsetY - this.state.lastY) > this.actions.move.threshold)) {
|
||||
if ((Math.abs(event.offsetX - this.state.lastX) > this.settings.moveThreshold)
|
||||
|| (Math.abs(event.offsetY - this.state.lastY) > this.settings.moveThreshold)) {
|
||||
this.state.moving = true;
|
||||
clearTimeout(this.state.pressTO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +221,7 @@ export default class OnTap {
|
||||
event.deltaX = event.offsetX - this.state.lastX,
|
||||
event.deltaY = event.offsetY - this.state.lastY
|
||||
|
||||
this.actions.move.callback(event);
|
||||
this.settings.move(event);
|
||||
|
||||
this.state.lastX = event.offsetX;
|
||||
this.state.lastY = event.offsetY;
|
||||
@@ -197,11 +234,11 @@ export default class OnTap {
|
||||
}
|
||||
|
||||
wheel(event) {
|
||||
if (this.debug) console.debug('onTap.wheel', event);
|
||||
if (this.settings.debug) console.debug('onTap.wheel', event);
|
||||
|
||||
event.scaleStep = 1 + (event.deltaY / this.settings.wheelFactor);
|
||||
|
||||
this.actions.zoom.callback(event);
|
||||
this.settings.zoom(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,10 +35,14 @@ const TILES = {
|
||||
const DEFAULTS = {
|
||||
tile: HEX,
|
||||
board: HEX,
|
||||
tap: utils.noop,
|
||||
draw: utils.noop,
|
||||
orientation: FLAT,
|
||||
negativeTiles: true,
|
||||
|
||||
tap: utils.noop,
|
||||
pressStart: utils.noop,
|
||||
press: utils.noop,
|
||||
|
||||
draw: utils.noop,
|
||||
};
|
||||
|
||||
function selectCartographer(board, orientation) {
|
||||
@@ -72,6 +76,9 @@ export class Tessellate {
|
||||
[
|
||||
'checkSettings',
|
||||
'tap',
|
||||
'doubletap',
|
||||
'pressStart',
|
||||
'press',
|
||||
'move',
|
||||
'zoom',
|
||||
'pixelToTile',
|
||||
@@ -87,14 +94,15 @@ export class Tessellate {
|
||||
draw: this.draw
|
||||
});
|
||||
|
||||
this.onTap = new OnTap({
|
||||
this.onTap = new OnTap(Object.assign({
|
||||
element: this.settings.element,
|
||||
tap: this.tap,
|
||||
doubletap: this.doubletap,
|
||||
hold: this.hold,
|
||||
pressStart: this.pressStart,
|
||||
press: this.press,
|
||||
move: this.move,
|
||||
zoom: this.zoom,
|
||||
});
|
||||
}, funky.pick(this.settings, ['desktopPress', 'moveThreshold', 'doubletapThreshold', 'pressThreshold', 'wheelFactor'])));
|
||||
|
||||
const cartographer = selectCartographer(this.settings.board, this.settings.orientation);
|
||||
this.cartographer = new cartographer(Object.assign({
|
||||
@@ -134,6 +142,7 @@ export class Tessellate {
|
||||
}
|
||||
|
||||
doubletap(event) {
|
||||
console.log('DOUBLETAP', event);
|
||||
let point = new Point(event.offsetX, event.offsetY);
|
||||
let tile = this.cartographer.pixelToTile(point);
|
||||
|
||||
@@ -143,11 +152,10 @@ export class Tessellate {
|
||||
tile
|
||||
};
|
||||
|
||||
console.log('DOUBLETAP');
|
||||
console.log(tap);
|
||||
}
|
||||
|
||||
hold(event) {
|
||||
pressStart(event) {
|
||||
let point = new Point(event.offsetX, event.offsetY);
|
||||
let tile = this.cartographer.pixelToTile(point);
|
||||
|
||||
@@ -157,8 +165,20 @@ export class Tessellate {
|
||||
tile
|
||||
};
|
||||
|
||||
console.log('HOLD');
|
||||
console.log(tap);
|
||||
this.settings.pressStart(tap);
|
||||
}
|
||||
|
||||
press(event) {
|
||||
let point = new Point(event.offsetX, event.offsetY);
|
||||
let tile = this.cartographer.pixelToTile(point);
|
||||
|
||||
let tap = {
|
||||
event,
|
||||
point,
|
||||
tile
|
||||
};
|
||||
|
||||
this.settings.press(tap);
|
||||
}
|
||||
|
||||
move(event) {
|
||||
|
||||
Reference in New Issue
Block a user