React touchMove事件踩坑记录

    PM需求是在h5页面上有一个可以拖拽的video,效果类似于ios AssistiveTouch,如下图所示:

    在完成这个需求的时候,采用了React的Touch Event来对这种拖拽效果进行计算。部分代码如下

    在该video元素的dom上使用onTouchxxx绑定了相关事件,结果发现后面的页面也跟着发生了滚动,效果如下:

于是就跟以前一样想利用e.preventDefault()方法来禁止父级元素跟着滚动,代码如下

    结果在chrome的dev-tools里面发现了大量warning,而且在手机端上并没有出现理想的效果,warning如下图:

    通过搜索相关的warning结果发现原因为以下信息

1
2
3
AddEventListenerOptions defaults passive to false. With this change touchstart and touchmove listeners added to the document will default to passive:true (so that calls to preventDefault will be ignored)..
If the value is explicitly provided in the AddEventListenerOptions it will continue having the value specified by the page.
This is behind a flag starting in Chrome 54, and enabled by default in Chrome 56.

    根据 chrome 的提示得知,为了提高页面滚动的性能, Chrome 现在默认把通过在 document 上绑定的事件监听器 passive 属性(后面细说)默认置为 true,这样就会导致设置的 e.preventDefault() 被忽视了。为了防止带来的副作用,官方考虑的很周到,给我们提供了一个 CSS 属性专门用来解决这个问题

1
touch-action: none;

    在加上了这个属性之后,安卓机确实按照需求的样子执行了,感觉应该能行。可是在ios中,移动video元素的时候父级元素还是在不停的滚动,并没有发生变化,兼容问题令人头疼~

    查询了相关的兼容性之后才发现,在ios上对该属性只是部分兼容。所以只能考虑别的方法,由于前面说的产生这样的原因是因为chrome默认把passive置成了true,导致e.preventDefault()被忽略了。所以只能想办法把这个值置为false。查询相关文档后,React提供的合成事件没有提供传递该参数的方法,所以只能降级使用原生的事件绑定方式。查询MDN后发现addEventListener提供了修改该参数的方法:

文档在这里

解决方法:

    然后就解决了该问题,实现效果如文档开始展现的那样。以后碰到类似的情况,可以用这种方式解决。在解决该问题的过程中,一开始以为是一个通用的问题,所以开始搜索的时候都是搜索的如何避免页面穿透这样的思路,然后试了好多方法,比如将父级元素的position定位fixed,又或者强行截断冒泡事件的执行,在页面上用scrollTop将值始终置为0,最终发现效果都不太好。最后发现是chrome针对页面滚动而采取的措施,开始搜索的方向就有点偏,稍微浪费了点时间。吃一堑长一智,以后还是要多多写代码,积累相关的经验。。。

文章作者: Junjie
文章链接: http://yoursite.com/2019/07/22/reactTouchmove/
版权声明: 本博客所有文章除特别声明外,均采用 undefined 许可协议。转载请注明来自 Junjie's blog