神馬? 瀏覽器不都有自帶的重新整理按鈕麼?
Web 頁為什麼要下(zi)拉(xing)刷(che)新 ?
這裡特指移動端,原因如下:
- 相較於點選右上角重新整理按鈕(還有可能要點兩次,第一次先展開 menu bar,然後才能看到 refresh 按鈕),直接了當地下拉重新整理無疑提供了更好的使用者體驗
- 點選重新整理按鈕同步過載頁面必然存在一定白屏時間,而通過下拉重新整理的邏輯完全可以對於頁面內容進行非同步更新,其體驗毫無疑問更加優秀
- 移動端特有 touch 相關事件,使用者在移動裝置上的觸控、滑動操作頻繁,習慣已經養成,下拉重新整理在提供更好的體驗的同時,絲毫沒有增加使用者的學習成本
- 很多內容 + 社交的業務場景裡面,主頁面的存留時長極高且內容實時性強(如微博、知乎、頭條等), 這些 Native App 已經普遍向使用者提供了這種(下拉重新整理)更新頁面內容的互動方式。作為一個 Web 開發者,如有志於在移動領域讓 Web App 和 Native App 在體驗方面一較高下,那 H5 頁(非同步)下拉重新整理功能也算是不可或缺的一環
- 當然,還可以應對一些特殊場景 … (如 Webview 不提供重新整理按鈕 =,=)
精簡成人話其實就是:被互動逼的
現有哪些輪子可以做到?
目測這個是目前 Github 上最流行的 JS 下拉重新整理元件了(15K stars)。highly customizable and dependency-free!
看使用方法,真的是簡潔明瞭:
1 2 3 4 5 6 |
PullToRefresh.init({ mainElement: 'body', // 想拖動(重新整理)目標元素 onRefresh: function(){ window.location.reload(); // 重新整理動作完後的會執行的回撥函式 } }); |
不過這種單功能元件暴露了 20 個 API,著實有點多了。當然原始碼中有預設的一系列 default setting,個人覺得負擔不大但不必要。
看到一個 React 版本的就簡直看到了親人吶。雖然“只有” 200 stars,但是我們也不能完全以“星”取人啊,果斷開始搗鼓。
1 2 3 4 5 6 7 8 9 10 |
<ReactPullToRefresh onRefresh={this.handleRefresh} className="your-own-class-if-you-want" style={{ textAlign: 'center' }}> <h3>Pull down to refresh</h3> <div>{items}</div> <div>etc.</div> </ReactPullToRefresh> |
iScroll is a high performance, small footprint, dependency free, multi-platform javascript scroller. It works on desktop, mobile and smart TV.(並不是我喜歡複製貼上湊篇幅,而是當我真正用過 TA 以後再回過頭來再看這句話,就不厚道地笑了。平臺通吃的東西在移動端效能真的可以很好麼???)
不過,這是目前為止唯一一個讓我成功完成任務的輪子。 然而還是放棄了,比較可惜,原因彙總如下:
我為什麼不用上面的輪子?
- pulltorefresh.js:很遺憾的是,我在 React 裡面使用,頁面第一次載入 OK,但下拉一次非同步請求資料之後,再次下拉,整個元件原始碼報錯了: “Cannot read property … of undefined …” 找不到目標 dom 元素了 =^=。目測應該和 React 元件更新機制有關,懶得追究,遂放棄
- react-pull-to-refresh:我遇到了一個很多人都遇到過的問題:issues#12: Can’t scroll on mobile。作者的回覆是: this is a due to the default touch-action in hammerjs is ‘none’. A fix could be to set the default to ‘pan-y’. Like this: let hammer = require(‘hammerjs’); hammer.defaults.touchAction = ‘pan-y’;。實力甩鍋 hammerjs。小爺我也懶得繼續死磕下去了,如果一行布丁程式碼就能解決的問題,幹嘛不在自己元件裡面相容掉?果斷棄之,不可惜
- iscroll:效能問題
對於一個 150 個元素的列表,左圖為 iScroll 的 FPS;中間圖為iScroll+lazyload 的 FPS;最右圖為 native scroll +my pulltorefresh (下一段落詳細說)的 FPS,看曲線,別看瞬時幀頻,其實差距挺大但沒有那麼大。
所以,iscroll, 1 秒 3 幀。。。淚流滿面,著實讓我體會了一把頁面卡成 PPT 的感覺。
對了,測試機器也提一句,是我的備用機。為了避免廣告,我只能說是手機界說相聲說的最好的那個胖子旗下的低端千元機。
不得已而為之,自造輪子
要把大象裝冰箱,總共分幾步?
第 1 步:監聽原生 touchstart
事件,記錄滑動起始位置ev.touches[0].pageY
第 2 步:監聽原生 touchmove
事件,記錄最新滑動位置與起始位置ev.touches[0].pageY
的畫素差 pullLength
,如果大於 0 則為向下拖動,此時將pullLength
結果同步設定成目標元素 translateY
值,實現元素跟隨手勢向下移動。當然,元素移動距離是要有上限的。
第 3 步:監聽原生 touchend
事件,如果元素已在頁面頂部,且下pullLength
大於一個閾值(預設 60px),則觸發 callback,並且將 translateY
的取值還原為 0,恢復目標元素位置 (有過渡效果是墜吼滴!)
原始碼很簡單,短短 65 行,請戳 這裡。線上 Demo 請戳 這裡(移動裝置或模擬器檢視)
======= Updated 2018-01-17 ======
React 版 PullToRefresh 新鮮出爐,原始碼請戳 這裡,線上 Demo 請戳 這裡,Doc 請戳 這裡。
====== end ======
這可能是史上最簡陋的 JS 下拉重新整理元件了吧 =,=
生產環境也在使用:大豚廠 IBU H5 首發航班動態求公測~
示例程式碼:
請注意:元件本身,不提供預設的 loading 提示元素!不提供預設的 loading 提示元素!不提供預設的 loading 提示元素! 需要自行設計樣式並正確繫結,如下方程式碼中的 “ptr-instructions”。(提供了真的會有人不改直接用麼???)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
div id = "ptr-instructions"> <!--your loading icon here--> <span class= "ptr-instructions-text"></span> </div> <div id = "main"> <div class = "item">1</div> <div class = "item">2</div> <div class = "item">3</div> <div class = "item">4</div> <div class = "item">5</div> <div class = "item">6</div> <div class = "item">7</div> <div class = "item">8</div> <div class = "item">9</div> <div class = "item">10</div> <div class = "item">11</div> <div class = "item">12</div> <div class = "item">13</div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
pullToRefresh.init({ // required ptrElement: '#ptr-instructions', // 'pull to refresh' intructions element ptrTextElement: '.ptr-instructions-text', // intructions' text element targetElement: '#main', // target element that will be dragged and refreshed // optional instructionsPullToRefresh: 'pull to refresh', // text instructionsReleaseToRefresh: 'Release to refresh', //text instructionsRefreshing: 'refreshing', // text threshold: 60, // minimum distance required to trigger the onPull callback onPull: function(){ // callback fn console.log('onPull fired'); } }); |
Done ~
At last
360 度托馬斯迴旋跪地求星星: Github Repo