美文网首页
H5下拉刷新(解决与android微信端下拉冲突、与页面滚动冲突

H5下拉刷新(解决与android微信端下拉冲突、与页面滚动冲突

作者: 书中自有颜如玉__ | 来源:发表于2019-07-12 09:52 被阅读0次

    前言

    因为微信公众号第三方网页的项目中有下拉刷新的需求,第一反应就是试试使用H5自己写一个,网上搜了一下确实很多前辈都写过,所以直接参考;结果使用到项目中,问题频出:

    • 问题1:android端微信网页有个下拉查看浏览器信息和网页信息的功能,我们加入的下拉刷新与之冲突,导致功能无法实现。
    • 问题2:下拉刷新与页面滚动冲突。
    • 问题3:无论在android还是ios,手势的向下滑动和左右滑动交替,导致左右滑动时触发了下拉刷新。

    以下是使用的前辈代码:

    实现原理

    实现下拉刷新主要分为三步:

    • 监听原生touchstart事件,记录其初始位置的值,e.touches[0].pageY;
    • 监听原生touchmove事件,记录并计算当前滑动的位置值与初始位置值的差值,大于0表示向下拉动,并借助CSS3的translateY属性使元素跟随手势向下滑动对应的差值,同时也应设置一个允许滑动的最大值;
    • 监听原生touchend事件,若此时元素滑动达到最大值,则触发callback,同时将translateY重设为0,元素回到初始位置。
    <main>
       <p class="refreshText"></p>
        <ul id="refreshContainer">
            <li>111</li>
            <li>222</li>
           <li>333</li>
            <li>444</li>
            <li>555</li>
            ...
        </ul>
    </main>
    
    (function(window) {
        var _element = document.getElementById('refreshContainer'),
          _refreshText = document.querySelector('.refreshText'),
          _startPos = 0,
          _transitionHeight = 0;
    
        _element.addEventListener('touchstart', function(e) {
            console.log('初始位置:', e.touches[0].pageY);
            _startPos = e.touches[0].pageY;
            _element.style.position = 'relative';
            _element.style.transition = 'transform 0s';
        }, false);
    
        _element.addEventListener('touchmove', function(e) {
            console.log('当前位置:', e.touches[0].pageY);
            _transitionHeight = e.touches[0].pageY - _startPos;
    
            if (_transitionHeight > 0 && _transitionHeight < 60) {
                _refreshText.innerText = '下拉刷新';
                _element.style.transform = 'translateY('+_transitionHeight+'px)';
    
                if (_transitionHeight > 55) {
                  _refreshText.innerText = '释放更新';
                }
            }                
        }, false);
    
        _element.addEventListener('touchend', function(e) {
            _element.style.transition = 'transform 0.5s ease 1s';
            _element.style.transform = 'translateY(0px)';
            _refreshText.innerText = '更新中...';
    
            // todo...
    
        }, false);
    })(window);
    

    我开始解决以上三个问题,

    问题1:android端微信网页有个下拉查看浏览器信息和网页信息的功能,我们加入的下拉刷新与之冲突,导致功能无法实现
    • 既然与微信自带的下拉刷新冲突,我的思路就是取消下拉事件的默认动作,阻止事件向外传播,自然想到了preventDefault();我高兴地在 id="refreshContainer"元素的touchstart事件、touchmove事件、touchend事件的执行函数中都加入了e.preventDefault();这样的确生效了,解决了问题1;
    • 不幸的是,又引入了新的问题,在下拉刷新内容块内的click事件失效了;把刚刚加入的e.preventDefault()注释掉,click事件是正常的;基本能确定两者冲突;思来想去,touchstart、touchend不就是一个完整的click嘛?那就把他们两个的e.preventDefault()注释掉;果然我的猜想是对的,click生效了,至此,问题1圆满解决;
    问题2:下拉刷新与页面滚动冲突
    • 首先由于解决问题1,touchmove事件引入e.preventDefault(),导致touchmove取消默认动作,也就是滚动区域无法滚动,我的思路是将e.preventDefault()提升到其父元素 id="myPage"处理。监听滚动条的位置,如果位于滚动元素顶部,并且向下滑动时,触发下拉刷新,并且取消事件的默认动作;如果不是位于顶部,自然不触发下拉刷新。注:RefreshContainer-scroll是我为页面滚动元素命名的id。
     <div id="myPage">
          <p className="refreshText" hidden={hide}>{text}</p>
          <div id="refreshContainer">
            {this.props.children}
          </div>
    </div>
    
        wrapBoxHandle = () => { // 兼容Android,解决下拉触发微信网页的下拉。
            let that = this;
            let _element = document.getElementById('myPage'),
                _startPos = 0,
                _transitionHeight = 0;
            _element.addEventListener('touchstart', function (e) {
                _startPos = e.touches[0].pageY;
            }, false);
            _element.addEventListener('touchmove', function (e) {
                _transitionHeight = e.touches[0].pageY - _startPos;
                if (that.getScrollTop() === 0 && _transitionHeight > 0) { // 位于滚动元素顶部并且向下滑动时
                    e.preventDefault(); // 取消事件的默认动作。可屏蔽android端微信网页自带的拉下动作。
                }
            }, false);
        }
        getScrollTop = () => { // 解决下拉刷新与页面滚动的冲突;获取当前页滚动元素的滚动条位置,保证在滚动条位于顶部时才会
                                            //执行下拉刷新
            return document.getElementById('RefreshContainer-scroll').scrollTop;
        }
    
          _element.addEventListener('touchmove', function (e) {
                if (that.state.transitionWidth === 0) { // 阻止其频繁变动,保证能进入【下拉刷新】就能继续【释放更新】
                    _transitionWidth = Math.abs(e.touches[0].pageX - _startX); // 防止横向滑动
                }
                _transitionHeight = e.touches[0].pageY - _startPos;
    
                if (that.getScrollTop() === 0 && _transitionWidth < 10
                    && _transitionHeight > 0 && _transitionHeight < 60) {
                    that.setState({
                        hide: false,
                        text: '下拉刷新',
                        transitionWidth: _transitionWidth // 保证能进入【下拉刷新】就能继续【释放更新】
                    });
                    if (_transitionHeight > 30) {
                        _element.style.transform = `translateY(30px)`;
                        that.setState({
                            text: '释放更新',
                            flage: true,
                            transitionWidth: 0 // 一个流程走完,重置
                        });
                    }
                }
            }, false);
    
    问题3:无论在android还是ios,手势的向下滑动和左右滑动交替,导致左右滑动时触发了下拉刷新
    • 我的思路是监听左右滑动的幅度,判断是左右滑动还是上下滑动,在touchmove中给变量赋值
      _transitionWidth = Math.abs(e.touches[0].pageX - _startX);
      如果 _transitionWidth < 10则我认为是在下滑(这个值自己定的,你想定20也没问题,就是判定一个幅度),否则就是左右滑动,左右滑动的话我就不触发下拉刷新的动作。

    完整代码如下:

    import * as React from "react";
    import './index.less';
    
    interface Props {
        reload: Function
    }
    
    export default class RefreshContainer extends React.PureComponent<Props, any> {
        constructor(props) {
            super(props);
            this.state = {
                hide: true, // 提示元素隐藏
                text: '', // 提示信息
                flage: false, // 执行刷新函数标志位
                transitionWidth: 0 // 保证能进入【下拉刷新】就能继续【释放更新】的变量
            }
        }
        componentDidMount = () => {
            this.wrapBoxHandle();
            this.refresh();
        }
    
        refresh = () => { // 下拉刷新功能函数
            let that = this;
            let _element = document.getElementById('refreshContainer'),
                _startPos = 0,
                _startX = 0,
                _transitionWidth = 0,
                _transitionHeight = 0;
    
            _element.addEventListener('touchstart', function (e) {
                _startPos = e.touches[0].pageY;
                _startX = e.touches[0].pageX;
                _element.style.position = 'relative';
                _element.style.transition = 'transform 0s';
                that.setState({
                    flage: false
                });
            }, false);
    
            _element.addEventListener('touchmove', function (e) {
                if (that.state.transitionWidth === 0) { // 阻止其频繁变动,保证能进入【下拉刷新】就能继续【释放更新】
                    _transitionWidth = Math.abs(e.touches[0].pageX - _startX); // 防止横向滑动
                }
                _transitionHeight = e.touches[0].pageY - _startPos;
    
                if (that.getScrollTop() === 0 && _transitionWidth < 10
                    && _transitionHeight > 0 && _transitionHeight < 60) {
                    that.setState({
                        hide: false,
                        text: '下拉刷新',
                        transitionWidth: _transitionWidth // 保证能进入【下拉刷新】就能继续【释放更新】
                    });
                    if (_transitionHeight > 30) {
                        _element.style.transform = `translateY(30px)`;
                        that.setState({
                            text: '释放更新',
                            flage: true,
                            transitionWidth: 0 // 一个流程走完,重置
                        });
                    }
                }
            }, false);
    
            _element.addEventListener('touchend', function (e) {
                _element.style.transition = 'transform 0.5s';
                _element.style.transform = 'translateY(0px)';
                if (that.state.flage) { // 标志下滑的值达到刷新,就执行刷新函数
                    that.setState({
                        text: '正在刷新...'
                    });
                    that.props.reload().then(() => {
                        that.setState({
                            hide: true
                        });
                    });
                } else {
                    that.setState({
                        hide: true
                    });
                }
            }, false);
        }
    
        wrapBoxHandle = () => { // 兼容Android,解决下拉触发微信网页的下拉。
            let that = this;
            let _element = document.getElementById('myPage'),
                _startPos = 0,
                _transitionHeight = 0;
            _element.addEventListener('touchstart', function (e) {
                _startPos = e.touches[0].pageY;
            }, false);
            _element.addEventListener('touchmove', function (e) {
                _transitionHeight = e.touches[0].pageY - _startPos;
                if (that.getScrollTop() === 0 && _transitionHeight > 0) { // 位于滚动元素顶部并且向下滑动时
                    e.preventDefault(); // 取消事件的默认动作。可屏蔽android端微信网页自带的拉下动作。
                }
            }, false);
        }
    
        getScrollTop = () => { // 解决下拉刷新与页面滚动的冲突;获取当前页滚动元素的滚动条位置,保证在滚动条位于顶部时才会执行下拉刷新
            return document.getElementById('RefreshContainer-scroll').scrollTop;
        }
    
        render() {
            const { text, hide } = this.state;
    
            return (
                <div id="myPage">
                    <p className="refreshText" hidden={hide}>{text}</p>
                    <div id="refreshContainer">
                        {this.props.children}
                    </div>
                </div>
            )
        }
    }
    

    相关文章

      网友评论

          本文标题:H5下拉刷新(解决与android微信端下拉冲突、与页面滚动冲突

          本文链接:https://www.haomeiwen.com/subject/xbgskctx.html