I have this pen:
https://codepen.io/anon/pen/eyKeqK
If you try it on a touch-screen device (f.e. visit the pen on your phone) you'll notice that when you drag, the white light (the little sphere) only moves for a tiny bit then it stops working.
The logic for the movement is in the pointermove
event handler. It works fine on Desktop using a mouse, just not with touch.
How do we fix this so the light keeps moving while touch dragging (not just for a moment), and as bonus how do we prevent it from refreshing the page when we pull down?
Here's the code for the pen:
HTML (Slim):
/! Made with http://github.com/trusktr/infamous script src="https://cdn.rawgit.com/trusktr/e37dbc24c51b9d3e2f9e508e75cf8f99/raw/2a3fee4ee506a05cc4ac509f592f0c3af1ddfed4/infamous-mixed-mode-3.js" script src="https://unpkg.com/[email protected]/src/Tween.js" i-scene experimental-webgl="true" id="scene" TODO-perspective="800" backgroundColor="0 0 0" backgroundOpacity="0" style="perspective: 800px" shadowmap-type="pcfsoft" i-ambient-light color="#404040" intensity="1" i-dom-plane id="bg" sizeMode="proportional proportional" size="1 1 0" i-node id="button-container" position="0 0 6" size="600 31 0" align="0.5 0.5 0" mountPoint="0.5 0.5 0" - for n in (0..4) i-dom-plane sizeMode="literal proportional" size="100 1 0" align="#{n*0.25} 0 0" mountPoint="#{n*0.25} 0 0" button button #{n+1} i-point-light id="light" color="white" position="300 300 120" size="0 0 0" cast-shadow="true" intensity="1" i-mesh has="sphere-geometry basic-material" size="10 10 10" color="white" receive-shadow="false" cast-shadow="false" style="pointer-events: none"
CSS (Stylus):
body, html width 100% height 100% margin 0 padding 0 font-family sans-serif i-node text-align center #bg background #62B997 button width 100% height 100% white-space nowrap border-radius 0px border 1px solid #534334 background lighten(#FB752C, 20%) color darken(#534334, 10%) outline none // remove those darn ugly browser-specific outlines &:focus, &:hover background #FB752C color darken(#534334, 20%)
JavaScript:
infamous.html.useDefaultNames() const Motor = infamous.core.Motor light.threeObject3d.shadow.radius = 3 light.threeObject3d.distance = 20000 light.threeObject3d.shadow.bias = 0.00001 document.addEventListener('pointermove', e => { e.preventDefault() light.position.x = e.clientX light.position.y = e.clientY }) let downTween, upTween, pressedButton // On mouse down animate the button downward document.addEventListener('pointerdown', e => { if ( is( e.target, 'button' ) ) { pressedButton = e.target if (upTween) { upTween.stop() upTween = null } downTween = new TWEEN.Tween(e.target.parentNode.position) .to({z: -6}, 75) .start() .onComplete(() => downTween = null) Motor.addRenderTask(time => { if (!downTween) return false downTween.update(time) }) } }) // On mouse up animate the button upward document.addEventListener('pointerup', e => { if ( pressedButton ) { if (downTween) { downTween.stop() downTween = null } upTween = new TWEEN.Tween(pressedButton.parentNode.position) .to({z: 0}, 75) .start() .onComplete(() => upTween = null) Motor.addRenderTask(time => { if (!upTween) return false upTween.update(time) }) } }) // The following is a temporary hack because opacity isn't // exposed through the HTML API yet. work-in-progress... setTimeout(() => { Array.from( document.querySelectorAll('i-dom-plane') ).forEach(n => { n.threeObject3d.material.opacity = 0.3 }) scene._needsToBeRendered() }, 0) function is( el, selector ) { if ( [].includes.call( document.querySelectorAll( selector ), el ) ) return true return false }
Pointer events are DOM events that are fired for a pointing device. They are designed to create a single DOM event model to handle pointing input devices such as a mouse, pen/stylus or touch (such as one or more fingers).
The pointermove event is fired when a pointer changes coordinates, and the pointer has not been canceled by a browser touch-action.
On the MDN documentation page about pointermove
, there's this line:
The pointermove event is fired when a pointer changes coordinates, and the pointer has not been canceled by a browser touch-action.
source, emphasis mine
After a short period of time, the (mobile) browser will claim the pointermove
event for "native" behavior like panning the page.
The designed, simple solution is to use the css property touch-action
and set it to none
on the container that has the event handler.
Here's the css property added to your codepen: https://codepen.io/anon/pen/XVBMvL
Or in a simplified example:
var dot = document.querySelector(".dot") document.body.addEventListener("pointermove", function(ev) { dot.style.transform = `translate3d(${ev.clientX}px, ${ev.clientY}px, 0)`; }, false);
* { margin: 0; padding: 0 } .wrapper { display: flex; height: 100vh; } .hasTouchAction, .noTouchAction { flex-grow: 1; text-align: center; background: #efefef; } .hasTouchAction { touch-action: none; } .noTouchAction { background: #ccc; } .dot { width: 16px; height: 16px; border-radius: 50%; background: red; position: absolute; top: -8px; left: -8px; }
<div class="wrapper"> <div class="hasTouchAction"> With <code>touch-action: none</code> </div> <div class="noTouchAction"> Without <code>touch-action</code> </div> </div> <div class="dot"></div>
Make sure you don't break important things and hurt accessibility. Also spend some time to investigate browser support. This worked for me with touch emulated events in Chrome, but might not work in every browser...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With