web-dev-qa-db-fra.com

L'événement `pointermove` ne fonctionne pas avec le toucher. Pourquoi pas?

J'ai ce stylo:

https://codepen.io/anon/pen/eyKeqK

Si vous l'essayez sur un appareil à écran tactile (par exemple, visitez le stylo sur votre téléphone), vous remarquerez que lorsque vous faites glisser, la lumière blanche (la petite sphère) ne se déplace que pendant un tout petit peu, puis il cesse de fonctionner.

La logique du mouvement se trouve dans le gestionnaire d'événements pointermove. Cela fonctionne très bien sur le bureau à l'aide d'une souris, mais pas avec le toucher.

Comment pouvons-nous résoudre ce problème afin que la lumière continue de bouger pendant le glissement tactile (pas seulement pendant un moment), et en bonus comment l'empêcher de rafraîchir la page lorsque nous tirons vers le bas?


Voici le code du stylo:

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 (stylet):

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
}
14
trusktr

Sur la page de documentation MDN sur pointermove, il y a cette ligne:

L'événement pointermove est déclenché lorsqu'un pointeur change de coordonnées, et le pointeur n'a pas été annulé par une action tactile du navigateur.

source , c'est moi qui souligne

Après une courte période de temps, le navigateur (mobile) réclamera l'événement pointermove pour un comportement "natif" comme le panoramique de la page.

La solution simple et conçue consiste à utiliser la propriété css touch-action et définissez-le sur none sur le conteneur qui contient le gestionnaire d'événements.

Voici la propriété css ajoutée à votre codepen: https://codepen.io/anon/pen/XVBMvL

Ou dans un exemple simplifié:

  • Configurez votre navigateur pour émuler le toucher (dans Chrome, outils de développement> Capteurs> Touch)
  • Commencez une interaction dans la partie gauche et le point suivra votre doigt
  • Commencez une interaction dans la bonne partie, et vous remarquerez qu'elle échouera rapidement comme dans l'exemple fourni
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>

Assurez-vous de ne pas casser les choses importantes et de nuire à l'accessibilité. Passez également un peu de temps à étudier la prise en charge du navigateur. Cela a fonctionné pour moi avec les événements émulés tactiles dans Chrome, mais peut ne pas fonctionner dans tous les navigateurs ...

28
user3297291