React项目实战(三)尝试实现一个拉动刷新组件

上一篇React项目实践(二)一个登陆页面的状态迁移javascript

需求

分析:咱们须要实现两个方向(向下拉动,向上滑动)上的拉动刷新,考虑完成 PullDownRefresh 和 PullUpRefresh 两个组件的编写。思考其中细节:
① 自适应滚动的内容是基于页面仍是基于内部容器;
② 组件是否能够组合使用;
③ 状态提示loading如何设计;
④ loading开始与结束的时机,定时器规定时间?数据加载完结束?css

目前实现:
① 暂时未考虑页面和内部容器同时滑动的状况;
② 经过 props.children 传入须要拉动刷新的内容,两个组件可组合使用;
③ 经过一个 refreshing 状态变量来决定当前是否处于刷新状态;
④ 经过在 componentWillReceiveProps 生命周期钩子中接收新状态/数据来控制 loading 的结束,即 refreshing=falsehtml

依旧是模仿掘金主页的实现 java

预备知识

弄清楚元素的几个属性值(翻一下MDN吧react

  • clientHeight:这个属性是只读属性,对于没有定义CSS或者内联布局盒子的元素为0,不然,它是元素内部的高度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距

clientHeight

  • offsetHeight 是一个DOM属性。它有时被称为一个元素的物理/图形的尺寸,或是一个元素的边界框(border-box)的高度。 git

    offsetheight

  • scrollTop 属性能够获取或设置一个元素的内容垂直滚动的像素数。部到视口可见内容(的顶部)的距离的度量。当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为0 github

    scrollTop

  • scrollHeight:这个只读属性是一个元素内容高度的度量,包括因为溢出致使的视图中不可见内容。(属性将会对值四舍五入取整)。包括元素的padding,但不包括元素的border和margin。scrollHeight也包括 ::before::after这样的伪元素。 web

    scrollHeight
    判断是否滚动到底部:

    element.scrollHeight - element.scrollTop === element.clientHeight promise

  • getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。 浏览器

  • Window​.scrollY:返回文档在垂直方向已滚动的像素值。跨浏览器兼容:

    var supportPageOffset = window.pageXOffset !== undefined;
    var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");
    var y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;
    复制代码
  • Touch​.pageY:触点相对于HTML文档上边沿的的Y坐标. 和 clientY 属性不一样, 这个值是相对于整个html文档的坐标, 和用户滚动位置无关. 所以当存在垂直滚动的偏移时, 这个值包含了垂直滚动的偏移

下拉刷新组件

首先要明确的是下拉刷新组件的触发条件:① 内容垂直滚动的距离应该为0;② 触屏拉动的距离应该大于某个给定的阈值;③ 明确到底是哪一个容器设置了内容自动滚动。下面以我写的具体例子做为参考(没有考虑复杂状况)

  • 在container页面使用 PullDownRefresh 套了须要下拉刷新的内容,这里 scroll_content 设置了 overflow-y:auto
    <div className="main scroll_content">
      <PullDownRefresh onRefresh={this._onRefreshDown} refreshing={this.state.refreshing}> {entryList.map((element, index) => { return <EntryItem item={element} key={index} /> })} </PullDownRefresh> 复制代码
  • 在PullDownRefresh组件中
    <>
      {this.state.refreshing ? <RefreshLoading orient="up" /> : null} <div ref={el => (this.scrollContent = el)} onTouchStart={this.handleTouchStart} onTouchMove={this.handleTouchMove} onTouchEnd={this.handleTouchEnd} > {this.props.children} </div> </> 复制代码
    • 在 handleTouchStart 中咱们记录初始触屏点位置
      handleTouchStart = e => {
        this.setState({
          startPos: e.touches[0].pageY
        })
      }
      复制代码
    • 在 handleTouchMove 中,若是两个触屏点距离大于minHeight,且当前不处于正在刷新状态,且内容滚动高度为0,那么能够开始加载(refreshing: true
      handleTouchMove = e => {
        if (
          this.state.refreshing === false &&
          this.state.parentNode.scrollTop === 0
        ) {
          let _pullHeight = e.touches[0].pageY - this.state.startPos
          if (_pullHeight > this.state.minHeight) {
            this.setState({
              refreshing: true
            })
          }
        }
      }
      复制代码
    • 在 handleTouchEnd 中,咱们进行数据更新请求。由于咱们观察刷新操做是释放以后才开始进行的,并且在move中进行的话可能屡次触发
      handleTouchEnd = e => {
        if (this.state.refreshing) {
          this.props.onRefresh()
        }
      }
      复制代码
    • 若是咱们直接在页面进行请求,能够在promise的finally以后,设置更新结束(refreshing: false);若是咱们是使用外部传入的数据,须要在 componentWillReceiveProps 中设置更新结束(refreshing: false
      componentWillReceiveProps(nextProps) {
        if (nextProps.refreshing !== this.state.refreshing) {
          this.setState({
            refreshing: nextProps.refreshing
          })
        }
      }
      复制代码
    • 获取滚动容器的方法,能够在componentDidMount生命周期中获取
      var scrollContent = ReactDOM.findDOMNode(this.scrollContent).parentNode
      复制代码
    • 基于页面滚动
      var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");
      var scrollContent = isCSS1Compat ? document.documentElement : document.body;
      复制代码

上滑加载组件

原理相似,不过多赘述,判断到达底部的条件是:

scrollContent.scrollHeight - scrollContent.scrollTop - scrollContent.clientHeight === 0
复制代码

浏览器兼容

咱们既想要滚动的功能,又不但愿内部容器显示滚动条,考虑为内部容器添加类 scroll_content 实现

::-webkit-scrollbar {
    /*隐藏滚轮*/
    display: none;
}
.scroll_content {
    -ms-scroll-chaining: chained;
    -ms-overflow-style: none;
    -ms-content-zooming: zoom;
    -ms-scroll-rails: none;
    -ms-content-zoom-limit-min: 100%;
    -ms-content-zoom-limit-max: 500%;
    -ms-scroll-snap-type: proximity;
    -ms-scroll-snap-points-x: snapList(100%, 200%, 300%, 400%, 500%);
    -ms-overflow-style: none;
    overflow: auto;
    -ms-overflow-style: none;
    overflow: -moz-scrollbars-none;
}
复制代码

--
总结:
① 一些功能仍待完善:好比,这里下拉自动回弹使用了动画,考虑释放后再回弹;
② componentWillReceiveProps 是常常使用的一个生命周期钩子,包括在参数路由改变时更新数据;

整合后的代码

相关文章
相关标签/搜索