如何在Chrome Android上禁用pointercancel事件而不影响触摸滚动?

8

我有一些子div在一个可滚动的区域内,就像这样:

<div style='overflow: scroll;'>
    <div id='a' />
    <div id='b' />
    <div id='c' />
    ...
</div>

我在每个子元素上监听pointerdown事件,当其中一个触发时,我会在文档上设置pointermove处理程序。例如:

const pointerdownHandle = e => {
    e.target.releasePointerCapture(e.pointerId)
    document.addEventListener('pointermove', pointermoveHandle)
    document.addEventListener('pointerup', pointerupHandle)
}

const pointermoveHandle = e => { ... }

const pointerupHandle = {
    document.removeEventListener('pointermove', pointermoveHandle)
    document.removeEventListener('pointerup', pointerupHandle)  
}

document.getElementById('a').addEventListener('pointerdown', pointerdownHandle)

这在桌面端和iOS Safari上运行得很好。 但是在Android Chrome上,pointercancel事件几乎立即触发,导致出现问题。

这似乎是预期的行为:“当浏览器确定不太可能有更多指针事件时,或者在触发pointerdown事件后,指针被用于通过平移,缩放或滚动来操作视口时,会触发pointercancel事件。”

推荐的解决方案是将css属性“touch-action:none”应用于父元素。 这样做是有效的。 但是不幸的是,这也会破坏滚动,因为现在触摸操作被忽略了。

我尝试在pointerdown事件触发后以编程方式应用css属性,但这没有起作用。 将preventDefault / stopPropagation添加到pointermoveHandle也不起作用。

有人解决了这个问题吗? 如何停止触发pointercancel事件而不禁用父元素上的滚动?

(我意识到可以退回到触摸事件,但支持pointerenter和pointerleave的指针事件更简单、更易清理。)


1
你尝试过 touch-action: pan-y 吗? - Andre Nuechter
是的,我有。除了“none”以外的任何触摸操作都会导致“pointercancel”事件被触发。 - user1031947
你解决了吗? - Dmitry Minkovsky
3
@Dmitry -- 不,我从来没有这样做过。最终我不得不重写它以使用触摸事件。 - user1031947
@user1031947 谢谢,我可能也得这么做。我一开始使用PointerEvents是因为我认为它在各种浏览器上的支持更好 :). - Dmitry Minkovsky
@user1031947 请问您在触摸事件方面有什么进展吗?我也遇到了同样的问题,只不过是在iOS上。当开始缩放时关闭pan-x和pan-y似乎没有任何作用。 - Emperor Eto
2个回答

3

你正在使用 releasePointerCapture,但我认为你可能想要做相反的事情。指针捕获将移动事件定向到您的元素,因此您不必在document上放置事件处理程序。

尽管我在其他地方都使用指针事件,但我不得不使用一个触摸事件来取消滚动。我无法弄清如何使用指针事件来取消滚动。

function makeDraggable(element) {
    let pos = {x: 0, y: 0}
    let dragging = false
    
    const stopScrollEvents = (event) => {
        event.preventDefault()
    }
        
    const pointerdownHandle = (event) => {
        dragging = {dx: pos.x - event.clientX, dy: pos.y - event.clientY}
        element.classList.add('dragging')
        element.setPointerCapture(event.pointerId)
    }
    
    const pointerupHandle = (event ) => {
        dragging = null
        element.classList.remove('dragging')
    }
    
    const pointermoveHandle = (event) => {
        if (!dragging) return
        pos.x = event.clientX + dragging.dx
        pos.y = event.clientY + dragging.dy
        element.style.transform = `translate(${pos.x}px, ${pos.y}px)`
    }
    
    element.addEventListener('pointerdown', pointerdownHandle)
    element.addEventListener('pointerup', pointerupHandle)
    element.addEventListener('pointercancel', pointerupHandle)
    element.addEventListener('pointermove', pointermoveHandle)
    element.addEventListener('touchstart', stopScrollEvents)
}

for (let element of document.querySelectorAll("#draggable-children > div")) {
    makeDraggable(element)
}
#draggable-children {
    width: 100%;
    height: 100%;
    min-height: 5em;
    background: #eee;
    border: 1px solid black;
}

#draggable-children > div {
    width: 5em;
    height: 1.5em;
    background: #88a;
    border: 1px solid black;
    cursor: grab;
}

#draggable-children > div.dragging {
    width: 5em;
    height: 1.5em;
    background: #aa8;
    border: 1px solid black;
    cursor: grabbing;
}
<div id="draggable-children">
    <div></div>
    <div></div>
    <div></div>
</div>


你可以在CSS中使用touch-events: none来忽略touchstart事件,参考这个答案 :) - Simon
1
同意,touch-events: none 在某些情况下更简单且有效。对于楼主的例子,这可能会起作用。但在我的许多使用情况中并不适用,所以我不得不切换到 touchstart 的方法。详情 - amitp

0

我遇到了类似的问题。我只有一个可拖动的小部件,在Android上失败了。解决方法是将touch-action设置为仅在拖动元素上。pinch-zoom仍然有效,允许缩放,但任何单指平移事件都会被阻止。

所以在你的情况下,我不确定你是否试图使滚动区域内的div可拖动,或者其他什么,但也许touch-action:pinch-zoom可以在div上起作用。

#a, #b, #c {
    touch-action: pinch-zoom;
}

如果事情仍然不起作用,您可以在开始拖动物品时将文档的状态设置为禁用任何触摸操作,然后在pointerup或pointercancel事件上再次删除它。
您可以通过在body元素上设置类来实现此目的。
.dragging {
  touch-action: pinch-zoom;
  /*touch-action: none; <- disables all touch gestures */
}

const pointerdownHandle = e => {
    document.body.classList.add('dragging')
    // ... your code ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接