diff --git a/src/controls/PixelPainterControls.js b/src/controls/PixelPainterControls.js index 4d486a5..8b1b7c4 100644 --- a/src/controls/PixelPainterControls.js +++ b/src/controls/PixelPainterControls.js @@ -16,6 +16,7 @@ import { screenToWorld, getChunkOfPixel, getOffsetOfPixel, + getTapOrClickCenter, } from '../core/utils'; import { HOLD_PAINT, @@ -28,8 +29,9 @@ class PixelPainterControls { // clickTapStartView = [0, 0]; clickTapStartTime = 0; - clickTapStartCoords = [0, 0]; tapStartDist = 50; + // screen coords of where a tap/click started + clickTapStartCoords = [0, 0]; // stored speed for acceleration speedScalar = 0; // on mouse: true as long as left mouse button is pressed @@ -118,7 +120,7 @@ class PixelPainterControls { ].map(Math.abs); // thresholds for single click / holding if (clickTapStartTime > Date.now() - 250 - && coordsDiff[0] < 3 && coordsDiff[1] < 3 + && coordsDiff[0] < 6 && coordsDiff[1] < 6 ) { PixelPainterControls.placePixel( store, @@ -231,7 +233,7 @@ class PixelPainterControls { this.clearTabTimeout(); this.isTapPainting = false; this.clickTapStartTime = Date.now(); - this.clickTapStartCoords = PixelPainterControls.getTouchCenter(event); + this.clickTapStartCoords = getTapOrClickCenter(event); this.clickTapStartView = this.renderer.view; if (event.touches.length > 1) { @@ -263,26 +265,25 @@ class PixelPainterControls { onTouchEnd(event) { event.preventDefault(); if (event.touches.length) { - // still other touches left return; } const { store, renderer } = this; if (!this.wasEverMultiTap) { - const { pageX, pageY } = event.changedTouches[0]; + const [clientX, clientY] = getTapOrClickCenter(event); const { clickTapStartCoords, clickTapStartTime } = this; const coordsDiff = [ - clickTapStartCoords[0] - pageX, - clickTapStartCoords[1] - pageY, + clickTapStartCoords[0] - clientX, + clickTapStartCoords[1] - clientY, ].map(Math.abs); // thresholds for single click / holding if (clickTapStartTime > Date.now() - 580 - && coordsDiff[0] < 3 && coordsDiff[1] < 3 + && coordsDiff[0] < 6 && coordsDiff[1] < 6 ) { PixelPainterControls.placePixel( store, this.renderer, - this.screenToWorld([pageX, pageY]), + this.screenToWorld([clientX, clientY]), ); setTimeout(() => { store.dispatch(unsetHover()); @@ -300,7 +301,7 @@ class PixelPainterControls { const multiTouch = (event.touches.length > 1); const state = this.store.getState(); - const [clientX, clientY] = PixelPainterControls.getTouchCenter(event); + const [clientX, clientY] = getTapOrClickCenter(event); if (this.isMultiTap !== multiTouch) { this.wasEverMultiTap = true; // if one finger got lifted or added, reset clickTabStart @@ -328,7 +329,7 @@ class PixelPainterControls { const [lastPosX, lastPosY] = clickTapStartView; const deltaX = clientX - clickTapStartCoords[0]; const deltaY = clientY - clickTapStartCoords[1]; - if (deltaX > 2 || deltaY > 2) { + if (deltaX > 5 || deltaY > 5) { this.clearTabTimeout(); } const { viewscale: scale } = this.renderer; diff --git a/src/core/utils.js b/src/core/utils.js index bba87aa..cf3f8c0 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -636,6 +636,30 @@ export function getDateKeyOfTs(ts) { return `${year}${month}${day}`; } +/* + * get screen coords of touch / mouse event + * @param event MouseEvent or TouchEvent + * @return [x, y] in screen coordinates + */ +export function getTapOrClickCenter(event) { + if (event instanceof TouchEvent) { + const touches = (event.touches.length) + ? event.touches : event.changedTouches; + let x = 0; + let y = 0; + for (const { pageX, pageY } of touches) { + x += pageX; + y += pageY; + } + const { length } = touches; + return [x / length, y / length]; + } + return [ + event.clientX, + event.clientY, + ]; +} + /* * check if parent window exists and * is accessible diff --git a/src/ui/Renderer3D.js b/src/ui/Renderer3D.js index a2b0e10..f5ace11 100644 --- a/src/ui/Renderer3D.js +++ b/src/ui/Renderer3D.js @@ -28,6 +28,7 @@ import ChunkLoader from './ChunkLoader3D'; import { getChunkOfPixel, getOffsetOfPixel, + getTapOrClickCenter, } from '../core/utils'; import { THREE_TILE_SIZE, @@ -62,12 +63,15 @@ class Renderer3D extends Renderer { threeRenderer; // temp variables for mouse events mouse = new Vector2(); - mouseMoveStart; raycaster = new Raycaster(); pressTime; pressCdTime; multitap = 0; lastIntersect = 0; + // on touch: true if current tab was ever more than one figher at any time + wasEverMultiTap = false; + // screen coords of where a tap/click started + clickTapStartCoords = [0, 0]; constructor(store) { super(store); @@ -188,7 +192,6 @@ class Renderer3D extends Renderer { ); this.controls = controls; - this.onDocumentMouseMove = this.onDocumentMouseMove.bind(this); this.onDocumentTouchMove = this.onDocumentTouchMove.bind(this); // eslint-disable-next-line max-len @@ -516,13 +519,16 @@ class Renderer3D extends Renderer { ); } - onDocumentMouseDownOrTouchStart() { + onDocumentMouseDownOrTouchStart(event) { this.pressTime = Date.now(); - const state = this.store.getState(); - this.mouseMoveStart = state.canvas.hover; + this.clickTapStartCoords = getTapOrClickCenter(event); + this.wasEverMultiTap = (event.touches?.length > 1); } - onDocumentTouchMove() { + onDocumentTouchMove(event) { + if (event.touches?.length > 1) { + this.wasEverMultiTap = true; + } this.updateRollOverMesh(0, 0); } @@ -581,7 +587,7 @@ class Renderer3D extends Renderer { ); } - multiTapEnd() { + multiTapEnd(event) { const { store, multitap, @@ -589,12 +595,16 @@ class Renderer3D extends Renderer { this.multitap = 0; const state = store.getState(); - if (!this.mouseMoveStart || !state.canvas.hover) { + if (!state.canvas.hover || this.wasEverMultiTap) { return; } - const [px, py, pz] = this.mouseMoveStart; - const [qx, qy, qz] = state.canvas.hover; - if (px !== qx || py !== qy || pz !== qz) { + const [clientX, clientY] = getTapOrClickCenter(event); + const { clickTapStartCoords } = this; + const coordsDiff = [ + clickTapStartCoords[0] - clientX, + clickTapStartCoords[1] - clientY, + ].map(Math.abs); + if (coordsDiff[0] > 5 || coordsDiff[1] > 5) { return; } @@ -605,7 +615,7 @@ class Renderer3D extends Renderer { if (this.rollOverMesh.position.y < 0) { return; } - this.placeVoxel(px, py, pz); + this.placeVoxel(...state.canvas.hover); break; } case 2: { @@ -644,6 +654,9 @@ class Renderer3D extends Renderer { onDocumentTouchEnd(event) { event.preventDefault(); + if (event.touches.length) { + return; + } const curTime = Date.now(); if (curTime - this.pressTime > 600) { @@ -654,7 +667,7 @@ class Renderer3D extends Renderer { // we should reset on every tap // but we don't need that right now... if (this.multitap === 0) { - setTimeout(this.multiTapEnd, 500); + setTimeout(() => this.multiTapEnd(event), 500); } this.multitap += 1; } @@ -680,21 +693,21 @@ class Renderer3D extends Renderer { return; } - if (!this.mouseMoveStart || !state.canvas.hover) { + if (!state.canvas.hover) { return; } - const [px, py, pz] = this.mouseMoveStart; - const [qx, qy, qz] = state.canvas.hover; - if (px !== qx || py !== qy || pz !== qz) { + const [clientX, clientY] = getTapOrClickCenter(event); + const { clickTapStartCoords } = this; + const coordsDiff = [ + clickTapStartCoords[0] - clientX, + clickTapStartCoords[1] - clientY, + ].map(Math.abs); + if (coordsDiff[0] > 5 || coordsDiff[1] > 5) { return; } event.preventDefault(); - const { - clientX, - clientY, - button, - } = event; + const { button } = event; const { innerWidth, innerHeight,