水平动画大概有这个几个方法:
- 通过
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