我是 JavaScript 新手,正在开发启动应用程序。我正在尝试编写代码,使动画在同一文本元素上执行多次(按顺序),并且文本在动画之间变化。在第一个动画完成之前,不应发生文本更改和第二个动画,依此类推。
使用我编写的代码,文本在单个动画的过程中会更改几次,然后无限期地挂起。我假设这是异步/等待或动画结束侦听器的问题......有更多经验的人(基本上每个人,哈哈)有一个方向可以指出我吗?如果有任何意见,我将不胜感激!
这是带有我的 JS 代码的示例 HTML:
更新:添加了可执行堆栈片段。
async function initAnim() {
await setDialogue("First text");
await setDialogue("Second text");
await setDialogue("Third text");
await setDialogue("Fourth text");
await setDialogue("Fifth text");
}
async function setDialogue(text) {
const dialogueEl = document.querySelector(".dialogue");
dialogueEl.textContent = text;
dialogueEl.classList.add("dialogueEnter");
await waitforAnimation(dialogueEl);
dialogueEl.classList.remove("dialogueEnter");
}
function waitforAnimation(element) {
return new Promise(resolve => {
function handleAnimationEnd() {
element.removeEventListener('animationend', handleAnimationEnd);
resolve();
}
element.addEventListener('animationend', handleAnimationEnd);
});
}
.dialogueEnter {
animation: tilt-n-move-shaking 0.25s forwards,
text-pulse 0.6s forwards;
}
@keyframes tilt-n-move-shaking {
0% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(10px, 10px) rotate(10deg); }
50% { transform: translate(0, 0) rotate(0eg); }
75% { transform: translate(-10px, 10px) rotate(-10deg); }
100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes text-pulse {
0% { font-size: 1em }
75% { font-size: 2em }
100% { font-size: 1.6em }
}
<div>
<h1 class="dialogue" onclick="initAnim()">Click me</h1>
</div>
这里有两个问题。
第一个,也是最大的一个,是在两次调用
setDialogue
之间,您确实同步删除和添加类。
确实,如果我们解开代码,
dialogueEl.textContent = "First text";
await waitforAnimation(dialogueEl);
// Sync after this line
dialogueEl.classList.remove("dialogueEnter"); // <- end first call to setDialogue()
dialogueEl.textContent = "Second text"; // <- begin second call to setDialogue()
dialogueEl.classList.add("dialogueEnter");
对于 CSSOM,classList 从未改变,因为它只会在下一次回流期间重新计算,因此它不会看到新的动画应该开始。您可以参考我的这个答案
以获取有关此行为的更多详细信息。一个快速解决方法是使用其中一个触发器来触发回流。 第二个问题是您为每个元素设置两个具有不同持续时间的动画。您捕获的
animationend
事件是较短的
tilt-n-move-shaking
之一。要仅等待较长的一个,您可以检查收到的动画事件的 animationName
属性:
async function initAnim() {
await setDialogue("First text");
await setDialogue("Second text");
await setDialogue("Third text");
await setDialogue("Fourth text");
await setDialogue("Fifth text");
}
async function setDialogue(text) {
const dialogueEl = document.querySelector(".dialogue");
dialogueEl.textContent = text;
dialogueEl.offsetWidth; // force a reflow
dialogueEl.classList.add("dialogueEnter");
await waitforAnimation(dialogueEl);
dialogueEl.classList.remove("dialogueEnter");
}
function waitforAnimation(element) {
return new Promise(resolve => {
function handleAnimationEnd(evt) {
if (evt.animationName === "text-pulse") {
element.removeEventListener('animationend', handleAnimationEnd);
resolve();
} else {
console.log("ignoring ", evt.animationName);
}
}
element.addEventListener('animationend', handleAnimationEnd);
});
}
.dialogueEnter {
animation: tilt-n-move-shaking 0.25s forwards,
text-pulse 0.6s forwards;
}
@keyframes tilt-n-move-shaking {
0% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(10px, 10px) rotate(10deg); }
50% { transform: translate(0, 0) rotate(0deg); } /* <- There was a typo here */
75% { transform: translate(-10px, 10px) rotate(-10deg); }
100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes text-pulse {
0% { font-size: 1em }
75% { font-size: 2em }
100% { font-size: 1.6em }
}
<div>
<h1 class="dialogue" onclick="initAnim()">Click me</h1>
</div>
但是,所有这些都可以通过
Web Animation API
const shakingKeyframes = [
{ transform: "translate(0, 0) rotate(0deg)" },
{ transform: "translate(10px, 10px) rotate(10deg)" },
{ transform: "translate(0, 0) rotate(0deg)" },
{ transform: "translate(-10px, 10px) rotate(-10deg)" },
{ transform: "translate(0, 0) rotate(0deg)" },
];
const shakingOptions = { duration: 250, fill: "forwards" };
const pulseKeyframes = [
{ fontSize: "1em" },
{ fontSize: "2em" },
{ fontSize: "1.6em" },
];
const pulseOptions = { duration: 600, fill: "forwards" };
async function initAnim() {
await setDialogue("First text");
await setDialogue("Second text");
await setDialogue("Third text");
await setDialogue("Fourth text");
await setDialogue("Fifth text");
}
async function setDialogue(text) {
const dialogueEl = document.querySelector(".dialogue");
dialogueEl.textContent = text;
const shakingAnim = dialogueEl.animate(shakingKeyframes, shakingOptions);
const pulseAnim = dialogueEl.animate(pulseKeyframes, pulseOptions);
await Promise.all([shakingAnim.finished, pulseAnim.finished]);
}
<div>
<h1 class="dialogue" onclick="initAnim()">Click me</h1>
</div>