`pointermove`事件不适用于触摸.为什么不?

tru*_*ktr 11 html javascript touch dom-events pointer-events

我有这支笔:

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

如果你在触摸屏设备上尝试(fe访问手机上的笔),你会注意到当你拖动时,白光(小球体)只移动一点点然后停止工作.

移动的逻辑在pointermove事件处理程序中.它使用鼠标在桌面上工作正常,而不是触摸.

我们如何解决这个问题,以便在触摸拖动时光线不断移动(不仅仅是片刻),作为奖励我们如何在拉下时防止它刷新页面?


这是笔的代码:

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/tween.js@16.6.0/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"
Run Code Online (Sandbox Code Playgroud)

CSS(手写笔):

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%)
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

use*_*291 30

在MDN文档页面上pointermove,有这一行:

当指针改变坐标时,会触发pointermove事件,并且浏览器触摸操作尚未取消指针.

来源,强调我的

在很短的一段时间后,(移动)浏览器将声明该pointermove事件的"本机"行为,如平移页面.

设计的简单解决方案是使用css属性touch-action并将其设置为none具有事件处理程序的容器.

这是添加到您的codepen的css属性:https://codepen.io/anon/pen/XVBMvL

或者在一个简化的例子中:

  • 将浏览器设置为模拟触摸(在Chrome中,开发工具>传感器>触摸)
  • 在左侧部分开始交互,点将跟随您的手指
  • 在正确的部分开始交互,你会发现它会像提供的例子一样迅速失败

var dot = document.querySelector(".dot")
document.body.addEventListener("pointermove", function(ev) {
  dot.style.transform = `translate3d(${ev.clientX}px, ${ev.clientY}px, 0)`;

}, false);
Run Code Online (Sandbox Code Playgroud)
* { 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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

确保你不会破坏重要的东西并损害可访问性.还花一些时间来研究浏览器支持.这对我来说适用于Chrome中的触控模拟事件,但可能无法在每个浏览器中使用...

  • 如果在触发pointerdown后动态设置它似乎不起作用。 (4认同)
  • 谢谢!!我错过了那一部分.对于`e.preventDefault()`而言,直接使用web API是非常直观的!使用PEP.js polyfill,它可以在Safari,Chrome和Firefox中使用(尽管PEP.js需要触摸操作属性而不是样式属性).我得把手放在Edge上触摸...... (2认同)
  • 你拯救了我的职业生涯,哈哈 (2认同)