美文网首页
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