前言
继续react组件库
轮播图实现你要考虑的几个点:
- 使用javascript实现轮播滚动动画和css实现动画哪个更好?
- 如果使用css动画实现,如何解决css动画期间,继续点击轮播图下一张不会影响到正在执行的滚动动画?
- 如何实现点击去第n张图的动画?
示例(arco组件官网):arco.design/react/compo…
js和css动画性能对比
你知道为什么css动画要比js实现动画性能更好吗?
这就要谈到浏览器渲染流水线中,每一帧渲染要经历哪些过程了,简单的来说,就是先执行js有关的内容,然后渲染线程绘制位图。
所以js执行动画的时候要从流水线从头开始计算,然后重绘和回流走一遍,而css如果单独把这个动画提到一个图层,那么重绘和回流(轮播不涉及位置重新计算,所以也就没有了回流),只需要一个重绘就行了,所以对于浏览器渲染来说,压力小很多。
但是js执行也有提升性能的办法,就是在requestAnimationFrame环节执行动画,但是还是不如css动画的原因在于,css动画的合成是有gpu加速的(需要设置一些css属性),所以处理速度快的多,综合来说,技术选型上使用了css动画。
css动画实现原理
首先点击下一张图,会发生什么情况。
简单来说说,就是设置一些变量表示此时正在执行轮播动画,并且轮播的图片的index记录下来,在执行完动画,然后做一些状态更改。详细情况如下:
- 如果没有正在执行动画,并且下一张跟这一张图不是同一张,则进入下一步,否则退出
- 将 isAnimating 设置为 true,表示正在执行动画
- 将 targetIndex设为当前图片在图片数组的index,表示要滚动到该幻灯片
- 将 previousIndex 设置为上一张图的索引,表示上一个幻灯片的索引,也就是现在的图片就是下次轮播的上一张图
- 触发onChange事件,表示此时已经开始轮播了
- 创建一个 setTimeout 定时器,等待指定的 moveSpeed 时间(毫秒),时间到了就把 isAnimating 设置为 false,表示动画已经执行完毕。
关键原理在于,当按下一页的时候,给当前的图片加一个class,给即将出现的图片加一个class,从而实现css动画,啥意思,举例:
[item-slide-out]:
isAnimating && index === previousIndex,
上面是arco的轮播图源码,我解释下。
item-slide-out,也就是当前图片退出,退出触发的前提是当前图片的index等于previousIndex,previousIndex是不是上面我们已经说了,在每次执行动画的时候,是不是把准备走的这张图,设置到了previousIndex中,所以每当我们点击下一张的时候
我们就会把当前图片上的dom增加一个item-slide-out的class,这个calss本质上是增加了一个animation的css动画
@keyframes ~'@{prefix}-carousel-slide-x-out' {
from {
transform: translateX(0);
}
to {
transform: translateX(-100%);
}
}
也就是利用translateX来使一张图从左边出去
[item-slide-in]:
isAnimating && isCurrent,
上面这个css的class是不是也很好理解了,就是下一张图从右边进来
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
如何解决css动画期间,继续点击轮播图下一张不会影响到正在执行的滚动动画
上面我们已经说了,有个isAnimating参数,表示是否正在执行动画,如果正在执行,继续点击下一张是没有效果的。
如何让css动画时间跟js时间同步
比如外部传入动画时间执行500ms,那么我们上面也分析了,设置了一个定时器,css动画500ms它自己执行,因为有animation这个css属性可以设置,js就通过定时器执行500ms,然后执行完毕设置isAnimating参数为false,表示执行完动画
从而达到动画同步的效果,但是我们知道定时器有可能有问题,但因为动画执行时间很短,基本没不会有bug。
如何实现点击去第n张图的动画
这个问题也是通过我们之前介绍的方法,之前我们说了是点击下一张的时候,设置targetIndex为下一张的index,那去第n张,是不是设置为去第n张的index就可以了
最后,轮播图基本实现是不是超级简单啊,留个思考题,如何实现如下的轮播(从css样式上考虑)