移动端APP列表点透事件处理方法

Posted 萌萌的卡洛奇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移动端APP列表点透事件处理方法相关的知识,希望对你有一定的参考价值。

关于点透事件这里不再赘述,如果不清楚的可以上网搜一搜,或者看小火柴的这篇文章

这里是自己在做移动端时,在列表滑动的时候,遇到的点透问题。出现这个问题的来由是因为在转场的时候,各个手机的转场效果不一样,有的比较好,但是在有些低端机上,转场显得有点卡,于是就把过渡效果去掉了,因此就是直接的路由切换。【具体事件具体分析,可能我遇到的问题并不适合你,这里只是贴出来共享】

先看下面两张图片:


点击列表页的按钮会切换到下一个页面,但是在下一个页面上的每一个条目都是可以点击的,这时就会触发了下一个页面的弹窗,事实上我们并不想直接显示这个弹窗,而是要等待用户点击。

你可能在项目中的列表页写了如下的一段代码:

render() 
    return (
        <ul className="list">
             
                list.map((l, index) => 
                    return (
                        <li key= `list$index`  onClick= () => doSomething() >
                             `item$index` 
                        </li>
                    )
                )  
            
        </ul>
    );

在一个列表中的每个项目上绑定了点击事件,但是当点击之后切换到下一页。当时移动端的点击事件都会有300ms的延迟,因此在切换了页面之后,浏览器会再次判断点击的行为,此时如果下一个页面都有可以触发点击的元素,这时候就触发了下一个页面的点击行为。

于是,你可能会这么做,将onClick事件换成onTouchEnd事件

<li 
    key= `list$index`  
    onTouchStart=  event => event.preventDefault() 
    onTouchEnd= () => doSomething() >
     `item$index` 
</li>

但是,每次滑动的时候,其实你也触发了onTouchEnd事件,于是每次滑动你都会点击进入到下一页。于是你又想,加上一个onTouchStart事件,然后阻止掉默认事件,尼玛发现滑都滑不动了。

因此针对常用的几种解决点透事件的方法,我想了几种解决方案:

方案一:自己模拟Tap事件

大致的代码如下:

var list = document.querySelector('#list');
var dragState = ;

list.addEventListener('touchstart', function(event) 
    var touch = event.touches[0];
    dragState.startTime = new Date();
    dragState.startLeft = touch.pageX;
    dragState.startTop = touch.pageY;
    dragState.startTopAbsolute = touch.clientY;
);

list.addEventListener('touchmove', function(event) 
    var touch = event.touches[0];
    dragState.currentLeft = touch.pageX;
    dragState.currentTop = touch.pageY;
    dragState.currentTopAbsolute = touch.clientY;
);

list.addEventListener('touchend', function() 
    var dragDuration = new Date() - dragState.startTime;
    var offsetLeft = dragState.currentLeft - dragState.startLeft;
    var offsetTop = dragState.currentTop - dragState.startTop;

    if (dragDuration < 300) 
        var fireTap = Math.abs(offsetLeft) < 10 && Math.abs(offsetTop < 10);
        if (isNaN(offsetLeft) || isNaN(offsetTop)) 
            fireTap = true;
        
        if (fireTap) 
            alert('tap');
        
    
    dragState = ;
);

判断水平位移差和垂直位移差都小于10像素,并且touchstarttouchend的时间差小于300ms时,即认为触发了Tap事件。

方案二:加入转场动画

既然是因为转场动画在某些机型上比较卡的原因造成的,那么如果不是太考虑性能的话,可以加上转场动画,关于react中的转场动画,时间大概在300ms就好,可以看我之前对于转场代码的研究:react-css3-transition-group

方案三:在目标页面加入遮罩层

在目标页面加上一层透明的弹层,使上一个页面的点击在此弹层上失效,具体做法为使用一个高阶组件,在高阶组件中添加一个定时器,在每个页面加载的时候生成一个弹层,400ms之后消失弹层即可。

import React from 'react';

const styles = 
    modal: 
        width: '100%',
        height: '100%',
        position: 'absolute',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        zIndex: 20,
        backgroundColor: 'transparent'
    
;

const ComponentWrapper = MyComponent => 

    const ComponentTemplate = React.createClass(
        getInitialState() 
            return 
                modal: true
            ;
        ,

        componentDidMount() 
            this.modalInter = setTimeout(() => 
                this.setState( modal: false );
            , 400);
        ,

        componentWillUnmount() 
            this.hideTips();
            this.modalInter && clearTimeout(this.modalInter);
        ,

        render() 
            return (
                <Page style= this.props.style >
                    <MyComponent  ...this.props  container= this  />
                     this.state.modal && <div style= styles.modal ></div> 
                </Page>
            )
        
    );

    return ComponentTemplate;
;

export default ComponentWrapper;

另外在0.13.3版本的react还支持mixins的时候,可以添加如下代码:

import React from 'react';
import ReactDOM from 'react-dom';

const styles = 
    modal: 
        width: '100%',
        height: '100%',
        position: 'absolute',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        zIndex: 20,
        backgroundColor: 'transparent'
    
;

const clickThroughMixin = 
    getInitialState() 
        return 
            clickThroughModal: true
        
    ,

    componentDidMount() 
        this._renderModal();
        this.modalInter = setTimeout(() => 
            this._modalTarget && ReactDOM.unmountComponentAtNode(this._modalTarget);
            this._modalTarget.remove();
        , 400);
    ,

    componentWillUnmount() 
        this.modalInter && clearTimeout(this.modalInter);
    ,

    _renderModal() 
        if (!this._modalTarget) 
            this._modalTarget = document.createElement('div');
            this._container = this._getContainerDOMNode().appendChild(this._modalTarget);
            ReactDOM.unstable_renderSubtreeIntoContainer(
                this, (<div style= styles.modal ></div>), this._modalTarget
            );
        
    ,

    _getContainerDOMNode() 
        const node = ReactDOM.findDOMNode(this), body = document.body;
        return node ? node.parentNode || body : body;
    
;

export default clickThroughMixin;

以上是关于移动端APP列表点透事件处理方法的主要内容,如果未能解决你的问题,请参考以下文章

移动端APP列表点透事件处理方法

移动端-点透的解决方法

移动端-点透问题 巧妙解决

移动端WEB开发,click,touch,tap事件浅析

移动端WEB开发,click,touch,tap事件浅析

移动端WEB开发,click,touch,tap事件浅析