自定义加载动画
自定义加载动画就是一份由你编写的 JSON spec。每个 LoaderKit 引擎都会读取它并原生绘制,无需新增原生代码。本页会一步步构建一个加载动画,每一步都给出完整的 spec 和实时预览。拖动预览下方的滑块,可以在一个周期内来回查看。
实验性功能
自己编写 spec 目前仍是实验性功能。在 schema 宣布稳定之前,次版本更新可能会改动 schema 版本 1。内置加载动画及其参数不受影响。
核心概念
| 术语 | 含义 |
|---|---|
| 方框(Box) | 绘制加载动画的正方形,边长取视图较短的那条边。所有长度都是这个边长的比例,取值 0 到 1。原点在左上角,y 轴向下。 |
| 布局(Layout) | 在方框中摆放元素:single、stack、row、grid 或 ring。见布局。 |
| 形状(Shape) | 每个元素画什么:circle、rect、ring、triangle 或 line。见形状。 |
| Track(动画轨道) | 某个属性(缩放、不透明度、旋转等)在一个周期内如何通过关键帧变化。见 Track。 |
| 周期(Cycle) | 动画的一轮循环。duration 是它的时长,单位为秒。 |
| Stagger | 为每个元素设置启动延迟,让元素错开启动、依次运动。见时序。 |
| 参数(Params) | 用户可以覆盖的具名数字,比如 count。见参数。 |
角度的单位是弧度(一整圈是 6.283185307),正角度表示顺时针。时间的单位是秒。
第 1 步:一个会脉动的点
最小可用的 spec 包含名称、时长、布局、形状和一条 track。
{
"$schema": "https://maitrungduc1410.github.io/loader-kit/schema/v1.json",
"schemaVersion": 1,
"name": "FirstDot",
"duration": 1,
"layout": { "type": "single", "size": 0.5 },
"shape": { "type": "circle" },
"tracks": [
{ "property": "scale", "keyTimes": [0, 0.5, 1], "values": [1, 0.4, 1] }
]
}FirstDot在 playground 中打开 各字段的作用:
schemaVersion目前固定为1。duration: 1让一个周期持续 1 秒。layout是single:方框中央只有一个元素。size: 0.5让它占方框的一半。shape是circle:元素画一个实心圆。- 这条 track 驱动
scale。周期开始时(keyTimes为 0)缩放为 1,中间(0.5)为 0.4,结束时(1)回到 1。关键帧之间的值做线性变化。
$schema 这一行是可选的,有了它,编辑器就能检查文件并提示字段。见 JSON Schema。
第 2 步:一行三个点
把布局改成 3 个元素的 row,元素间距为 0.1。这一行会占满方框的宽度,所以每个点宽 (1 - 2 × 0.1) / 3。
{
"$schema": "https://maitrungduc1410.github.io/loader-kit/schema/v1.json",
"schemaVersion": 1,
"name": "FirstRow",
"duration": 1,
"layout": { "type": "row", "count": 3, "gap": 0.1 },
"shape": { "type": "circle" },
"tracks": [
{ "property": "scale", "keyTimes": [0, 0.5, 1], "values": [1, 0.4, 1] }
]
}FirstRow在 playground 中打开 这条 track 作用于所有元素,所以三个点一起脉动。
第 3 步:依次运动
stagger 会延迟每个元素的启动。设为 { "each": 0.15 } 时,元素 0 在第 0 秒启动,元素 1 在第 0.15 秒,元素 2 在第 0.3 秒。
{
"$schema": "https://maitrungduc1410.github.io/loader-kit/schema/v1.json",
"schemaVersion": 1,
"name": "FirstStagger",
"duration": 1,
"layout": { "type": "row", "count": 3, "gap": 0.1 },
"shape": { "type": "circle" },
"stagger": { "each": 0.15 },
"tracks": [
{ "property": "scale", "keyTimes": [0, 0.5, 1], "values": [1, 0.4, 1] }
]
}FirstStagger在 playground 中打开 元素启动之前,显示的是它的静止值(缩放 1,不透明度 1)。见静止值。
第 4 步:让动效更平滑
加上 easing,让动作在每个关键帧附近放慢;再加第二条 track,让点在缩小的同时淡出。同一个 track 列表里,每个属性最多只能有一条 track。
{
"$schema": "https://maitrungduc1410.github.io/loader-kit/schema/v1.json",
"schemaVersion": 1,
"name": "FirstEasing",
"duration": 1,
"layout": { "type": "row", "count": 3, "gap": 0.1 },
"shape": { "type": "circle" },
"stagger": { "each": 0.15 },
"tracks": [
{ "property": "scale", "keyTimes": [0, 0.5, 1], "values": [1, 0.4, 1], "easing": "easeInOut" },
{ "property": "opacity", "keyTimes": [0, 0.5, 1], "values": [1, 0.3, 1], "easing": "easeInOut" }
]
}FirstEasing在 playground 中打开 - 元素 0, 从 0 秒开始
- 元素 1, 从 0.15 秒开始
- 元素 2, 从 0.3 秒开始
时间线画出了每个元素的 track 值在一个周期内的变化。曲线之间的错位就是 stagger。
第 5 步:让用户可以调整
在 params 中声明参数及其默认值,然后在需要数字的地方写 { "$param": "name" }。这样用户无需新的 spec,就能要 5 个点,或者幅度更大的脉动。
{
"$schema": "https://maitrungduc1410.github.io/loader-kit/schema/v1.json",
"schemaVersion": 1,
"name": "FirstParams",
"duration": 1,
"params": { "count": 3, "minScale": 0.4 },
"layout": { "type": "row", "count": { "$param": "count" }, "gap": 0.1 },
"shape": { "type": "circle" },
"stagger": { "each": 0.15 },
"tracks": [
{ "property": "scale", "keyTimes": [0, 0.5, 1], "values": [1, { "$param": "minScale" }, 1], "easing": "easeInOut" },
{ "property": "opacity", "keyTimes": [0, 0.5, 1], "values": [1, 0.3, 1], "easing": "easeInOut" }
]
}FirstParams在 playground 中打开 在 App 中使用
用所在平台的引擎加载这份 JSON。各平台的做法见使用 spec。
<loader-kit id="dots" params='{"count": 5}'></loader-kit>
<script type="module">
import '@loader-kit/web/element';
document.querySelector('#dots').spec = await (await fetch('/first-params.json')).json();
</script>loader.spec = IndicatorSpec.parse(json)
loader.params = mapOf("count" to 5.0)loader.spec = try IndicatorSpec(json: json)
loader.params = ["count": 5]indicator.Spec = json;
indicator.Params = new Dictionary<string, double> { ["count"] = 5 };用 TypeScript 编写
也可以借助 @loader-kit/spec 用 TypeScript 编写 spec。defineIndicator() 会在运行时检查 spec 并自动补上 schemaVersion,param() 用来生成 $param 引用。把结果序列化,就得到每个引擎都能读取的 JSON。
npm install @loader-kit/specimport { defineIndicator, param } from '@loader-kit/spec';
export const Blink = defineIndicator({
name: 'Blink',
duration: 0.9,
params: { count: 4, low: 0.15 },
layout: { type: 'row', count: param('count'), gap: 0.08 },
shape: { type: 'rect', cornerRadius: 0.25 },
stagger: { each: 0.15 },
tracks: [
{ property: 'opacity', keyTimes: [0, 0.5, 1], values: [1, param('low'), 1], easing: 'easeInOut' },
],
});
console.log(JSON.stringify(Blink));defineIndicator() 出错时会抛出 InvalidIndicatorError,列出所有问题。50 个内置加载动画就是这样写出来的。