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

2019-07-12  本文已影响0人  书中自有颜如玉__

前言

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

以下是使用的前辈代码:

实现原理

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

  • 监听原生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端微信网页有个下拉查看浏览器信息和网页信息的功能,我们加入的下拉刷新与之冲突,导致功能无法实现
问题2:下拉刷新与页面滚动冲突
 <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,手势的向下滑动和左右滑动交替,导致左右滑动时触发了下拉刷新

完整代码如下:

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>
        )
    }
}
上一篇 下一篇

猜你喜欢

热点阅读