水平動畫大概有這個幾個方法:
- 通過
margin-left
,常見於輪播圖 - 通過
translateX
,過渡效果好,複雜多坑 - 通過
scrollLeft
,簡單但不支持css3動畫
margin-left
很容易實現,但是需要大量JS輔助,且從頭至尾的跳躍性很大,過渡效果不佳,不太推薦。
translateX
利用css3動畫,可以做到很好的過渡效果,尤其適合輪播圖,可以無限循環。它可以很複雜,對於一般的平移動畫,一個animation定義就可以搞定。
特別提下遇到的一個坑。有一個複雜的交互 ,整屏水平滾動,同時其中的元素獨立地移動,由於兩者的時機可能不一致,因此存在視差。出現過這些狀況:
- 外層水平移動時,裏面的小元素不可見,雖然它確實在動!通過給外層元素添加
visibility: visible!important
可以解決,原理還不清楚,應該和層疊上下文有關,需要挖RFC文檔。 - 通過
translateX
平移後,不能手動向左平移,因此動畫結束後,需要重置translateX
爲0,同時設置scrollLeft
爲合適的平移值。也因此,css裏最好加上transform: translateX(0)
。 - 整屏的css3動畫很耗性能,很多機型上裏面的元素動畫時明顯卡頓。
scrollLeft
這個和scrollTop
是一對,兼容性很好,但是,不支持css3動畫。上面提到了,整屏的css3動畫性能比較糟糕,因此使用requestAnimationFrame
這個性能表現更好的方案,這時必須靠scrollLeft
實現平移。當然這樣也有難點:
- 利用
requestAnimationFrame
實現動畫,代碼複雜且不易理解。 - 依賴cpu運行時,如果只用
requestAnimationFrame
,時間是不可控的,而且在移動端,實際運行時間很可能比你預期的長一截。爲了更加精確,可以通過setInterval
觸發動畫。
附兩個requestAnimationFrame
動畫實現:
/** 線性方案*/
function scrollToLeft(dom, stop, duration) {
// 手機上實際動畫會延長,做一些壓縮
duration *= 0.88;
var t1 = performance.now();
var from = dom.scrollLeft(),
step = Math.round((stop - from) * 15 / duration);
function move() {
from += step;
if (step > Math.abs(stop - from)) {
from = stop;
}
dom.scrollLeft(from);
if (from === stop) {
var t2 = performance.now();
} else {
window.requestAnimationFrame(move);
}
}
window.requestAnimationFrame(move);
}
/** 曲線方案 */
function scrollToLeft(dom, stop, duration, onEnd) {
var from = dom.scrollLeft(),
cosParameter = Math.abs(stop - from) / 2,
scrollCount = 0,
oldTimestamp = performance.now(),
delta = Math.PI / duration;
function step (newTimestamp) {
scrollCount += delta * (newTimestamp - oldTimestamp);
if (scrollCount >= Math.PI) {
dom.scrollLeft(stop);
if ($.isFunction(onEnd)) onEnd();
return;
}
var left = stop - Math.round(cosParameter + cosParameter * Math.cos(scrollCount));
dom.scrollLeft(left);
oldTimestamp = newTimestamp;
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
}
參考:http://stackoverflow.com/questions/21474678/scrolltop-animation-without-jquery