CSS3 動畫在 iOS 上為什麼會因為頁面滾動也停止?

之前一個項目通過AJAX獲取一個列表, 會像百度貼吧IOS客戶端一樣在載入時有動畫, 但是在IOS上, 動畫會因為頁面滾動(或者拖著頁面不動但也不放)而停止.桌面版不存在該問題.這是什麼原因造成的? 貌似曾經在哪裡看到過什麼解決方法.


這事兒吧其實和前端沒啥關係,倒是和iOS的事件處理機制有關。

iOS最先響應屏幕反應。響應順序依次為Touch——Media——Service——Core架構,當用戶只要觸摸接觸了屏幕之後,系統就會最優先去處理屏幕顯示也就是Touch這個層級,然後才是媒體(Media),服務(Service)以及Core架構。

所以說,當系統接收到Touch事件之後會優先響應,此時會暫停屏幕上包括js、css的渲染。這個時候不光是css動畫不動了,哪怕頁面沒有載入完如果你手指頭還停留在屏幕上那麼頁面也不會繼續載入,直到你的手鬆開。

相反Android響應屏幕排在應用與框架之後。Android的優先順序響應級別則是Application——Framework——Library——Kernal架構,和顯示相關的圖形圖像處理這一部分屬於Library,當你對屏幕操作之後,Android系統首先會激活應用、框架,然後才是屏幕最後是核心架構。

因此Android上面沒有這個問題,但這樣也帶來了卡頓。參考文獻:Android為何比iOS卡?論1G內存的使用_Cellphones 手機_cnBeta.COM


這個也是我一直以來都很在意的小毛病,搜集整理了一些資料,並做了測試,結論是:

iOS(7 以及之前版本)瀏覽器在頁面「慣性滾動」期間,暫停了 CSS3 animation / JS 大多數事件,在「觸摸滾動」期間,暫停了 CSS3 animation / JS scroll 事件。不過在 iOS 8 里,終於解決了這個問題 [1]。

測試方法是使用 setInterval 進行計數,可以發現滾動時確實暫停了事件隊列。有些網頁在滾動的時候,載入中的圖片不渲染,停止滾動的瞬間即載入出來,也是這個原因。

造成此問題的原因是基於效率的考慮,蘋果當然是不允許自己引以為傲的慣性滾動出現卡頓和閃爍的。

解決辦法有兩個,各有瑕疵:

  1. 不要使用 scroll 事件(此事件會被暫停),而是採用 touchmove(此事件會在用戶觸屏滾動的時候不斷觸發)。瑕疵是,在結束觸屏後慣性滾動的時間裡,touchmove 無法被觸發了(scroll 當然也不行);

  2. 基於上一種方法,在所有的 touchmove 事件中,強行 preventDefault 阻止掉事件,然後根據 event.pageY 來手工設置所滑動元素的 scrollTop 值。當然,這樣一來就沒有了慣性滾動。

    你也可以在 touchend 之後,手工模擬慣性滾動,計算速度以及速度衰減,可以參考各種各樣的滾動插件。

----

附:

[1] Why the Scroll Event Change in iOS 8 is a Big Deal -Telerik Developer Network

也可以看看我之前寫的小工具,就為了解決此問題大費周折:https://quietshu.github.io/list-label/demo.html


其實吧,這個問題我覺得歸根結底應該是oc runloop帶來的坑。端上動畫的執行是在NSDefaultRunLoopModel裡面的,當滑動時model會切換到UITrackingRunloopModel,從而因為model改變而導致動畫停止,但端上的解決辦法倒是有幾個。放在h5上排查就更困難了,遇到這樣的問題,個人覺得前端解決應該都屬於治標不治本的方式,避而為之,徹底解決還得瀏覽器去兼容!


為了避免性能問題,js 在滑動時是暫停執行的,沒有好的辦法,CSS3 Animation 做的倒是沒有試過,也許行。


推薦閱讀:

現在的 CSS、JS 效果和幾年前火爆的 Flash 有什麼區別?
CSS 那麼多屬性,而且每個屬性都有多個值怎麼記?
Google+ 相冊中的圖片自動無縫對齊是怎麼做到的?
響應式設計怎麼讓圖片自適應?
如何看待 CSS 中 BEM 的命名方式?

TAG:前端開發 | CSS3 | Safari | iOS8 |