效果预览

按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。

https://codepen.io/comehope/pen/eLMKJG

可交互视频

此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。

请用 chrome, safari, edge 打开观看。

https://scrimba.com/p/pEgDAM/cdDRmH9

源代码下载

每日前端实战系列的全部源代码请从 github 下载:

https://github.com/comehope/front-end-daily-challenges

代码解读

定义 dom,容器中包含 10 个 div 子元素,每个 div 中包含 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 个元素,它有一个外壳 div,里面是一个白色的小方块 span

.container {
position: relative;
} .container div {
position: absolute;
width: inherit;
height: inherit;
display: flex;
align-items: center;
justify-content: center;
} .container div span {
position: absolute;
width: 40px;
height: 40px;
background-color: white;
}

为容器中的元素定义下标变量,并让元素的外壳依次旋转,围合成一个圆形,其中 outline 是辅助线:

.container div {
outline: 1px dashed black;
transform: rotate(calc((var(--n) - 1) * 36deg));
} .container div:nth-child(1) { --n: 1; }
.container div:nth-child(2) { --n: 2; }
.container div:nth-child(3) { --n: 3; }
.container div:nth-child(4) { --n: 4; }
.container div:nth-child(5) { --n: 5; }
.container div:nth-child(6) { --n: 6; }
.container div:nth-child(7) { --n: 7; }
.container div:nth-child(8) { --n: 8; }
.container div:nth-child(9) { --n: 9; }
.container div:nth-child(10) { --n: 10; }

至此,子元素绘制完成,接下来开始写动画脚本。
引入 GSAP 库:

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script>

定义一个变量,代表子元素选择器:

let elements = '.container div 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 div {
/* 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;
}

大功告成!

前端每日实战:133# 视频演示如何用 CSS 和 GSAP 创作有多个关键帧的连续动画的更多相关文章

  1. 前端每日实战:134# 视频演示如何用 CSS 和 GSAP 创作一个树枝发芽的 loader

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/LJmpXZ 可交互视频 此视频是可 ...

  2. 前端每日实战:144# 视频演示如何用 D3 和 GSAP 创作一个集体舞动画

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/gdVObN 可交互视频 此视频是可 ...

  3. 前端每日实战:136# 视频演示如何用 D3 和 GSAP 创作一个横条 loader

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/pOZKWJ 可交互视频 此视频是可 ...

  4. 前端每日实战:89# 视频演示如何用 CSS 和 D3 创作旋臂粒子动画

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/xJrOqd 可交互视频 此视频是可 ...

  5. 前端每日实战:141# 视频演示如何用 CSS 的 Grid 布局创作一枚小狗邮票

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/BOeEYV 可交互视频 此视频是可 ...

  6. 前端每日实战:142# 视频演示如何用 CSS 的 Grid 布局创作一枚小鸡邮票

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/mGZbmQ 可交互视频 此视频是可 ...

  7. 前端每日实战:116# 视频演示如何用 CSS 和原生 JS 开发一个监控网络连接状态的页面

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/oPjWvw 可交互视频 此视频是可 ...

  8. 前端每日实战:157# 视频演示如何用纯 CSS 创作一个棋盘错觉动画(实际上每一行都是平行的)

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/VEyoGj 可交互视频 此视频是可 ...

  9. 前端每日实战:158# 视频演示如何用纯 CSS 创作一个雨伞 toggle 控件

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/pxLbjv 可交互视频 此视频是可 ...

随机推荐

  1. 小白上手Linux系统安装jdk教程

    1.查看是否有预装jdk及jdk版本: rpm -qa|grep jdk 如果有则卸载安装:rpm -e --nodeps jdk-1.7.0_79-fcs.x86_64 2.先将linux版的jdk ...

  2. Tableau学习step1一Tableau概述

    本文首发于博客冰山一树Sankey,去博客浏览效果更好. 一.Tableau优缺点 优点 ·简单易用,只要是会用 EXCEL,几分钟就可掌握 Tableau的基本用法 急速高效 ·结果美观 ·轻松整合 ...

  3. HTML的表格标签,列表标签,表单标签,HTML5有哪些新特性

    欢迎大家去博客冰山一树Sankey,浏览效果更好.直接右上角搜索该标题即可 博客园主页:博客园主页-冰山一树Sankey CSDN主页:CSDN主页-冰山一树Sankey 前端学习:学习地址:黑马程序 ...

  4. 测评 | 矩池云上架 RTX 2080 Ti 八卡机开箱

    大家好,福利君今天给给大家带来的是一则消息.矩池云将上架了超微八卡GPU服务器,全新的机器组合,可靠的服务品质. 产品性能 在这里引用Lambda Labs基于FP32对多GPU扩展训练性能评测的数据 ...

  5. linux作业--第六周

    1.编写脚本实现登陆远程主机.(使用expect和shell脚本两种形式). #expect方式 yum -y install expect vim remote_ssh.sh #!/usr/bin/ ...

  6. Vue-表单验证-全选-反选-删除-批量删除

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  7. ESXI 虚拟化误删除管理端口Management Network (vmk0),导致无法访问后台解决方案

    按F2开启控制台shell,启用后返回.按Alt+F1打开终端. 输入 esxcfg-vmknic -a -i 192.168.1.10 -n 255.255.255.0 "Manageme ...

  8. pygame坦克大战前夕

    最近想自己写pygame版的坦克大战,今晚已经完成如下功能: 1,我方坦克,可手动移动:敌方坦克,自动转方向与移动 2,坦克颜色随机,坦克形态大小可调. 3,双方坦克速度可调. 4,刷新坦克的位置随机 ...

  9. 使用gdb

    1.设置断点,在源程序第16 行处 (gdb)break 16 Breakpoint 1 at 0x8048496: file tst.c, line 16. 2.设置断点,在函数func()入口处. ...

  10. path()和re_path()用法&区别

    path() 参数列表: 参数1:字符串类型,用来匹配请求路径 参数2:指定路径所对应的视图函数名 参数3:关键字参数 实际用的不多 参数4... # urls.py # 创建子应用的路由文件 fro ...