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

Posted 糊糊糊糊糊了

tags:

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

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

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

先看下面两张图片:

page1
page2

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

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

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列表点透事件处理方法

移动端-点透的解决方法

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

移动端事件点透问题

移动端“点透事件”

移动端--touch事件与点透问题