stricter thresholds for clicks and taps to avoid accidental pixel

placing
This commit is contained in:
HF 2024-01-24 13:13:31 +01:00
parent ce09198374
commit 641c996c0c
3 changed files with 71 additions and 33 deletions

View File

@ -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;

View File

@ -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

View File

@ -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,