From 9d7a89c3f26961c4431875eedb101d51d6ffdfe9 Mon Sep 17 00:00:00 2001 From: Jim Eckerlein Date: Thu, 11 Aug 2022 11:56:31 +0200 Subject: [PATCH 1/7] Fix touch input so that orbit does not reset between gestures --- app_web/src/logic/uimodel.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app_web/src/logic/uimodel.js b/app_web/src/logic/uimodel.js index 9c40bb33..62f2e5a8 100644 --- a/app_web/src/logic/uimodel.js +++ b/app_web/src/logic/uimodel.js @@ -265,18 +265,20 @@ class UIModel const touchmove = fromEvent(document, 'touchmove'); const touchstart = fromEvent(inputDomElement, 'touchstart'); const touchend = merge(fromEvent(inputDomElement, 'touchend'), fromEvent(inputDomElement, 'touchcancel')); - + const touchOrbit = touchstart.pipe( filter( event => event.touches.length === 1), - mergeMap(() => touchmove.pipe(takeUntil(touchend))), map( event => event.touches[0]), - pairwise(), - map( ([oldTouch, newTouch]) => { - return { - deltaPhi: newTouch.pageX - oldTouch.pageX, - deltaTheta: newTouch.pageY - oldTouch.pageY - }; - }) + mergeMap(() => touchmove.pipe( + pairwise(), + map( ([oldTouch, newTouch]) => { + return { + deltaPhi: newTouch.pageX - oldTouch.pageX, + deltaTheta: newTouch.pageY - oldTouch.pageY + }; + }), + takeUntil(touchend) + )), ); const touchZoom = touchstart.pipe( From bd21221360818a956019dd7592de8533f3122ff8 Mon Sep 17 00:00:00 2001 From: Jim Eckerlein Date: Thu, 11 Aug 2022 12:28:13 +0200 Subject: [PATCH 2/7] Fix mouse input on iPadOS --- app_web/src/logic/uimodel.js | 44 ++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/app_web/src/logic/uimodel.js b/app_web/src/logic/uimodel.js index 62f2e5a8..ff16eae9 100644 --- a/app_web/src/logic/uimodel.js +++ b/app_web/src/logic/uimodel.js @@ -234,25 +234,45 @@ class UIModel filter(file => file !== undefined), ); - const move = fromEvent(document, 'mousemove'); - const mousedown = fromEvent(inputDomElement, 'mousedown'); - const cancelMouse = merge(fromEvent(document, 'mouseup'), fromEvent(document, 'mouseleave')); - - const mouseOrbit = mousedown.pipe( + const mouseMove = fromEvent(document, 'mousemove'); + const mouseDown = fromEvent(inputDomElement, 'mousedown'); + const mouseUp = merge(fromEvent(document, 'mouseup'), fromEvent(document, 'mouseleave')); + + inputDomElement.addEventListener('mousemove', event => event.preventDefault(), false); + inputDomElement.addEventListener('mousedown', event => event.preventDefault(), false); + inputDomElement.addEventListener('mouseup', event => event.preventDefault(), false); + + const mouseOrbit = mouseDown.pipe( filter( event => event.button === 0 && event.shiftKey === false), - mergeMap(() => move.pipe(takeUntil(cancelMouse))), - map( mouse => ({deltaPhi: mouse.movementX, deltaTheta: mouse.movementY })) + mergeMap(() => mouseMove.pipe( + pairwise(), + map( ([oldMouse, newMouse]) => { + return { + deltaPhi: newMouse.pageX - oldMouse.pageX, + deltaTheta: newMouse.pageY - oldMouse.pageY + }; + }), + takeUntil(mouseUp) + )) ); - const mousePan = mousedown.pipe( + const mousePan = mouseDown.pipe( filter( event => event.button === 1 || event.shiftKey === true), - mergeMap(() => move.pipe(takeUntil(cancelMouse))), - map( mouse => ({deltaX: mouse.movementX, deltaY: mouse.movementY })) + mergeMap(() => mouseMove.pipe( + pairwise(), + map( ([oldMouse, newMouse]) => { + return { + deltaX: newMouse.pageX - oldMouse.pageX, + deltaY: newMouse.pageY - oldMouse.pageY + }; + }), + takeUntil(mouseUp) + )) ); - const smbZoom = mousedown.pipe( + const smbZoom = mouseDown.pipe( filter( event => event.button === 2), - mergeMap(() => move.pipe(takeUntil(cancelMouse))), + mergeMap(() => mouseMove.pipe(takeUntil(mouseUp))), map( mouse => ({deltaZoom: mouse.movementY })) ); const wheelZoom = fromEvent(inputDomElement, 'wheel').pipe( From 2a0a117cd69ab63e8f91c7868f087b7e950c1734 Mon Sep 17 00:00:00 2001 From: Jim Eckerlein Date: Thu, 11 Aug 2022 12:37:32 +0200 Subject: [PATCH 3/7] Ensure that mouse events do not select the canvas --- app_web/src/ui/sass.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app_web/src/ui/sass.scss b/app_web/src/ui/sass.scss index 651918be..6114e005 100644 --- a/app_web/src/ui/sass.scss +++ b/app_web/src/ui/sass.scss @@ -321,6 +321,9 @@ button.button border: 0px; padding: 0px; background: rgb(51, 51, 51); + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; } .canvasUIMaximize { From 9c58152bca13015b0546384c09deb10174754c61 Mon Sep 17 00:00:00 2001 From: Jim Eckerlein Date: Thu, 3 Nov 2022 13:21:38 +0100 Subject: [PATCH 4/7] Fix orbiting on Android devices --- app_web/src/logic/uimodel.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app_web/src/logic/uimodel.js b/app_web/src/logic/uimodel.js index ff16eae9..be520c3b 100644 --- a/app_web/src/logic/uimodel.js +++ b/app_web/src/logic/uimodel.js @@ -287,14 +287,16 @@ class UIModel const touchend = merge(fromEvent(inputDomElement, 'touchend'), fromEvent(inputDomElement, 'touchcancel')); const touchOrbit = touchstart.pipe( - filter( event => event.touches.length === 1), - map( event => event.touches[0]), + filter(event => event.touches.length === 1), + map(event => event.touches[0]), mergeMap(() => touchmove.pipe( + filter(event => event.touches.length === 1), + map(event => event.touches[0]), pairwise(), - map( ([oldTouch, newTouch]) => { + map(([oldTouch, newTouch]) => { return { - deltaPhi: newTouch.pageX - oldTouch.pageX, - deltaTheta: newTouch.pageY - oldTouch.pageY + deltaPhi: newTouch.clientX - oldTouch.clientX, + deltaTheta: newTouch.clientY - oldTouch.clientY, }; }), takeUntil(touchend) From ab100e2f6b09626dfcdd2a28bc02c2b70dd18a90 Mon Sep 17 00:00:00 2001 From: Jim Eckerlein Date: Thu, 3 Nov 2022 13:30:59 +0100 Subject: [PATCH 5/7] Fix zooming --- app_web/src/logic/uimodel.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app_web/src/logic/uimodel.js b/app_web/src/logic/uimodel.js index be520c3b..81fd922c 100644 --- a/app_web/src/logic/uimodel.js +++ b/app_web/src/logic/uimodel.js @@ -1,5 +1,5 @@ import { Observable, merge, fromEvent } from 'rxjs'; -import { map, filter, startWith, pluck, takeUntil, mergeMap, pairwise } from 'rxjs/operators'; +import { map, filter, startWith, pluck, takeUntil, mergeMap, pairwise, tap } from 'rxjs/operators'; import { GltfState } from 'gltf-viewer-source'; import { SimpleDropzone } from 'simple-dropzone'; @@ -288,7 +288,6 @@ class UIModel const touchOrbit = touchstart.pipe( filter(event => event.touches.length === 1), - map(event => event.touches[0]), mergeMap(() => touchmove.pipe( filter(event => event.touches.length === 1), map(event => event.touches[0]), @@ -304,15 +303,18 @@ class UIModel ); const touchZoom = touchstart.pipe( - filter( event => event.touches.length === 2), - mergeMap(() => touchmove.pipe(takeUntil(touchend))), - map( event => { - const pos1 = vec2.fromValues(event.touches[0].pageX, event.touches[0].pageY); - const pos2 = vec2.fromValues(event.touches[1].pageX, event.touches[1].pageY); - return vec2.dist(pos1, pos2); - }), - pairwise(), - map( ([oldDist, newDist]) => ({ deltaZoom: newDist - oldDist })) + filter(event => event.touches.length === 2), + mergeMap(() => touchmove.pipe( + filter(event => event.touches.length === 2), + map(event => { + const pos1 = vec2.fromValues(event.touches[0].clientX, event.touches[0].clientY); + const pos2 = vec2.fromValues(event.touches[1].clientX, event.touches[1].clientY); + return vec2.dist(pos1, pos2); + }), + pairwise(), + map(([oldDist, newDist]) => ({ deltaZoom: newDist - oldDist })), + takeUntil(touchend)) + ), ); inputDomElement.addEventListener('ontouchmove', event => event.preventDefault(), false); From 91ffab5f0508a7bb63263d1166894ec99e5293d5 Mon Sep 17 00:00:00 2001 From: Jim Eckerlein Date: Thu, 3 Nov 2022 13:35:28 +0100 Subject: [PATCH 6/7] Remove unused import --- app_web/src/logic/uimodel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app_web/src/logic/uimodel.js b/app_web/src/logic/uimodel.js index 81fd922c..142215ef 100644 --- a/app_web/src/logic/uimodel.js +++ b/app_web/src/logic/uimodel.js @@ -1,5 +1,5 @@ import { Observable, merge, fromEvent } from 'rxjs'; -import { map, filter, startWith, pluck, takeUntil, mergeMap, pairwise, tap } from 'rxjs/operators'; +import { map, filter, startWith, pluck, takeUntil, mergeMap, pairwise } from 'rxjs/operators'; import { GltfState } from 'gltf-viewer-source'; import { SimpleDropzone } from 'simple-dropzone'; From f604a1861ec69d8fd6812ea77479633aaf81d6a7 Mon Sep 17 00:00:00 2001 From: Jim Eckerlein Date: Fri, 4 Nov 2022 12:02:09 +0100 Subject: [PATCH 7/7] Fix zooming and panning on touch devices --- app_web/index.html | 2 +- app_web/src/logic/uimodel.js | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/app_web/index.html b/app_web/index.html index 4df2c6e9..d0a1fe7d 100644 --- a/app_web/index.html +++ b/app_web/index.html @@ -86,7 +86,7 @@ - +
diff --git a/app_web/src/logic/uimodel.js b/app_web/src/logic/uimodel.js index 142215ef..90b9e818 100644 --- a/app_web/src/logic/uimodel.js +++ b/app_web/src/logic/uimodel.js @@ -238,9 +238,9 @@ class UIModel const mouseDown = fromEvent(inputDomElement, 'mousedown'); const mouseUp = merge(fromEvent(document, 'mouseup'), fromEvent(document, 'mouseleave')); - inputDomElement.addEventListener('mousemove', event => event.preventDefault(), false); - inputDomElement.addEventListener('mousedown', event => event.preventDefault(), false); - inputDomElement.addEventListener('mouseup', event => event.preventDefault(), false); + inputDomElement.addEventListener('mousemove', event => event.preventDefault()); + inputDomElement.addEventListener('mousedown', event => event.preventDefault()); + inputDomElement.addEventListener('mouseup', event => event.preventDefault()); const mouseOrbit = mouseDown.pipe( filter( event => event.button === 0 && event.shiftKey === false), @@ -279,7 +279,8 @@ class UIModel map(wheelEvent => normalizeWheel(wheelEvent)), map(normalizedZoom => ({deltaZoom: normalizedZoom.spinY })) ); - inputDomElement.addEventListener('onscroll', event => event.preventDefault(), false); + inputDomElement.addEventListener('scroll', event => event.preventDefault(), { passive: false }); + inputDomElement.addEventListener('wheel', event => event.preventDefault(), { passive: false }); const mouseZoom = merge(smbZoom, wheelZoom); const touchmove = fromEvent(document, 'touchmove'); @@ -294,8 +295,8 @@ class UIModel pairwise(), map(([oldTouch, newTouch]) => { return { - deltaPhi: newTouch.clientX - oldTouch.clientX, - deltaTheta: newTouch.clientY - oldTouch.clientY, + deltaPhi: 2.0 * (newTouch.clientX - oldTouch.clientX), + deltaTheta: 2.0 * (newTouch.clientY - oldTouch.clientY), }; }), takeUntil(touchend) @@ -312,12 +313,14 @@ class UIModel return vec2.dist(pos1, pos2); }), pairwise(), - map(([oldDist, newDist]) => ({ deltaZoom: newDist - oldDist })), + map(([oldDist, newDist]) => ({ deltaZoom: 0.1 * (oldDist - newDist) })), takeUntil(touchend)) ), ); - inputDomElement.addEventListener('ontouchmove', event => event.preventDefault(), false); + inputDomElement.addEventListener('ontouchmove', event => event.preventDefault(), { passive: false }); + inputDomElement.addEventListener('ontouchstart', event => event.preventDefault(), { passive: false }); + inputDomElement.addEventListener('ontouchend', event => event.preventDefault(), { passive: false }); observables.orbit = merge(mouseOrbit, touchOrbit); observables.pan = mousePan;