分享微信小程式中實現sticky效果的列表頁

一橫發表於2017-11-09

首先什麼是sticky效果呢

具體參照餓了麼APP的商品列表頁(小程式版本發現並未實現)
具體的效果及實現可以檢視一下這個demo => sticky demo on codepen
簡單地說就是標題會有粘黏的效果,向下滑動時跟著列表走,向上滑動到頂部時將會固定在頂部。

但是如果在不考慮相容性的情況下(IOS6以上、 Safari9.1+、 chrome56+)
其實從CSS3開始有一個position屬性sticky就能實現這種效果

{
    position: sticky;
    top: 0;
}複製程式碼

只需要這兩行就能實現,然而...然而相容性那是相當的差,那麼好用的一個屬性為什麼相容性那麼差呢?

那麼餓了麼的商品列表頁在小程式中實有哪些步驟呢?

  1. 利用scroll-view裡的scroll-into-view屬性,具體可檢視小程式的文件
  2. 實現sticky效果(可參照 codepen 的 demo)
  3. 獲取每個scroll-into-view的scrollTop
  4. 監聽scroll的滾動,並改變scroll-into-view

下來來讓我們看一下具體該如何實現

<scroll-view scroll-y class="left-wrapper" id="left">
  <view wx:for="..." bindtap="..."></view>
  <!--這裡是左側的型別選擇-->
</scroll-view>
<scroll-view scroll-y
             class="right-wrapper"
             bindscroll="onScroll"
             scroll-into-view="{{toView}}"
             id="right">
  <view wx:for="{{items}}" wx-for-item="item" class="lists" id="{{item.title}}">
    <view class="type-title" style="{{style}}">
      <!-- 這個就是ticky header部分 -->
      {{item.title}}
    </view>
    <view class="content">
      <view wx:for="{{item.child}}" class="item">
        <!--這裡是需要展示具體的列表項-->
      </view>
    </view>
  </view>
</scroll-view>複製程式碼

左側列表頁沒什麼好講的,無非就是按下某個型別,給上一個checked樣式,然後改變toView(關鍵)的值。
那麼toView是什麼呢?toView的值是和scroll-view裡面你需要跳轉的view的id對應起來的,也就是程式碼中的這個id

<view wx:for="{{items}}" wx-for-item="item" class="lists" id="{{item.title}}">複製程式碼

所以當左側按下對應的按鈕以後,右側的scroll就會跳轉到相應id的scroll-into-view裡面,
其實到目前為止已經實現了sticky header + 跳轉的問題了
但是...
如果滑動右側的滾動條的話,左側的資料如何跟著變化呢?
假如不是小程式的話應該很多人都知道怎麼做,無非就是監聽滾動條,判斷滾動條的位置,然後根據區域去改變左側的選擇。
但是...
小程式如果獲得scroll-into-view在scroll-view裡面的位置呢???
小程式是沒有類似document.getElementById()這種Dom操作的
也沒法使用JQuery的$去快捷地獲取scrollTop的
也不能像vue一樣直接操作$el的
還好小程式在1.4時開放了一個介面wx.createSelectorQuery()

wx.createSelectorQuery()
返回一個SelectorQuery物件例項。可以在這個例項上使用select等方法選擇節點,並使用boundingClientRect等方法選擇需要查詢的資訊。


nodesRef.boundingClientRect([callback])
新增節點的佈局位置的查詢請求,相對於顯示區域,以畫素為單位。其功能類似於DOM的getBoundingClientRect。返回值是nodesRef對應的selectorQuery。
返回的節點資訊中,每個節點的位置用left、right、top、bottom、width、height欄位描述。如果提供了callback回撥函式,在執行selectQuery的exec方法後,節點資訊會在callback中返回。
然後可以通過這個方法拿到所有的scroll-into-view的位置

let query = wepy.createSelectorQuery()
for (let i = 0; i < this.types.length; ++i) {
  let id = this.types[i]
  query.select(`#${id}`).boundingClientRect((rect) => {
    this.scrollTops[id] = rect.top
  }).exec()
}複製程式碼

特別注意
這個操作必須得放在onReady ()的時候去做,否則將無法得到rect屬性
得到這個屬性以後其實就很好操作了,直接上程式碼了

onScroll (event) {
  // 如果是右側的滾動view
  if (event.currentTarget.id === 'right') {
    // 判斷滾動的方向
    let top = event.detail.scrollTop
    this.dir = this.currentTop < top ? 'down' : 'up'
    this.currentTop = top
    // 判斷當前滾動條所在區域,如果不在當前區域則進行跳轉
    if (top > this.scrollTops[this.getNextView()] &&
        this.dir === 'down' &&
        this.checked < this.types.length - 1) {
      this.setChecked(this.checked + 1)
    }
    if (top < this.scrollTops[this.toView] &&
        this.dir === 'up' &&
        this.checked > 0) {
      this.setChecked(this.checked - 1)
    }
  }
}複製程式碼

然後一個簡單的具有sticky效果的商品列表頁跳轉功能就實現了。

  1. scroll-view必須設定高度,否則在IOS上將無法使用scroll-into-view跳轉
  2. 使用createSelectorQuery必須在頁面渲染完成以後

存在問題

  1. scroll會有個慣性運動,這時候按左側的按鈕切換scroll-into-view會和onScroll事件發生一些衝突,測試IOS存在有該問題,希望大神給予些指導意見。

說明

由於採用了wepy構建的小程式,所以在部分程式碼上會和小程式有出入(改過一些),主要是思路。
順便丟個wepy的github
wepy的本意是希望小程式能像vue一樣開發,由於本人一直在用vue做專案,所以用wepy開發小程式會順手一些,但是wepy雖然盡力貼合vue,但在某些設計上存在著一定的問題,但相對來說比直接開發小程式用起來舒服一些。

碎碎念...誰能教教我怎麼優雅地寫好Markdown呢T.T

Github: github.com/lyh2668
AuthBy: lyh2668

相關文章