在微信裏面瀏覽頁面的時候,有一個很管用的方法能夠區分這個頁面是原生的仍是H5形式的。隨便打開一個頁面,用力往下扯的時候,若是頁面上方出現了「黑底」,黑底上有一行諸如網頁由game.weixin.qq.com提供
的文字,就代表這個頁面是H5形式的。這帶來的問題是,若是一個頁面可滾動區域很小,隨便一拉,頁面下方出現了黑底,而後你又輕輕往上一拉,上面的黑底又出來了,我的表示很是難受啊!
因而乎,折騰了一番,寫了一個簡單的組件來實現禁止這種拉動頁面出現黑底的特性。php
首先須要說明的是,因爲Android和IOS的webview存在差別,這個組件對於IOS是比較友好的,安卓下並不能作到完美避免,下面一一分析。node
智能手機和平板電腦一類的移動設備一般會有一個電容式觸摸屏(capacitive touch-sensitive screen),以捕捉用戶的手指所作的交互。有三種在規範中列出並得到跨移動設備普遍實現的基本觸摸事件:ios
其中每個觸摸事件都會包含三個觸摸列表:git
這些列表由包含了觸摸信息的對象組成:github
在這個組件中,咱們只須要用到e.touches[0].clientY
屬性就夠了:在開始觸摸的時候,記錄觸摸點的起始位置,在手指移動過程當中,不斷獲取最新的clientY
,與起始位置的clientY
比較,就能獲知拉動頁面的方向。web
這三個屬性是用來計算元素處於頁面的哪一個位置的,考慮下面兩種狀況:chrome
經過上面兩點,咱們已經知道要達到禁止出現黑底的效果,努力的方向是在知道滑動方向的條件下,在與height相關的屬性達到臨界值的時候及時阻止事件冒泡。只有三種簡單的狀況:移動web開發
總結起來以下表(1爲容許,0爲禁止,高位表示向上方向,低位表示向下方向)瀏覽器
能夠拉的方向(height) | 拉的方向(touch) | 可否繼續拉 |
---|---|---|
00 | 10 | 0 |
00 | 01 | 0 |
01 | 10 | 0 |
01 | 01 | 1 |
10 | 10 | 1 |
10 | 01 | 0 |
從表中咱們能夠得出一個結論是,可否在該方向上繼續拉其實就是對兩種條件作一個&
運算!話很少說,上核心源碼微信
// 防止過度拉動 preventMove: function(e) { // 高位表示向上滾動, 底位表示向下滾動: 1允許 0禁止 var status = '11', e = e || window.event, // 使用 || 運算取得event對象 ele = this, currentY = e.touches[0].clientY, startY = startMoveYmap[ele.id], scrollTop = ele.scrollTop, offsetHeight = ele.offsetHeight, scrollHeight = ele.scrollHeight; if (scrollTop === 0) { // 若是內容小於容器則同時禁止上下滾動 status = offsetHeight >= scrollHeight ? '00' : '01'; } else if (scrollTop + offsetHeight >= scrollHeight) { // 已經滾到底部了只能向上滾動 status = '10'; } if (status != '11') { // 判斷當前的滾動方向 var direction = currentY - startY > 0 ? '10' : '01'; // console.log(direction); // 操做方向和當前容許狀態求與運算,運算結果爲0,就說明不容許該方向滾動,則禁止默認事件,阻止滾動 if (!(parseInt(status, 2) & parseInt(direction, 2))) { e.preventDefault(); e.stopPropagation(); return; } } },
開始的時候,我覺得上面的代碼就萬事大吉了,通過實踐和摸索,結論是:簡直是天真。
異步的概念之因此首先在Web2.0中火起來,是由於在瀏覽器中JavaScript在單線程上執行,並且它還與UI渲染共用一個UI線程。這意味着JavaScript在執行的時候UI渲染和響應是處於停滯狀態的。 ----《深刻淺出nodejs》
這意味這什麼呢?當咱們的UI線程在進行渲染的時候,JavaScript代碼也是處於停滯狀態的!不信的話能夠在一個能夠滑動的頁面上引入下面這段代碼:
var count = 0; setInterval(functiong() { console.log(++count); }, 100);
刷新頁面的時候,控制檯會一直打印不斷變大的數字,可是隻要你用手指開始拖動頁面,打印終止,等你把手放開的時候,打印繼續,並且數字會承接打印中止前那個數字。也就是UI在渲染的時候,js保存了狀態,在UI渲染中止的時候,js又能夠繼續運行。
這對咱們的組件帶來的影響是什麼呢?幾乎是毀滅性的,場景以下:
在尋求最終的解決方案以前,咱們先來討論一下overflow這個屬性。
傳統 pc 端中,子容器高度超出父容器高度,一般使用 overflow:auto 可出現滾動條拖動顯示溢出的內容,而移動web開發中,因爲瀏覽器廠商的系統不一樣、版本不一樣,致使有部分機型不支持對彈性滾動,從而在開發中製造了所謂的 BUG。
從本人這兩個月移動Web實踐的經驗來看,微信的webview裏面overflow: scroll
和overflow: auto
的滑動效果不管是在安卓仍是IOS下的體驗都很通常,有明顯的卡頓現象,在安卓下面還會出現滑動過快的時候在頁面停下來以後滾動條才閃到相應位置的現象。
在IOS5以後,出現了一個新的屬性: -webkit-overflow-scrolling
,用來控制元素在移動設備上是否使用滾動回彈效果。它的取值有兩個:
實驗代表,在IOS下,對一個元素設置了overflow:scroll
的基礎上再添加-webkit-overflow-scrolling: touch;
會讓滑動又如絲般順滑。
這個屬性和咱們解決以前的問題有什麼聯繫呢?祕密就在這彈性滾動效果。
頁面中body
元素的內容超過一屏,頁面能夠往下滑動(手指往上拉)。按照咱們組件的設定,手指開始的時候是不能往下拉的,可是若是手指的方向是先往上拉一小段,在手指不離開屏幕的基礎上再往下拉,當頁面拉到頂部的時候,會相繼出現黑底,由於UI在渲染,js無法去阻止事件冒泡。
如今咱們把組件的做用元素設定爲body內最外圍的div元素,而且給這個元素添加兩個CSS屬性overflow:scroll
和-webkit-overflow-scrolling: touch;
,那麼上面的場景就會變成:
頁面中body內最外圍的div標籤內容超過一屏,其內容能夠往下滑動(手指往上拉)。按照咱們組件的設定,手指開始的時候是不能往下拉的。和以前同樣,手指先往上拉一小段,在手指不離開該元素的基礎上再往下拉,當元素內容到頂以後,由於UI在渲染,js本插不上手,可是該元素內部的內容設置了彈性滾動,要實現彈性滾動,基本要求就是這個div容器是不動的,能夠理解成由於彈性滾動,自動就禁止掉了事件冒泡,也就不會出現黑底了。
確定有人要問了,既然自動禁止了事件冒泡,那還要這個組件何用?固然有用,會禁止掉事件冒泡的前提是內容在滾動。依照上面的場景,若是一開始手指直接往下拉,沒有組件的限制,仍是會露出黑底,於是,要實現比較好的效果,是須要這兩個屬性和組件配合的。
至於安卓嘛,由於沒有這個屬性,暫時只能一邊涼快去吧。
多說無用,看源碼吧:
https://github.com/yuanzm/preventoverscrolljs