本篇文章给大家带来的内容是关于如何使用CSS和GSAP实现有多个关键帧的连续动画(附源码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
效果预览

源代码下载
https://github.com/comehope/front-end-daily-challenges
代码解读
定义 dom,容器中包含 10 个 p
子元素,每个 p
中包含 1 个 span
元素:
<figure class="container"> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> </figure>
居中显示:
body { margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center; background-color: lightyellow; }
定义容器的尺寸和样式:
.container { width: 400px; height: 400px; background: linear-gradient(45deg, tomato, gold); border-radius: 3%; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); }
画出容器里的 1 个元素,它有一个外壳 p
,里面是一个白色的小方块 span
:
.container { position: relative; } .container p { position: absolute; width: inherit; height: inherit; display: flex; align-items: center; justify-content: center; } .container p span { position: absolute; width: 40px; height: 40px; background-color: white; }
为容器中的元素定义下标变量,并让元素的外壳依次旋转,围合成一个圆形,其中 outline
是辅助线:
.container p { outline: 1px dashed black; transform: rotate(calc((var(--n) - 1) * 36deg)); } .container p:nth-child(1) { --n: 1; } .container p:nth-child(2) { --n: 2; } .container p:nth-child(3) { --n: 3; } .container p:nth-child(4) { --n: 4; } .container p:nth-child(5) { --n: 5; } .container p:nth-child(6) { --n: 6; } .container p:nth-child(7) { --n: 7; } .container p:nth-child(8) { --n: 8; } .container p:nth-child(9) { --n: 9; } .container p:nth-child(10) { --n: 10; }
至此,子元素绘制完成,接下来开始写动画脚本。
引入 GSAP 库:
<script></script>
定义一个变量,代表子元素选择器:
let elements = '.container p span';
声明一个时间线对象:
let animation = new TimelineMax();
先设定入场方式为由小(第1帧)变大(第2帧),其中并没有第 2 帧的代码,它是隐含在语义中的:
animation.from(elements, 1, {scale: 0});
让子元素变成竖长条,向四周散开(第3帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25});
让竖长条旋转着变成小方块(第4帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180});
让小方块变成横长条,围成一个圆形(第5帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1});
注意,因 scrimba 在录制过多帧时会崩溃,所以第 6 帧至第 11 帧没有在视频中体现。
让圆形向内收敛,同时线条变细(第6帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1}) .to(elements, 1, {y: '-60px', scaleY: 0.1});
让线条向左摆动(第7帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1}) .to(elements, 1, {y: '-60px', scaleY: 0.1}) .to(elements, 1, {x: '-30px'});
再让线条向右摆动(第8帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1}) .to(elements, 1, {y: '-60px', scaleY: 0.1}) .to(elements, 1, {x: '-30px'}) .to(elements, 1, {x: '30px'});
再把横线变为竖线,造型与第 3 帧相似,只是线更细,更向内收敛(第9帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1}) .to(elements, 1, {y: '-60px', scaleY: 0.1}) .to(elements, 1, {x: '-30px'}) .to(elements, 1, {x: '30px'}) .to(elements, 1, {x: '0', scaleX: 0.1, scaleY: 1});
再把竖线变为横线,造型与第 5 帧相似,但线短一些(第10帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1}) .to(elements, 1, {y: '-60px', scaleY: 0.1}) .to(elements, 1, {x: '-30px'}) .to(elements, 1, {x: '30px'}) .to(elements, 1, {x: '0', scaleX: 0.1, scaleY: 1}) .to(elements, 1, {scaleX: 0.5, scaleY: 0.1})
横线稍向外扩散,变为圆点(第11帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1}) .to(elements, 1, {y: '-60px', scaleY: 0.1}) .to(elements, 1, {x: '-30px'}) .to(elements, 1, {x: '30px'}) .to(elements, 1, {x: '0', scaleX: 0.1, scaleY: 1}) .to(elements, 1, {scaleX: 0.5, scaleY: 0.1}) .to(elements, 1, {y: '-80px', scaleY: 0.5, borderRadius: '50%'});
让圆点变形为竖线,并向内收缩,这个变化的距离长,所以动画时间也要长一些(第12帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1}) .to(elements, 1, {y: '-60px', scaleY: 0.1}) .to(elements, 1, {x: '-30px'}) .to(elements, 1, {x: '30px'}) .to(elements, 1, {x: '0', scaleX: 0.1, scaleY: 1}) .to(elements, 1, {scaleX: 0.5, scaleY: 0.1}) .to(elements, 1, {y: '-80px', scaleY: 0.5, borderRadius: '50%'}) .to(elements, 1, {y: '-10px', scaleX: 0.1, scaleY: 0.5, borderRadius: '0%', rotation: 0});
让竖线从中心向外快速扩散,扩散前稍停片刻,好像线条都被发射出一样(第13帧):
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1}) .to(elements, 1, {y: '-60px', scaleY: 0.1}) .to(elements, 1, {x: '-30px'}) .to(elements, 1, {x: '30px'}) .to(elements, 1, {x: '0', scaleX: 0.1, scaleY: 1}) .to(elements, 1, {scaleX: 0.5, scaleY: 0.1}) .to(elements, 1, {y: '-80px', scaleY: 0.5, borderRadius: '50%'}) .to(elements, 1, {y: '-10px', scaleX: 0.1, scaleY: 0.5, borderRadius: '0%', rotation: 0}) .to(elements, 1, {y: '-300px', delay: 0.5});
用时间尺度缩放函数让动画播放速度加快一倍:
animation.from(elements, 1, {scale: 0}) .to(elements, 1, {y: '-100px', scaleX: 0.25}) .to(elements, 1, {scaleY: 0.25, rotation: 180}) .to(elements, 1, {scaleX: 1}) .to(elements, 1, {y: '-60px', scaleY: 0.1}) .to(elements, 1, {x: '-30px'}) .to(elements, 1, {x: '30px'}) .to(elements, 1, {x: '0', scaleX: 0.1, scaleY: 1}) .to(elements, 1, {scaleX: 0.5, scaleY: 0.1}) .to(elements, 1, {y: '-80px', scaleY: 0.5, borderRadius: '50%'}) .to(elements, 1, {y: '-10px', scaleX: 0.1, scaleY: 0.5, borderRadius: '0%', rotation: 0}) .to(elements, 1, {y: '-300px', delay: 0.5}) .timeScale(2);
修改声明时间线的代码,使动画重复播放:
let animation = new TimelineMax({repeat: -1, repeatDelay: 1});
至此,动画完成。
隐藏容器外的内容,并删掉辅助线;
.container { overflow: hidden; } .container p { /* outline: 1px dashed black; */ }
最后,装饰一下页面的角落:
body { overflow: hidden; } body::before, body::after { content: ''; position: absolute; width: 60vmin; height: 60vmin; border-radius: 50%; background: radial-gradient( transparent 25%, gold 25%, gold 50%, tomato 50% ); } body::before { left: -30vmin; bottom: -30vmin; } body::after { right: -30vmin; top: -30vmin; }
大功告成!
相关推荐:
以上是如何使用CSS和GSAP实现有多个关键帧的连续动画(附源码)的详细内容。更多信息请关注PHP中文网其他相关文章!

您是否曾经在项目上需要一个倒计时计时器?对于这样的东西,可以自然访问插件,但实际上更多


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

DVWA
Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

记事本++7.3.1
好用且免费的代码编辑器

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。