图片
说难不难,说简单也不简单,这是一个无限循环的轮播效果,并且个数是固定的,你会如何实现呢?
原效果是通过vue的transition组件实现的,感觉有些笨重,思考了一番,发现纯 CSS也能实现这样的效果,而且性能更好,实现也更简洁,一起来看看吧!
首先来分析一下思路。看着是一个连贯的轮播效果,其实单独看每一个子项,都是完全相同的运动轨迹。
图片
然后是这个动画,其实就是6个关键帧,逐一去位移和缩放,如下:
图片
然后给每个子项不同的“负延迟”,是不是就刚好错开,形成一个完整的轮播效果了呢?
无论CSS怎么实现,动画原理就这些了,下面来看几个实现方式
首先简单布局一下,先用一个元素实现动画;
<div class="item"></div>
加点修饰;
html,body{ font-family: -apple-system, "BlinkMacSystemFont", sans-serif; margin: 0; height: 100%; display: flex; justify-content: center; flex-direction: column; align-items: center; background: aliceblue; counter-reset: num;}.item{ position: absolute; display: grid; place-content: center; width: 100px; height: 100px; border-radius: 8px; background-color: #3E65FF; color: #fff; font-size: 30px; counter-increment: num;}
这里的数字是用CSS计数器生成的,效果如下:
图片
根据前面的关键帧,很容易用代码实现,就是。
.item{ animation: slide 12s infinite;}@keyframes slide { 0%,100% { transform: translate(0%, 0%) scale(1); } 16.67% { transform: translate(120%, -30%) scale(0.9); } 33.33% { transform: translate(100%, -70%) scale(0.8); } 50% { transform: translate(0, -90%) scale(0.7); } 66.67% { transform: translate(-100%, -70%) scale(0.8); } 83.33% { transform: translate(-120%, -30%) scale(0.9); }}
这里的16.67%关键帧是将100%进行6等分得到的,如下所示:
图片
效果如下:
图片
虽然有动画了,但是效果有点奇怪,每次改变位置的时候好像没有停顿,显得过渡有些缓慢,只是有减速和加速的过程,这是默认的ease-in-out的淡入淡出缓冲效果。
那么,如何拉开一定的间隔呢?也就是希望每次运动的快一点,然后停留一会。其实也很简单,在之前的每个关键点前再添加一个相同的关键帧,比如在16.67%的前面一点6.67%,由于是相同的,所以这段时间内是没有动画的,也就相当于停留了一会。
图片
用代码实现就是:
@keyframes slide { 0%,90%,100% { transform: translate(0%, 0%) scale(1); } 6.67%, 16.67% { transform: translate(120%, -30%) scale(0.9); } 23.33%, 33.33% { transform: translate(100%, -70%) scale(0.8); } 40%, 50% { transform: translate(0, -90%) scale(0.7); } 56.67%, 66.67% { transform: translate(-100%, -70%) scale(0.8); } 73.33%, 83.33% { transform: translate(-120%, -30%) scale(0.9); }}
这样是不是就好多了?
图片
实现了一个,多个子元素也就好办了。
<div class="item" style="--i: 0"></div><div class="item" style="--i: 1"></div><div class="item" style="--i: 2"></div><div class="item" style="--i: 3"></div><div class="item" style="--i: 4"></div><div class="item" style="--i: 5"></div>
我们给每个子元素加个CSS变量,然后给每个动画加个“负延迟”,让这个动画提前运行到指定位置;
.item{ animation: slide 12s calc(-2s * var(--i)) infinite;}
效果如下:
基本就实现这个这个轮播动画,不过层级还有点问题。
所以还需要再关键帧里加入层级变化;
@keyframes slide { 0%,90%,100% { z-index: 4; transform: translate(0%, 0%) scale(1); } 6.67%, 16.67% { z-index: 3; transform: translate(120%, -30%) scale(0.9); } 23.33%, 33.33% { z-index: 2; transform: translate(100%, -70%) scale(0.8); } 40%, 50% { z-index: 1; transform: translate(0, -90%) scale(0.7); } 56.67%, 66.67% { z-index: 2; transform: translate(-100%, -70%) scale(0.8); } 73.33%, 83.33% { z-index: 3; transform: translate(-120%, -30%) scale(0.9); }}
这样就完美了。
这种实现兼容性最好,只用到了 CSS 动画,兼容市面所有浏览器,可以放心使用
其实目前来说,用上面这种方式就足够了,没有任何问题。
不过,上面对于中间停顿的处理方式可能有些繁琐,下面再介绍另一种思路,可能更符合常规。
先思考一下,如果是用 JS要如何实现?是不是可以直接每隔2秒改变位移和缩放,然后通过transition实现过渡效果?
首先,我们还是需要像之前一样,定义一些关键帧,不过不是直接改变位移和缩放,而是改变一些 CSS变量。
@keyframes slide { 0%,100% { --translate: 0,0; --scale: 1; --z: 4; } 16.67% { --translate: 120%,-30%; --scale: 0.9; --z: 3; } 33.33% { --translate: 100%,-70%; --scale: 0.8; --z: 2; } 50% { --translate: 0%,-90%; --scale: 0.7; --z: 1; } 66.67% { --translate: -100%,-70%; --scale: 0.8; --z: 2; } 83.33% { --translate: -120%,-30%; --scale: 0.9; --z: 3; }}
然后每个子项就需要用transfrom来应用这些变量了;
.item{ transform: translate(var(--translate)) scale(var(--scale)); transition: .5s transform; animation: slide 12s calc(-2s * var(--i)) infinite;}
我们来看看效果:
好像并没有过渡动画?这是因为animation覆盖了transition,所以需要分离开来,我们嵌套一层父级。
<div class="item-wrap" style="--i: 0"> <div class="item"></div></div><div class="item-wrap" style="--i: 1"> <div class="item"></div></div><div class="item-wrap" style="--i: 2"> <div class="item"></div></div><div class="item-wrap" style="--i: 3"> <div class="item"></div></div><div class="item-wrap" style="--i: 4"> <div class="item"></div></div><div class="item-wrap" style="--i: 5"> <div class="item"></div></div>
然后将动画写在父级上;
.item-wrap{ animation: slide 12s calc(-2s * var(--i)) infinite; display: contents;}
这样就不影响了,效果如下:
不过还是有点怪怪的。这是因为animation的默认效果也是ease-in-out,所以有这种渐入渐出的效果。我们需要瞬间变化,不需要过渡效果,因为过渡效果可以由transition完成。
这里我们可以用steps来实现,steps(1)表示直接切换,中间没有任何过渡。
.item-wrap{ animation: slide 12s calc(-2s * var(--i)) steps(1) infinite;}
这样效果就和前面基本一致了。
你也可以访问以下链接来查看实际效果:
虽然里面没有提到CSS @property,但实际上是依赖这个特性的,因此兼容性稍差,需要Safari 16.4+,Firefox目前还不支持。
抛开兼容性,其实还有一种方式可以实现,而且更容易理解,也更符合JS的思路。
我们要做的动画很简单,只需要改变一个 CSS变量就行,如下:
@property --index { syntax: "<number>"; initial-value: 0; inherits: false;}@keyframes slide { 0% { --index: 0; } 100% { --index: 6; }}
通过@property可以让--index变量从0→6一次变化。
关于这个技巧,之前在多篇文章中都有提到。
你可能不需要 JS!CSS实现一个计时器。
如何让CSS计数器支持小数的动态变化?
还在使用定时器吗?CSS 也能实现电子时钟。
动画合成小技巧!CSS 实现动感的倒计时效果。
自定义计数器小技巧!CSS 实现长按点赞累积动画。
实现如下:
.item-wrap{ display: contents; animation: slide 12s calc(-2s * var(--i)) steps(6) infinite;}
这样就实现了一个--index不断变化的动画。
当然仅仅只是这样还不够,我们需要根据这个变量来匹配具体的样式,这就要用到CSS样式查询了,具体实现如下:
@container style(--index: 0) { .item { transform: translate(0, 0) scale(1); z-index: 4; }}@container style(--index: 1) { .item { transform: translate(120%, -30%) scale(0.9); z-index: 3; }}@container style(--index: 2) { .item { transform: translate(100%, -70%) scale(0.8); z-index: 2; }}@container style(--index: 3) { .item { transform: translate(0, -90%) scale(0.7); z-index: 1; }}@container style(--index: 4) { .item { transform: translate(-100%, -70%) scale(0.8); z-index: 2; }}@container style(--index: 5) { .item { transform: translate(-120%, -30%) scale(0.9); z-index: 3; }}
这段应该很好理解,比如@container style(--index: 5) 表示,当查询到--index为5的时候,下面的样式就生效了,非常像通过 JS 来改变类名一样,这种方式也能实现类似的效果。
你也可以访问以下链接来查看实际效果:
由于要用到样式查询,所以兼容性更差一点,需要Chrome 111+,酌情使用。
以上就是本文的全部内容了,共介绍了3种不同的实现思路,兼容性从高到低,大家可以自行选择。
对了,还有一点,有时候我们需要鼠标hover时暂停动画,这个就体现出CSS的优势了,直接用:hover实现,类似这样
.wrap:hover .item{ animation-play-state: paused;}
下面总结一下实现要点:
本文链接:http://www.28at.com/showinfo-26-98557-0.htmlCSS 实现3d轮播图的一些思路,你学会了吗?
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 六个常见的 Go 接口设计错误