記錄一下對swiper4.x.js在H5單頁中的滑動優化

應用場景

僅僅應用於單頁應用的滑動操做,用swiper4.x接管頁面的滾動操做。用來支持頂部和尾部的回彈效果,進一步來支持常見那種下拉刷新動畫效果。不適用於輪播圖那種應用場景。javascript

雖然只是針對swiper4.x,但相關原理,在別的框架中也是有參考意義的。css

出現的問題

1、慣性動畫不會在觸摸時中止

快速滑動頁面,手離開屏幕時產生的慣性動畫還在運行時,此時觸摸屏幕,動畫不會中止。致使連續快速滑動頁面,看起來有跳來跳去的感受。java

2、快速滑動手勢大機率識別成慢滑動手勢

連續快速多滑幾下,有那麼一、2下明顯可以感知到滑動慣性動畫變慢的情景,給人的感受就是滑動不流暢。小程序

3、慣性動畫時長不合理

不論是很慢的滑動,仍是很快的滑動,慣性動畫時長只能設置一個固定值。慢慢移動鬆手後也會有一個很長的動畫,快速滑的時候動畫又有點短,綜合看起來給人很卡的感受。bash

解決手段

除了第一個問題,另外兩個問題不修改swiper4.x源碼彷佛沒法克服。app

1、解決慣性動畫不會在觸摸時中止的問題

滑動時手離開屏幕時產生的慣性動畫是css動畫,並未直接提供中止動畫的方法。動畫還在運行時若是觸摸屏幕,不改源代碼,那咱們就監聽一下touchStart事件。框架

setTranslate(translate)能夠移除這個動畫css,須要提供頁面當前的滾動位置。用getTranslate()就好了:優化

這與經過屬性mySwiper.translate 獲取到的數值稍有不一樣,即便是在過渡時(animating)也能獲取到,然後者精度較高動畫

解決方案代碼

//在touchStart事件中執行
mySwiper.setTranslate(mySwiper.getTranslate());
複製代碼

2、解決快速滑動手勢大機率識別成慢滑動手勢問題

這個必須修改源代碼才能解決,問題出在計算慣性動畫起始速度時的計算參數精度不足。ui

原代碼

if (params.freeModeMomentum) {
        if (data.velocities.length > 1) {
          var lastMoveEvent = data.velocities.pop();
          var velocityEvent = data.velocities.pop(); //此處取值方式會致使精度不夠

          var distance = lastMoveEvent.position - velocityEvent.position;
          var time = lastMoveEvent.time - velocityEvent.time;
          swiper.velocity = distance / time;
          swiper.velocity /= 2;
複製代碼

能夠看出,他計算起始速度velocity參考的是onTouchMove最後記錄的兩個點,單純取最後兩個點是不可靠的,可能由於最後兩次onTouchMove觸發間隔比較長,致使計算出來的速度太低。從而致使偶爾會快速滑動但慣性效果是慢滑動的效果。

若是觸發間隔很短,致使動畫速度變快,變快了其實感知上並沒有區別。最要命的仍是變慢,感受很卡同樣。

解決方案

一個很短的時間內,人的滑動方向不太可能會產生變化,但這個短期內產生的滑動位移可以很好的表明手勢結尾的滑動速度。

通過反覆嘗試,用100毫秒內的位移來計算慣性起始速度最好,100毫秒內,若是是快速滑動,會觸發屢次onTouchMove,計算出來的速度是很接近實際的手勢速度。

問題的解決指向了100毫秒內的第一個點。解決代碼:

if (params.freeModeMomentum) {
        if (data.velocities.length > 1) {
			//fix 慣性動畫
			var velos=data.velocities;
			var firstEvent=velos[0];
			var lastMoveEvent = velos.pop();
			var velocityEvent =velos.pop();
			for(var i=velos.length-1;i>=0;i--){//找出100毫秒內的起始位置
				var velo=velos[i];
				if(lastMoveEvent.time-velo.time>100){
					break;
				}
				velocityEvent=velo;
			}

          var distance = lastMoveEvent.position - velocityEvent.position;
          var time = lastMoveEvent.time - velocityEvent.time;
          swiper.velocity = distance / time;
          swiper.velocity /= 2;
複製代碼

3、解決慣性動畫時長不合理問題

慢滑動時慣性應該很小,動畫很短;快速滑動時慣性應該很大,動畫很長。swiper4.x只能提供一個固定的慣性動畫時長,不改源代碼是解決不了的。

原代碼在第二問代碼下面一點點

var momentumDuration = 1000 * params.freeModeMomentumRatio; //寫死了固定動畫時長
複製代碼

優化動畫

其實也簡單,小的就小,大的就大,用初始速度velocity來作乘法運算便可達到效果。

var momentumDuration = Math.abs(swiper.velocity) * 1000 * params.freeModeMomentumRatio;
複製代碼

結果:比原代碼好不少,但跟別的app裏面的滑動仍是區別蠻大,慢滑時仍是太快了點。

App原生滑動動畫,感知上是慢的更慢,快的更快。輕微滑動慢的要死,快速動一點點,飛快。

對數曲線!1-0範圍蠻符合,緩的地方比線性的還緩,陡的地方奇陡無比。在上面優化的結果基礎上用對數加持一下,效果很是不錯。另外限定最長動畫不超過3秒。

//取對數曲線優化一下0.5-1.5倍之間,慢的越慢,快的越快
momentumDuration*=-Math.log10(Math.min(0.3,Math.max(0.03,(3000-momentumDuration)/3000)));
複製代碼

解決方案代碼

//根據初始速度來肯定慣性動畫時長
var momentumDuration = Math.min(3000,Math.abs(swiper.velocity) * 1000 * params.freeModeMomentumRatio);
//取對數曲線優化一下0.5-1.5倍之間,慢的越慢,快的越快
momentumDuration*=-Math.log10(Math.min(0.3,Math.max(0.03,(3000-momentumDuration)/3000)));
momentumDuration=Math.min(3000,momentumDuration);
複製代碼

最終結果

應用swiper4.x修改後的單頁滾動效果,已經很接近App的滾動效果,雖然仍是會有些細微的抖動卡頓感,但要作到徹底和App同樣的流暢效果,估計蠻難,當前算是已經蠻優了。

仍是iscroll省心些,但已經上了swiper4.x這條船了就算了,遷移過來又遷移過去白折騰。

改良後的滾動效果,目前[2019-02-27]號之後能夠到 jiebian.life/start/xcx/t… 體驗(這個時間以前新版本應該尚未上線),小程序和H5共用的頁面。

相關文章
相關標籤/搜索