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 是常常使用的一個生命週期鉤子,包括在參數路由改變時更新數據;

整合後的代碼

相關文章
相關標籤/搜索