僅僅應用於單頁應用的滑動操做,用swiper4.x
接管頁面的滾動操做。用來支持頂部和尾部的回彈效果,進一步來支持常見那種下拉刷新動畫效果。不適用於輪播圖那種應用場景。javascript
雖然只是針對swiper4.x
,但相關原理,在別的框架中也是有參考意義的。css
快速滑動頁面,手離開屏幕時產生的慣性動畫還在運行時,此時觸摸屏幕,動畫不會中止。致使連續快速滑動頁面,看起來有跳來跳去的感受。java
連續快速多滑幾下,有那麼一、2下明顯可以感知到滑動慣性動畫變慢的情景,給人的感受就是滑動不流暢。小程序
不論是很慢的滑動,仍是很快的滑動,慣性動畫時長只能設置一個固定值。慢慢移動鬆手後也會有一個很長的動畫,快速滑的時候動畫又有點短,綜合看起來給人很卡的感受。bash
除了第一個問題,另外兩個問題不修改swiper4.x
源碼彷佛沒法克服。app
滑動時手離開屏幕時產生的慣性動畫是css動畫
,並未直接提供中止動畫的方法。動畫還在運行時若是觸摸屏幕,不改源代碼,那咱們就監聽一下touchStart
事件。框架
用setTranslate(translate)
能夠移除這個動畫css,須要提供頁面當前的滾動位置。用getTranslate()
就好了:優化
這與經過屬性
mySwiper.translate
獲取到的數值稍有不一樣,即便是在過渡時(animating
)也能獲取到,然後者精度較高動畫
//在touchStart事件中執行
mySwiper.setTranslate(mySwiper.getTranslate());
複製代碼
這個必須修改源代碼才能解決,問題出在計算慣性動畫起始速度時的計算參數精度不足。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;
複製代碼
慢滑動時慣性應該很小,動畫很短;快速滑動時慣性應該很大,動畫很長。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共用的頁面。