共计 5943 个字符,预计需要花费 15 分钟才能阅读完成。
本文,咱们将一起学习,应用纯 CSS,实现如下所示的动画成果:
下面的动画成果,十分有意思,外围有两点:
- 小球随机做 X、Y 方向的直线运动,并且可能实现碰撞到边界的时候,实现反弹成果
- 小球在碰撞边界的霎时,色彩产生随机的变动
嗯?很有意思的成果。看上去,咱们如同应用 CSS 实现了碰撞检测。
然而,理论状况真的是这样吗?让咱们一起一探到底!
实现 X 轴方向的静止
这里其实咱们并没有实现碰撞检测,因为小球和小球之间接触时,并没有产生碰撞成果。
咱们只实现了,小球与边界之间的碰撞反馈。不过这里,也并非碰撞检测,咱们只须要设置好单个方向的静止动画,并且设置 animation-direction: alternate;
即可!
上面,咱们一起来实现单个方向上的静止动画:
<div></div>
div { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100px; | |
height: 100px; | |
border-radius: 50%; | |
background: #0cf; | |
animation: horizontal 3s infinite linear alternate; | |
} | |
@keyframes horizontal { | |
from {left: 0;} | |
to {left: calc(100vw - 100px); | |
} | |
} |
简略解读一下:
- 元素设置为
position: absolute
相对定位,利用left
进行 X 轴方向的静止 - 咱们让元素
div
静止的间隔为left: calc(100vw - 100px)
,元素自身的高宽都是100px
,因而相当于静止到屏幕的最右侧 - 动画设置了
alternate
也就是animation-direction: alternate;
的简写,示意 动画在每个循环中正反交替播放
这样,咱们就奇妙的实现了,在视觉上,小球元素挪动到最右侧边界时,回弹的成果:
如法炮制 Y 轴方向的静止
好,有了下面的铺垫,咱们只须要再如法炮制 Y 轴方向的静止即可。
利用元素的 top
进行 Y 轴方向的静止:
div { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100px; | |
height: 100px; | |
border-radius: 50%; | |
background: #0cf; | |
animation: | |
horizontal 3s infinite linear alternate, | |
vertical 3s infinite linear alternate; | |
} | |
@keyframes horizontal { | |
from {left: 0;} | |
to {left: calc(100vw - 100px); | |
} | |
} | |
@keyframes vertical { | |
from {top: 0;} | |
to {top: calc(100vh - 100px); | |
} | |
} |
咱们减少了一个 vertical 3s infinite linear alternate
Y 轴的静止动画,实现小球从 top: 0
到 top: calc(100vh - 100px);
的静止。
这样,咱们就胜利的失去了 X、Y 两个方向上的小球静止,它们叠加在一起的成果如下:
色彩的变动能够疏忽,GIF 录制问题。
当然,此时的问题在于,短少了随机性,小球的始终在左上和右下角之间来回静止。
为了解决这个问题,咱们须要增加肯定的随机性,这个问题也要解决,咱们只须要让两个方向上静止工夫不统一即可。
咱们批改一下代码,让 X、Y 轴的静止时长不统一即可:
div { | |
position: absolute; | |
// ... | |
animation: | |
horizontal 2.6s infinite linear alternate, | |
vertical 1.9s infinite linear alternate; | |
} |
如此一来,整体的成果就好上了不少,因为整个动画是有限重复进行的,随着工夫的推动,整个动画出现进去的就是 无序、随机的静止:
应用 transform 代替 top、left
当然,下面的成果基本上没有什么太大的问题了,然而代码层面不够优雅,次要有两点问题:
- 元素挪动应用的是
top
和left
,性能绝对较差,须要应用transform
进行代替 - 代码中 hardcode 了
100px
,因为 DEMO 中小球的大小是100px x 100px
,并且在动画的代码中也应用了100px
这个值进行了静止终态的计算,因而如果想批改小球的元素大小,须要改变中央较多
上述两个问题,应用 transform: translate()
都能够解决,然而咱们为什么一开始不必 transform
呢?
咱们来尝试一下,应用 transform 代替 top、left:
div { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100px; | |
height: 100px; | |
border-radius: 50%; | |
background: #0cf; | |
animation: | |
horizontal 2.6s infinite linear alternate, | |
vertical 1.9s infinite linear alternate; | |
} | |
@keyframes horizontal {from { transform: translateX(0); } | |
to {transform: translateX(calc(100vw - 100%)); } | |
} | |
@keyframes vertical {from { transform: translateY(0); } | |
to {transform: translateY(calc(100vh - 100%)); } | |
} |
上述代码中,咱们应用了 transform 代替 top、left 静止。并且,将动画代码中的 100px
替换成了 100%
,这一点的益处是,在 transform: translate
中,100%
示意的是元素自身的高宽,这样,当咱们扭转元素自身的大小时,就无需再扭转 @keyframes
中的代码,通用性更强。
咱们来看看批改后的成果:
有点问题!料想中的成果并没有呈现,整个动画只有 Y 轴方向上的动画成果。
这是什么起因呢?
其本质在于,定义的 vertical 1.9s infinite linear alternate
的垂直方向的动画成果笼罩了在其之前定义的 transform: translateX(calc(100vw - 100%))
动画成果。
说人话就是 X、Y 轴的动画都应用了 transform
属性,两者之间造成了抵触。
应用 animation-composition 进行动画合成
在之前,这种状况根本是无解的,常见的解决方案就是:
- 解法一:应用
top
、left
代替 transform - 解法二:多一层嵌套,将一个方向的动画拆解到元素的父元素上
不过,到明天,这个问题有了更好的解法!也就是 CSS animation 家族中的新属性 —— animation-composition
。
这是一个十分新的属性,示意 动画合成属性,从 Chrome 112 版本开始反对。
有三种不同的取值:
{ | |
animation-composition: replace; // 示意动画值替换 | |
animation-composition: add; // 示意动画值追加 | |
animation-composition: accumulate; // 示意动画值累加 | |
} |
本文不会具体介绍 animation-composition
,感兴趣的能够看看 MDN 的属性介绍或者 XBOXYAN 大佬的这篇文章 — 理解一下全新的 CSS 动画合成属性 animation-composition
这里,基于下面的代码,咱们只须要再多设置一个 animation-composition: accumulate
即可解决问题:
div { | |
animation: | |
horizontal 2.6s infinite linear alternate, | |
vertical 1.9s infinite linear alternate; | |
animation-composition: accumulate; | |
} |
此时,咱们就能通过一个元素,利用 transform 失去 X、Y 两个方向位移动画的合成成果,也就是咱们想要的成果:
应用 steps 实现色彩切换
解决了位移动画的问题,咱们就只剩下最初一个问题了,如何在碰撞的霎时,实现色彩的切换?
这里也十分好解决,因为咱们是晓得每一轮 X、Y 方向上的动画时长的,那咱们只须要在每次这个结点上,切换一次色彩即可。
并且,因为色彩不是过渡变换,而是间接的跳变,所以,咱们须要用到 animation 中的 animation-timing-function: steps()
,也就是步骤缓动函数。
对
animation-timing-function: steps()
还不太理解的,可能须要先补一补根底,能够看看这一篇文章:深入浅出 CSS 动画
举个例子,假如 X 方向上,单次的动画时长为 3s,那咱们能够设置一个 steps(10)
的色彩动画,总时长为 30s,这样,每隔 3s 就会触发一次 steps()
步骤动画,色彩的变动就可能和小球与边界的碰撞动画产生在同一时刻。
那如何疾速实现色彩的变动呢?利用 filter: hue-rotate()
即可疾速实现色彩的变动。
了解一下上面的代码:
<div class="normal"></div> | |
<div class="steps"></div> |
div { | |
width: 200px; | |
height: 200px; | |
background: #fc0; | |
} | |
.normal {animation: colorChange 10s linear infinite;} | |
.steps {animation: colorChange 10s steps(5) infinite; | |
} | |
@keyframes colorChange { | |
100% {filter: hue-rotate(360deg); | |
} | |
} |
这里,咱们用 filter: hue-rotate(360deg)
的扭转,实现色彩的变动,察看上面的动图,了解 steps(5)
的作用。
animation: colorChange 10s linear infinite
示意背景动画的过渡变动animation: colorChange 10s steps(5) infinite
,这里示意 10s 的动画分成 5 步,每两秒,会触发一次动画:
成果如下:
了解了这一步,咱们就能够把色彩的变动,也一起叠加到上述的小球变动中:
div { | |
animation: | |
horizontal 2.6s infinite linear alternate, | |
vertical 2s infinite linear alternate, | |
colorX 26s infinite steps(10), | |
colorY 14s infinite steps(7); | |
animation-composition: accumulate; | |
} | |
@keyframes horizontal {from { transform: translateX(0); } | |
to {transform: translateX(calc(100vw - 100%)); } | |
} | |
@keyframes vertical {from { transform: translateY(0); } | |
to {transform: translateY(calc(100vh - 100%)); } | |
} | |
@keyframes colorX { | |
to {filter: hue-rotate(360deg); | |
} | |
} | |
@keyframes colorY { | |
to {filter: hue-rotate(360deg); | |
} | |
} |
这样,咱们就胜利的失去了题图中的成果:
残缺的代码,你能够戳这里:Random Circle Path
利用于图片成果、利用与多粒子成果
OK,下面,咱们就把整个成果的残缺原理分析了一遍。
把握了整个原理之后,咱们就能够把这个成果利用于不同场景中。
譬如,假如咱们有这么一张图片:
基于下面的成果,稍加革新,咱们就能够失去相似的如下成果:
<div></div>
div { | |
width: 220px; | |
height: 97px; | |
background: linear-gradient(#f00, #f00), url(https://s1.ax1x.com/2023/08/15/pPQm9oT.jpg); | |
background-blend-mode: lighten; | |
background-size: contain; | |
animation: horizontal 3.7s infinite -1.4s linear alternate, | |
vertical 4.1s infinite -2.1s linear alternate, | |
colorX 37s infinite -1.4s steps(10), | |
colorY 28.7s infinite -2.1s steps(7); | |
animation-composition: accumulate; | |
} | |
@keyframes horizontal {from { transform: translateX(0); } | |
to {transform: translateX(calc(100vw - 100%)); } | |
} | |
@keyframes vertical {from { transform: translateY(0); } | |
to {transform: translateY(calc(100vh - 100%)); } | |
} | |
@keyframes colorX { | |
to {filter: hue-rotate(2185deg); | |
} | |
} | |
@keyframes colorY { | |
to {filter: hue-rotate(1769deg); | |
} | |
} |
成果如下:
下面的 DEMO 是基于元素背景色的,本 DEMO 是基于图片的,因而这里多了一步,利用 mix-blend-mode
,实现了图片色彩的变动。
残缺的代码,你能够戳这里:CodePen Demo — Random DVD Path
实现多粒子碰撞
OK,咱们再进一步,基于下面的成果,咱们能够实现各种乏味的粒子成果,如果同时让页面存在 1000 个粒子呢?
上面是我应用 CSS-Doodle 实现的纯 CSS 的粒子成果,其外围原理与下面的保持一致,只是增加了更多的随机性:
Amazing!是不是十分乏味,整个成果的代码基于 CSS-doodle 的语法,不超过 40 行。残缺的代码,你能够戳这里:CSS Doodle – CSS Particles Animation
最初
总结一下,本文介绍了如何奇妙的利用 CSS 中的各种高阶技巧,组合实现相似于碰撞场景的动画成果。创立出了十分乏味的 CSS 动画,期间各种技巧的组合使用,值得好好推敲学习。
OK,本文到此结束,心愿本文对你有所帮忙 :)
想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 — iCSS 前端趣闻 😄
更多精彩 CSS 技术文章汇总在我的 Github — iCSS,继续更新,欢送点个 star 订阅珍藏。
如果还有什么疑难或者倡议,能够多多交换,原创文章,文笔无限,满腹经纶,文中若有不正之处,万望告知。