<pc-anim>
<pc-anim> 在实体层级上播放动画,例如角色行走、机械连续动作或开门。
- 必须直接位于
<pc-entity>、<pc-model>或<pc-node>下。 - 可包含 0 到多个
<pc-anim-clip>,各声明一个命名片段。
片段来源
组件自动选择以下两种方式:
显式片段。 各 <pc-anim-clip> 提供一个命名动作,适合自定义名称、逐片段速度/循环,或混合多个文件:
<pc-model name="robot" asset="arm">
<pc-anim clip="idle" transition-time="0.35">
<pc-anim-clip name="idle"></pc-anim-clip>
<pc-anim-clip name="pick"></pc-anim-clip>
<pc-anim-clip name="stow" loop="false"></pc-anim-clip>
</pc-anim>
</pc-model>
模型全部动画。 无片段子项且直接位于 <pc-model> 内时,按容器顺序添加全部动画,以轨道命名。空标签即可播放 GLB 自带内容:
<pc-model name="t-rex" asset="t-rex">
<pc-anim></pc-anim>
</pc-model>
显式片段优先,添加任一子片段会切换为声明集合。默认自动播放第一段,可用 activate=false 关闭。
名称包含点号或与更早轨道重复时会跳过并逐项警告,点号保留给混合树路径。
直接位于模型内时,组件附加到模型宿主,不需要也不应额外包装实体。模型内容在同宿主下,动画可以访问。修改 asset 保留宿主及组件,并重新针对新容器解析片段。
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
activate | Boolean(布尔) | "true" | 分配片段后自动播放,不会停止已在播放的动作 |
clip | String(字符串) | 第一个分配片段 | 活动片段名称,修改后按 transition-time 淡变。无匹配时警告并保留选择 |
enabled | Boolean(布尔) | "true" | 组件启用状态 |
speed | Number(数字) | "1" | 全部片段的速度倍率,0 冻结 |
transition-time | Number(数字) | "0" | 通过 clip 切换时的淡变秒数,0 为硬切 |
此处 speed 与逐片段速度相乘,例如片段 1.4、组件 0.5,最终 0.7。
切换片段
clip 实时指定播放项,可从标记、开发者工具或 JavaScript 修改。transition-time 大于 0 时交叉淡变,避免角色姿态突变:
<pc-anim id="hero" clip="idle" transition-time="0.3">
<pc-anim-clip name="idle"></pc-anim-clip>
<pc-anim-clip name="run"></pc-anim-clip>
</pc-anim>
document.getElementById('hero').setAttribute('clip', 'run'); // cross-fades over 0.3s
元素方法补充属性无法表达的操作,例如默认淡变时临时硬切、指定一次性时长和暂停:
const anim = document.getElementById('hero');
anim.play('run'); // hard cut, ignoring transition-time
anim.transition('idle', 1); // one-second fade, just this once
anim.pause(); // freeze, keeping the playhead
anim.play(); // resume from where it stopped
未知名称保留原播放。clip 会在控制台警告,play() 与 transition() 则静默忽略。
轨道如何绑定
轨道按节点名称绑定到组件实体下的整个层级。骨架是常见情况,但普通刚性层级也适用。机械臂示例无需蒙皮即可驱动十个部件。
引擎只解析曲线一次,不重试。元素会在下方模型就绪时重新绑定,并在来源模型重新实例化后刷新动作库,因此运行时修改模型 asset也能自动工作。
还会将 rootBone 指向所属模型宿主,使根节点曲线正确绑定。每轮重新推导,无法确定单一模型时清空;通过引擎 API 手动设置的 rootBone 则会被识别并保留。
注意,引擎不报告片段完成。非循环动作停在末姿态但无 end 事件。需要检测时,比较底层层的活动时间与轨道时长:
const { baseLayer } = anim.component;
const done = baseLayer.activeStateCurrentTime >= baseLayer.activeStateDuration;
这适合正放。负 speed 从零向下计时,不会到达正的 duration。
示例
单轨行走 GLB 声明为正常 walk 和三分之一速度 stalk,通过 transition-time=0.4 淡变。可修改逐片段 speed、关闭 activate,或移除子片段和 clip="walk",恢复自动播放模型自带动画:
<pc-app>
<pc-asset src="https://cdn.jsdelivr.net/npm/[email protected]/scripts/esm/camera-controls.mjs"></pc-asset>
<pc-asset src="https://developer.playcanvas.com/assets/t-rex.glb" id="t-rex"></pc-asset>
<pc-material id="floor" diffuse="#3a3f4b"></pc-material>
<pc-scene>
<pc-entity name="camera" position="2.5 1.5 3.5">
<pc-camera clear-color="#2a2d36"></pc-camera>
<pc-script>
<pc-script-instance name="cameraControls" focus-point="0 1.2 0" pitch-range="-90 0" zoom-range="1.5 10"></pc-script-instance>
</pc-script>
</pc-entity>
<pc-entity name="light" rotation="45 30 0">
<pc-light cast-shadows normal-offset-bias="0.05" shadow-bias="0.2" shadow-distance="20" intensity="1.5"></pc-light>
</pc-entity>
<pc-entity name="ground" scale="30 30 30">
<pc-render type="plane" material="floor"></pc-render>
</pc-entity>
<pc-model name="t-rex" asset="t-rex" scale="1.5 1.5 1.5">
<pc-anim id="anim" clip="walk" transition-time="0.4">
<pc-anim-clip name="walk"></pc-anim-clip>
<pc-anim-clip name="stalk" speed="0.33"></pc-anim-clip>
</pc-anim>
</pc-model>
</pc-scene>
</pc-app>
<div class="controls">
<button id="walk">Walk</button>
<button id="stalk">Stalk</button>
<button id="transport">Pause</button>
</div>
<style>
.controls {
position: absolute;
top: 12px;
left: 12px;
display: flex;
gap: 8px;
}
</style>
<script type="module">
const anim = document.getElementById('anim');
const transport = document.getElementById('transport');
// The clips are what guarantee the layer exists, so wait for them rather than the app
await Promise.all([...anim.querySelectorAll('pc-anim-clip')].map(clip => clip.ready()));
let playing = true;
document.getElementById('walk').onclick = () => anim.setAttribute('clip', 'walk');
document.getElementById('stalk').onclick = () => anim.setAttribute('clip', 'stalk');
transport.onclick = () => {
playing = !playing;
playing ? anim.play() : anim.pause();
transport.textContent = playing ? 'Pause' : 'Play';
};
</script>
机械臂示例还展示六段动作、可拖动播放头和混合开关。
JavaScript 接口
可以通过 AnimComponentElement API创建和操作元素。
| 成员 | 说明 |
|---|---|
play(name?) | 恢复播放,可先硬切指定片段,忽略 transition-time |
pause() | 暂停并保留播放头 |
transition(name, time?) | 淡变到指定片段并确保播放,time 默认 transition-time |
clips | 按分配顺序列出的名称 |
component | 底层 AnimComponent |
activate、clip、speed、transitionTime 也有对应 property。
只等元素不足以保证片段可播:宿主就绪即创建组件,而各轨道独立加载。需要层中已有轨道时,应等待片段:
import { whenReady } from '@playcanvas/web-components';
const anim = await whenReady('pc-anim');
await Promise.all([...anim.querySelectorAll('pc-anim-clip')].map(clip => clip.ready()));
console.log(anim.clips); // ['walk', 'stalk']
更多功能可通过底层 AnimComponent访问,包括活动时间 baseLayer.activeStateCurrentTime,循环时持续累加、片段时长,以及状态图和混合树。
另请参阅
<pc-anim-clip>:声明片段。<pc-model>:常用宿主与轨道来源。<pc-asset>:独立 GLB 或 animclip JSON。