本讲主要探讨如何通过tzMaker创建帖子的粒子动画。tz模块没有封装粒子动画相关指令,不过如你在前面相关章节所看到的,tz具备绘制粒子的能力,换言之,它可以创建静态的粒子,包括绕圆弧布局的粒子、随机分散布局的粒子、原地旋转构图的粒子等等。以随机分散粒子为例,我们可以使用 lzRan() 指令实现,给粒子指定一个class,在CSS对应class选择器中设置粒子的相关属性,特别地,不能缺少关键帧动画,这样,tz所创建的粒子就会鲜活起来。来看看闪烁的星空案例:
<style>
@import 'https://638183.freep.cn/638183/web/tz/tz.min.css';
.pa { background: #000; }
.lz {
width: 4px;
height: 4px;
background: lightblue;
animation: flash 1s infinite alternate;
}
@keyframes flash {
from { opacity: 1; transform: rotate(15deg); }
to { opacity: 0; transform: rotate(45deg); }
}
</style>
<div class="pa"></div>
<script type="module">
import TZ from 'https://638183.freep.cn/638183/web/tz/tz.v3.js?v1';
const tz = TZ.TZ('pa');
tz.lzRan(200, 'pa', {
className: 'lz', // 粒子使用 .lz 选择器的样式
delay: -2 // 提前运行动画时间区间 -2~0 秒
});
</script>
两百个 id="lz" 的 div 标签做成的粒子,随机分布在帖子容器里面,它们通过CSS 选择器 .lz 制定了样式和动画,指令配置代码中还特意加入约定属性 delay,让粒子随机在 -2~0 秒之间提前运行动画,避免动画同质化,并借此营造更为自然的动画效果。除了 delay 动画运行延时属性, tz还有一个约定动画周期时长属性 duration,> 0 的整数,对应CSS的 animation-duration 属性,必要的时候可以让它参与到配置中来。
粒子不一定要布满帖子容器,一切可以定制,例如星斗粒子,完全可以给它设置一个容器,安排好粒子容器的尺寸和在帖子容器中的位置,然后在 lzRan() 指令的第二个参数 'pa' 改为其 id 标识,就可以把粒子控制在特定的范围里。还可以给粒子制定动画路线,这样不论粒子是随机分布的还是预设了固定位置的,运行时粒子都会出现在动画的路线上。下面的例子,粒子沿着椭圆圆环路线运行,运行动画前它们都是随机摆放在帖子容器中:
<style>
@import 'https://638183.freep.cn/638183/web/tz/tz.min.css';
.pa { background: linear-gradient(black, tan); }
lz {
width: 30px;
height: 20px;
offset-path: ellipse(40% 30%);
animation: move 18s linear infinite;
}
@keyframes move {
from { offset-distance: 0%; }
to { offset-distance: 100%; }
}
</style>
<div class="pa"></div>
<script type="module">
import TZ from 'https://638183.freep.cn/638183/web/tz/tz.v3.js?v1';
const tz = TZ.TZ('pa');
tz.lzRan(30, 'pa', {
className: 'lz',
duration: 18, // 动画周期时长区间 0~18 秒
delay: -18
});
</script>
上例的花括号 {} 配置中 className 属性指明粒子使用CSS .lz 选择器定义的样式。粒子在 .lz 样式和JS的 {} 配置都没有设置背景色,模块将随机赋予它们十六进制的背景颜色值;如果需要,可以在CSS代码或JS {} 配置中定义粒子的背景,图片、渐变(含随机渐变)、颜色值都可以。CSS动画设置了一个相抵路径(offset-path)的椭圆形路线,并在关键帧动画选择器 @keyframes 中设置从 0% 到 100% 的运行机制,配合JS {} 配置的 duration 和 delay 两个动画相关的属性,粒子的运行就有快有慢、有前有后,大抵会均匀分布在预设路线中。
以上两个示例,使用的指令都是 lzRan(),它生成的粒子可以自由分布,也可以设定 top 和(或)left 以定位或半定位,然后设计好CSS关键帧动画,粒子就可以动起来。lzRan() 指令最适合创建“全局”动画,可以满帖子范围内跑,如果愿意还可以让它们跑出帖子之外。而另外两个粒子相关指令,lzRing() 和 lzRot(),它们更喜欢呆在小范围内且相对而言其位置是固定的,不过它们也可以在“原地”运行自己的动画,下例使用 lzRing() 指令做绕圈圈布局的粒子,它们整体随父元素做旋转动画(公转),各自也另外运行相同(可以不相同)的旋转动画(自转):
<style>
@import 'https://638183.freep.cn/638183/web/tz/tz.min.css';
.pa { background: white; }
.ma { border: 4px double purple; }
.lz {
width: 30px;
height: 30px;
animation: rotate 2s linear infinite var(--state);
}
</style>
<div class="pa"></div>
<script type="module">
import TZ from 'https://638183.freep.cn/638183/web/tz/tz.v3.js?v1.0';
const tz = TZ.TZ('pa');
// 加入音频以便动画可以进行交互
tz.add('audio', '', '', { src: 'https://music.163.com/song/media/outer/url?id=551842545' });
tz.add('div', 'lzpa', 'ma').playmp3();
tz.lzRing(8, 'lzpa', {
className: 'lz',
duration: 2,
delay: -2
});
</script>
lzRot() 生成的“粒子”本质上应是大个头,多个单体旋转后构成复合图案,彼此间可能有交叉或接触,复合组图后具备更强的整体性,单体个性化动画可操作空间很小。但事在人为,设计得好,依然可以有限地给单体设置动画,下例,使用粒子的伪元素实现闪烁关键帧动画:
<style>
@import 'https://638183.freep.cn/638183/web/tz/tz.min.css';
.pa { background: beige; }
.ma { display: grid; place-items: center; }
.lz {
position: absolute;
width: 2px;
height: 100%;
display: grid; /* grid布局便于安排伪元素位置 */
place-items: center;
}
.lz::before, .lz::after {
position: absolute;
content: '';
top: -40px;
width: 40px;
height: 40px;
background: linear-gradient(black, var(--cc)); /* 渐变背景 */
border-radius: 50%;
animation: flash .2s infinite alternate var(--state);
animation-delay: inherit; /* 继承父元素动画延时属性 */
}
.lz::after { top: 100%; }
@keyframes flash { to { opacity: .5; } }
</style>
<div class="pa"></div>
<script type="module">
import TZ from 'https://638183.freep.cn/638183/web/tz/tz.v3.js?v1.0';
const tz = TZ.TZ('pa');
tz.add('audio', '', '', { src: 'https://music.163.com/song/media/outer/url?id=37778306' });
tz.add('div', 'lzpa', 'ma').playmp3();
tz.lzRot(6, 'lzpa', {
className: 'lz',
cc: true, // 使用 --cc 随机颜色
angle: 180, // 粒子贯穿容器,角度不应是360
delay: -1 // 粒子主元素没有动画,但伪元素可以继承 animation-delay 属性值
});
</script>
最后这个示例很有意思:tz模块没有操作伪元素的能力,{}配置里的 delay 属性基于粒子而非粒子的伪元素,伪元素在CSS里设置了继承父元素的 animation-delay 属性,故而 delay 属性的配置能作用于伪元素,伪元素运行动画的起步时间差异因此可以实现,避免了同步运行;粒子伪元素所用到的 --cc 变量也是通过继承得来:{}配置里的 cc 属性在模块中生成的 --cc 变量作用于粒子自身,然后粒子再将变量值传递给伪元素。
本讲主要讨论使用 lzRan()、lzRing() 和 lzRot() 三个指令实现粒子动画,内容难度中上,需要花点心思体会。下一部分的粒子动画探讨可能更为抽象难懂,敬请期待。