• 轻松实现H5页面下拉刷新:滑动触发、高度提示与数据刷新全攻略


    前段时间在做小程序到H5的迁移,其中小程序中下拉刷新的功能引起了产品的注意。他说到,哎,我们迁移后的H5页面怎么没有下拉刷新,于是乎,我就急忙将这部分的内容给填上。

    本来是计划使用成熟的组件库来实现,尝试之后发现这些组件和我们H5页面的其他逻辑有冲突(H5还有吸顶、锚点、滑动高亮、横向滚动),小小H5页面上承载了太多的功能,兼容起来非常麻烦,想着下拉刷新功能也不复杂,干脆我自己写一个好了。

    流程图示

    正常数据展示状态 --> 手指触摸屏幕下拉 --> 手指松开 --> 数据获取 --> 恢复正常数据展示状态

    功能梳理

    要实现这个功能,主要分为两部分。

    监听手指触摸事件

    通过监听事件,我们可以得知以下的数据

    • 手指滑动的时机(手指开始触摸,结束触摸时间)
    • 滑动方向(是横向滑动还是纵向滑动)
    • 操作轨迹(手指操作从下往上还是从上往下滑动)
    • 是否首屏(如果非首屏进行滑动时是正常滑动操作)
      只有在向下滑动首屏非加载状态纵向滚动并且有高度时,才能进行上述刷新流程。

    css 和 提示文案

    • 手指按住屏幕由上往下滑动未松开时,展示滑动的高度和提示【释放刷新】文案
    • 手指松开后高度回弹,显示【数据更新中】文案
    • 数据请求接口成功后,显示【更新成功】文案,loading 内容和图标缓缓消失

    具体实现

    触摸的步骤可以分为: 手指按下(开始触摸)、手指移动不离开屏幕(触摸中)、手指离开屏幕(触摸结束),正好对应着三个 js 原生事件,touchstarttouchmovetouchend

    触摸事件执行时机

    touchstart 和 touchmove 在一次触摸流程只会执行一次,标志着开始和结束,但是 touchmove 不一样,只要你的手指还在屏幕上滑动没有松开,就会一直执行。如下图的输出的执行次数一样。

    下拉元素绑定

    首先需要给需要设置下拉刷新的区域绑定上这些事件,对于我们业务场景来说,头部区域无论你如何操作,都需要保留展示的,那么我们只需要将事件绑定到下方开始显示下拉刷新的区域。

    // html元素
    
    "refreshWrap"> {/* 下拉时文字提示 */}
    `pullDownContent`} style={{ height: pullDownHeight }}> {loading ? "" : "释放刷新"}
    {/* 加载时动画 */}
    `loadingFlex ${loading ? "" : "loadingHidden"}`}> <div className="flexCenter"> <div className="loadingRing" /> <div className="loadingText"> {loading ? "数据更新中..." : "更新成功"} div> div>
    <div className="middleArea">刷新区域下方内容区域div>
    // js 绑定 const pullDownClassName = ".refreshWrap"; bindPullDown() { const pulldownElement = document.querySelector(pullDownClassName); pulldownElement.addEventListener("touchstart", this.bindTouchstart); pulldownElement.addEventListener("touchmove", this.bindTouchMove); pulldownElement.addEventListener("touchend", this.bindTouched); }

    触摸开始

    手指触摸到屏幕的逻辑非常简单,使用 startTouch 对象来记录触摸的位置,包含 x 、y 轴。

    bindTouchstart = (event) => {
        this.startTouch = event.touches[0];
      };
    

    触摸中

    用户触摸中需要给他一个反馈,随着下拉的距离,屏幕上圈出的下拉区域会随之变大(下拉展示的区域会设置一个最大高度,如果能无限扩大展示不好看)

    endTouch 来保存触摸中的坐标值,因为触摸中的事件会执行多次,所以 endTouch 也会不断的更新,用来更新下拉时滑动的高度。

     bindTouchMove = (event) => {
        const { loading } = this.state;
        this.endTouch = event.touches[0];
        if (!loading && this.isInOneScreenPull() && this.isVerticalSliding()) {
          const pullDownHeight = this.getPullDownHeight();
          this.setState({
            pullDownHeight,
          });
        }
      };
    

    根据 endTouch 的值可以判断出滑动距离、横向还是纵向滑动,滑动的高度、再获取滑动元素是否在首屏。

    // 判断滑动的距离
    calcDeltaY = () => Math.abs(this.endTouch.pageY - this.startTouch.pageY);
    
    // 判断是否纵向滚动
    isVerticalSliding = () => {
      const deltaY = this.calcDeltaY();
      const deltaX = Math.abs(this.endTouch.pageX - this.startTouch.pageX);
      if (deltaY > deltaX && deltaY > 50) return true;
    };
    
    // 下拉展示高度最多展示为100,不能让加载区域无限制的扩大
    getPullDownHeight = () => {
      const deltaY = this.calcDeltaY();
      return Math.min(deltaY, 100);
    };
    
    // 是否在首屏
    isInOneScreenPull() {
      const pulldownElement = document.querySelector(pullDownClassName);
      return pulldownElement.scrollTop <= 0;
    }
    

    触摸结束

    触摸结束时,将 pulldownHeight 设置为0,异步加载数据,加载数据时设置变量 loading 表示开始更新、结束更新,防止不停的下拉刷新调用接口。

    bindTouched = (e) => {
      const { loading, pullDownHeight } = this.state;
    
      // 首屏、非加载状态、纵向滚动有高度时
      if (!loading && pullDownHeight) {
        this.setState({
          pullDownHeight: 0,
        });
    
        this.getData();
    
        // 重置触摸Y轴坐标点
        this.startTouch = {};
        this.endTouch = {};
      }
    };
    

    平滑过渡动画

    当下拉高度发生变化时,直接修改高度效果会比较生硬,使用 css transition 属性进行平滑过渡、animation 设置动画缓慢进入/消失。

    .pullDownContent {
      display: flex;
      align-items: flex-end;
      justify-content: center;
      font-size: 12px;
      color: rgba(0, 0, 0, 0.25);
      margin: auto;
      transition: height 0.3s ease-out; /* 平滑过渡效果 */
      overflow: hidden;
    }
    
    .loadingHidden {
      animation: shrinkHeight 1s forwards;
    }
    
    @keyframes shrinkHeight {
      100% {
        height: 0;
        opacity: 0;
        overflow: hidden;
      }
    }
    

    完整代码

    以上便是滑动触发、高度提示、数据刷新的下拉刷新功能解析,完整代码我放在了 github 上,戳 drop-down-refresh 可查看,欢迎大家点个 star~

  • 相关阅读:
    MySQL 45 讲 | 16 “order by”是怎么工作的?
    七夕特别篇 | 浪漫的Bug
    循环神经网络介绍(RNN)
    计算机毕业设计Java网上课程资源管理系统(源码+系统+mysql数据库+Lw文档)
    .NET Core Configuration 配置项知识点一网打尽!
    第一周 一起走进摄影世界
    elementUi自定义表头,根据判断显示不同的表头
    AWS无服务器 应用程序开发—第一章 目录
    LeetCode算法题解(回溯)|39. 组合总和、40. 组合总和 II、131. 分割回文串
    淘宝退货退款测试用例
  • 原文地址:https://www.cnblogs.com/vigourice/p/18250533