如何保持 animate.css 动画的到达状态?
How to keep the reached state of an animate.css animation?
我有一个元素起初是不可见的 (opacity: 0
)。
当动画被触发时,它淡入,但由于 opacity
的值似乎被重置,它在完成后再次消失。
如何防止重置动画属性?
我使用 animate.css 4.1.1 和 Google Chrome.
示例上下文:
HTML:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
<div class="main-container">
<div id="test1"></div>
</div>
CSS:
#test1 {
position: absolute;
margin: 10%;
width: 25%;
height: 25%;
opacity: 0;
background: blue;
}
下面的代码使用这个JS函数添加动画类(在animate.css website上找到):
const animateCSS = (element, animation, prefix = 'animate__') =>
// We create a Promise and return it
new Promise((resolve, reject) => {
const animationName = `${prefix}${animation}`;
const node = document.querySelector(element);
node.classList.add(`${prefix}animated`, animationName);
// When the animation ends, we clean the classes and resolve the Promise
function handleAnimationEnd(event) {
event.stopPropagation();
node.classList.remove(`${prefix}animated`, animationName);
resolve('Animation ended');
}
node.addEventListener('animationend', handleAnimationEnd, {once: true});
});
这里我尝试触发一个in动画,稍等片刻,然后有一个out动画。
描述的问题表现为闪烁(可见于此jsfiddle。)
首先我认为解决方案应该像更改 属性 一样简单。
完成后:
let test1 = document.getElementById("test1");
animateCSS("#test1", "flipInX") // flickers
.then(()=>{
test1.style.opacity = 1;
});
...
animateCSS("#test1", "flipOutX")
.then(()=>{
test1.style.opacity = 0;
});
立即:
let test1 = document.getElementById("test1");
animateCSS("#test1", "flipInX")
test1.style.opacity = 1;
...
animateCSS("#test1", "flipOutX") // flickers
test1.style.opacity = 0;
然后我认为闪烁是由任何动画延迟引起的。
所以我禁用了它:
test1.style.setProperty('--animate-delay', '0s');`
document.documentElement.style.setProperty('--animate-delay', '0s');
没有任何影响。
我做错了什么?
更新:
我最终使用了答案的修改版本:
function animateCss(node, animationName, duration = 1, prefix = 'animate__') {
const envCls = `${prefix}animated`;
const animationCls = `${prefix}${animationName}`;
// Remove all applied animate.css classes.
node.className = node.className
.split(" ")
.filter((cls) => !cls.startsWith(prefix))
.join(" ");
// Promise resolves when animation has ended.
return new Promise((resolve, reject) => {
node.addEventListener('animationend', (event) => {
event.stopPropagation();
resolve('Animation ended');
}, {once: true});
node.style.setProperty('--animate-duration', `${duration}s`);
node.classList.add(envCls, animationCls); // Starts CSS animation.
});
}
// --- Test ---
let test1 = document.getElementById("test1");
// hide the element at first.
animateCss(test1, "fadeOut", 0);
setTimeout(()=>{
animateCss(test1, "flipInX");
}, 1000);
setTimeout(()=>{
animateCss(test1, "zoomOut");
}, 5000);
我建议您在单独的 class 中将 opacity
替换为 transform
,并根据动画类型(“in”或“out”)切换 class ") 当 animationend
触发时。
此外,最好在开始动画之前设置事件处理程序。
const animateCSS = (element, animation, prefix = 'animate__') =>
new Promise((resolve, reject) => {
const animationName = `${prefix}${animation}`;
const node = document.querySelector(element);
const handleAnimationEnd = event => {
event.stopPropagation();
node.classList.remove(`${prefix}animated`, animationName);
node.classList.toggle('out', /out/i.test(animation));
}
node.addEventListener('animationend', handleAnimationEnd, {once: true});
node.classList.add(`${prefix}animated`, animationName);
});
// Test ------------------------------------------------------------
setTimeout(() => animateCSS("#test1", "flipInX"), 1000);
setTimeout(() => animateCSS("#test1", "flipOutX"), 3000);
setTimeout(() => animateCSS("#test1", "flipInX"), 6000);
setTimeout(() => animateCSS("#test1", "flipOutX"), 9000);
#test1 {
position: absolute;
width: 75%;
height: 75%;
background: blue;
}
.out {
transform:translateX(-9999vw)
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
<div id="test1" class="out"></div>
这里有几个问题:
- 动画
flipInX
和 flipOutX
already controls opacity。您不必自己管理它。
- 从元素中删除动画 class 后,动画效果就会撤消并且其状态会重置。因此,在动画结束后立即删除动画 classes 不会保留结束状态。我们需要在下一个动画开始之前删除旧动画 classes。
- 元素从水平到垂直进行动画处理。所以我们需要将 CSS 中元素的初始位置设置为 horizontal.
const animateCSS = (element, animation, lastAnim, prefix = 'animate__') => {
const animationName = `${prefix}${animation}`;
const node = document.querySelector(element);
// remove the last animation
node.classList.remove(`${prefix}animated`, `${prefix}${lastAnim}`);
// We create a Promise and return it
return new Promise((resolve, reject) => {
node.classList.add(`${prefix}animated`, animationName);
function handleAnimationEnd(event) {
event.stopPropagation();
//do not remove the class here
//node.classList.remove(`${prefix}animated`, animationName);
resolve('Animation ended');
}
node.addEventListener('animationend', handleAnimationEnd, { once: true});
});
}
// Test ------------------------------------------------------------
let test1 = document.getElementById("test1");
test1.style.setProperty('--animate-duration', '2s');
setTimeout(() => {
document.body.appendChild(document.createTextNode('1'));
animateCSS("#test1", "flipInX");
}, 1000);
setTimeout(() => {
document.body.appendChild(document.createTextNode('2'));
animateCSS("#test1", "flipOutX", "flipInX")
}, 3000);
setTimeout(() => {
document.body.appendChild(document.createTextNode('3'));
animateCSS("#test1", "flipInX", "flipOutX")
}, 6000);
setTimeout(() => {
document.body.appendChild(document.createTextNode('4'));
animateCSS("#test1", "flipOutX", "flipInX");
}, 9000);
#test1 {
position: absolute;
width: 25vw;
height: 25vw;
top:3rem;
left: 6rem;
background: blue;
/* starting state */
-webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
<div class="main-container">
<div id="test1"></div>
</div>
我有一个元素起初是不可见的 (opacity: 0
)。
当动画被触发时,它淡入,但由于 opacity
的值似乎被重置,它在完成后再次消失。
如何防止重置动画属性?
我使用 animate.css 4.1.1 和 Google Chrome.
示例上下文:
HTML:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
<div class="main-container">
<div id="test1"></div>
</div>
CSS:
#test1 {
position: absolute;
margin: 10%;
width: 25%;
height: 25%;
opacity: 0;
background: blue;
}
下面的代码使用这个JS函数添加动画类(在animate.css website上找到):
const animateCSS = (element, animation, prefix = 'animate__') =>
// We create a Promise and return it
new Promise((resolve, reject) => {
const animationName = `${prefix}${animation}`;
const node = document.querySelector(element);
node.classList.add(`${prefix}animated`, animationName);
// When the animation ends, we clean the classes and resolve the Promise
function handleAnimationEnd(event) {
event.stopPropagation();
node.classList.remove(`${prefix}animated`, animationName);
resolve('Animation ended');
}
node.addEventListener('animationend', handleAnimationEnd, {once: true});
});
这里我尝试触发一个in动画,稍等片刻,然后有一个out动画。
描述的问题表现为闪烁(可见于此jsfiddle。)
首先我认为解决方案应该像更改 属性 一样简单。
完成后:
let test1 = document.getElementById("test1"); animateCSS("#test1", "flipInX") // flickers .then(()=>{ test1.style.opacity = 1; }); ... animateCSS("#test1", "flipOutX") .then(()=>{ test1.style.opacity = 0; });
立即:
let test1 = document.getElementById("test1"); animateCSS("#test1", "flipInX") test1.style.opacity = 1; ... animateCSS("#test1", "flipOutX") // flickers test1.style.opacity = 0;
然后我认为闪烁是由任何动画延迟引起的。
所以我禁用了它:test1.style.setProperty('--animate-delay', '0s');` document.documentElement.style.setProperty('--animate-delay', '0s');
没有任何影响。
我做错了什么?
更新:
我最终使用了答案的修改版本:
function animateCss(node, animationName, duration = 1, prefix = 'animate__') {
const envCls = `${prefix}animated`;
const animationCls = `${prefix}${animationName}`;
// Remove all applied animate.css classes.
node.className = node.className
.split(" ")
.filter((cls) => !cls.startsWith(prefix))
.join(" ");
// Promise resolves when animation has ended.
return new Promise((resolve, reject) => {
node.addEventListener('animationend', (event) => {
event.stopPropagation();
resolve('Animation ended');
}, {once: true});
node.style.setProperty('--animate-duration', `${duration}s`);
node.classList.add(envCls, animationCls); // Starts CSS animation.
});
}
// --- Test ---
let test1 = document.getElementById("test1");
// hide the element at first.
animateCss(test1, "fadeOut", 0);
setTimeout(()=>{
animateCss(test1, "flipInX");
}, 1000);
setTimeout(()=>{
animateCss(test1, "zoomOut");
}, 5000);
我建议您在单独的 class 中将 opacity
替换为 transform
,并根据动画类型(“in”或“out”)切换 class ") 当 animationend
触发时。
此外,最好在开始动画之前设置事件处理程序。
const animateCSS = (element, animation, prefix = 'animate__') =>
new Promise((resolve, reject) => {
const animationName = `${prefix}${animation}`;
const node = document.querySelector(element);
const handleAnimationEnd = event => {
event.stopPropagation();
node.classList.remove(`${prefix}animated`, animationName);
node.classList.toggle('out', /out/i.test(animation));
}
node.addEventListener('animationend', handleAnimationEnd, {once: true});
node.classList.add(`${prefix}animated`, animationName);
});
// Test ------------------------------------------------------------
setTimeout(() => animateCSS("#test1", "flipInX"), 1000);
setTimeout(() => animateCSS("#test1", "flipOutX"), 3000);
setTimeout(() => animateCSS("#test1", "flipInX"), 6000);
setTimeout(() => animateCSS("#test1", "flipOutX"), 9000);
#test1 {
position: absolute;
width: 75%;
height: 75%;
background: blue;
}
.out {
transform:translateX(-9999vw)
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
<div id="test1" class="out"></div>
这里有几个问题:
- 动画
flipInX
和flipOutX
already controls opacity。您不必自己管理它。 - 从元素中删除动画 class 后,动画效果就会撤消并且其状态会重置。因此,在动画结束后立即删除动画 classes 不会保留结束状态。我们需要在下一个动画开始之前删除旧动画 classes。
- 元素从水平到垂直进行动画处理。所以我们需要将 CSS 中元素的初始位置设置为 horizontal.
const animateCSS = (element, animation, lastAnim, prefix = 'animate__') => {
const animationName = `${prefix}${animation}`;
const node = document.querySelector(element);
// remove the last animation
node.classList.remove(`${prefix}animated`, `${prefix}${lastAnim}`);
// We create a Promise and return it
return new Promise((resolve, reject) => {
node.classList.add(`${prefix}animated`, animationName);
function handleAnimationEnd(event) {
event.stopPropagation();
//do not remove the class here
//node.classList.remove(`${prefix}animated`, animationName);
resolve('Animation ended');
}
node.addEventListener('animationend', handleAnimationEnd, { once: true});
});
}
// Test ------------------------------------------------------------
let test1 = document.getElementById("test1");
test1.style.setProperty('--animate-duration', '2s');
setTimeout(() => {
document.body.appendChild(document.createTextNode('1'));
animateCSS("#test1", "flipInX");
}, 1000);
setTimeout(() => {
document.body.appendChild(document.createTextNode('2'));
animateCSS("#test1", "flipOutX", "flipInX")
}, 3000);
setTimeout(() => {
document.body.appendChild(document.createTextNode('3'));
animateCSS("#test1", "flipInX", "flipOutX")
}, 6000);
setTimeout(() => {
document.body.appendChild(document.createTextNode('4'));
animateCSS("#test1", "flipOutX", "flipInX");
}, 9000);
#test1 {
position: absolute;
width: 25vw;
height: 25vw;
top:3rem;
left: 6rem;
background: blue;
/* starting state */
-webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
<div class="main-container">
<div id="test1"></div>
</div>