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


應用場景

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

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

出現的問題

一、慣性動畫不會在觸摸時停止

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

二、快速滑動手勢大概率識別成慢滑動手勢

連續快速多滑幾下,有那么1、2下明顯能夠感知到滑動慣性動畫變慢的情景,給人的感覺就是滑動不流暢。

三、慣性動畫時長不合理

不管是很慢的滑動,還是很快的滑動,慣性動畫時長只能設置一個固定值。慢慢移動松手后也會有一個很長的動畫,快速滑的時候動畫又有點短,綜合看起來給人很卡的感覺。

解決手段

除了第一個問題,另外兩個問題不修改swiper4.x源碼似乎無法克服。

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

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

setTranslate(translate)可以移除這個動畫css,需要提供頁面當前的滾動位置。用getTranslate()就行了:

這與通過屬性mySwiper.translate 獲取到的數值稍有不同,即使是在過渡時(animating)也能獲取到,而后者精度較高

解決方案代碼

//在touchStart事件中執行
mySwiper.setTranslate(mySwiper.getTranslate());

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

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

原代碼

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;

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

慢滑動時慣性應該很小,動畫很短;快速滑動時慣性應該很大,動畫很長。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]號以后可以到 https://jiebian.life/start/xcx/tool_jieri 體驗(這個時間之前新版本應該還沒有上線),小程序和H5共用的頁面。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM