英文:
How to interrupt current CSS animation and start another?
问题
以下是您要翻译的代码部分:
I have the following example code which creates toasts that fly out from the top of the screen, stay for a few seconds, and then fly away:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function createToast() {
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
document.body.appendChild(elem);
setTimeout(() => elem.remove(), 3000);
}
setInterval(createToast, 3000);
<!-- language: lang-css -->
body {
background-color: #d7d7d7;
}
.toast {
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
max-width: 200px;
transform: translate(0, 0);
background-color: #fff;
border-radius: 50px;
animation-name: toastin, toastout;
animation-duration: 0.3s, 0.5s;
animation-iteration-count: 1, 1;
animation-delay: 0s, 2.5s;
animation-fill-mode: forwards, forwards;
}
@keyframes toastin {
from {
opacity: 0;
transform: translate(0, -40px);
}
to {
opacity: 1;
transform: translate(0, 20px);
}
}
@keyframes toastout {
from {
opacity: 1;
transform: translate(0, 20px);
}
to {
opacity: 0;
transform: translate(50vw, 20px);
}
}
<!-- language: lang-html -->
<body><body>
<!-- end snippet -->
英文:
I have the following example code which creates toasts that fly out from the top of the screen, stay for a few seconds, and then fly away:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function createToast() {
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
document.body.appendChild(elem);
setTimeout(() => elem.remove(), 3000);
}
setInterval(createToast, 3000);
<!-- language: lang-css -->
body {
background-color: #d7d7d7;
}
.toast {
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
max-width: 200px;
transform: translate(0, 0);
background-color: #fff;
border-radius: 50px;
animation-name: toastin, toastout;
animation-duration: 0.3s, 0.5s;
animation-iteration-count: 1, 1;
animation-delay: 0s, 2.5s;
animation-fill-mode: forwards, forwards;
}
@keyframes toastin {
from {
opacity: 0;
transform: translate(0, -40px);
}
to {
opacity: 1;
transform: translate(0, 20px);
}
}
@keyframes toastout {
from {
opacity: 1;
transform: translate(0, 20px);
}
to {
opacity: 0;
transform: translate(50vw, 20px);
}
}
<!-- language: lang-html -->
<body><body>
<!-- end snippet -->
This works fine, but sometimes in my application another toast will be created before the current one is gone. In that case, I want the current toast to fly away immediately upon the creation of the new toast, instead of waiting 2.5 seconds.
For the life of me, I cannot figure this out. Is it possible? It seems so simple, but nothing I try seems to have any effect.
First I tried grabbing the existing toast element and setting the animation-delay
to 0s
, but that had no effect. Then I tried replacing it with an entire new animation, tried using !important
, etc. In all cases the toast just, like, disappears, with no animation happening.
Is there any way to just make the toast immediately finish its current animation in like 0.4 seconds when a new toast is created? So that it flies away before it collides with the incoming toast?
答案1
得分: 1
我对你的方法进行了一些调整:
-
不要同时为两个动画分配类名,只将
toastout
动画分配给一个特定的类,比如.out
。 -
当尝试移除提示时,调用
removeToast()
函数。它会给提示添加.out
类,这样你可以控制何时播放toastout
动画,然后在动画结束后使用animationend
事件移除元素。 -
每次添加新提示时,尝试在所有现有提示上调用
removeToast()
。
以下是完整的解决方案:
function createToast() {
// 移除所有现有的提示
[...document.querySelectorAll('.toast')].forEach(removeToast);
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
setTimeout(() => removeToast(elem), 3000);
document.body.appendChild(elem);
}
function removeToast(toast) {
// 如果提示已经被移除,忽略
if (!toast || toast.classList.contains('out')) return;
toast.classList.add('out');
toast.addEventListener('animationend', () => toast.remove());
}
body {
background-color: #d7d7d7;
}
.toast {
display: flex;
position: fixed;
justify-content: center;
align-items: center;
padding: 5px 10px;
max-width: 200px;
transform: translate(0, 0);
background-color: #fff;
border-radius: 50px;
animation-name: toastin;
animation-duration: 0.3s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}
.toast.out {
animation-name: toastout;
}
@keyframes toastin {
from {
opacity: 0;
transform: translate(0, -40px);
}
to {
opacity: 1;
transform: translate(0, 20px);
}
}
@keyframes toastout {
from {
opacity: 1;
transform: translate(0, 20px);
}
to {
opacity: 0;
transform: translate(50vw, 20px);
}
}
<body>
<button onclick="createToast()">添加提示</button>
</body>
希望这能帮助你实现你的目标。
英文:
I made some adjustments to your approach:
-
Instead of assigning both of the animations at the same time, only assign the
toastout
animation to a certain class, let's say.out
. -
When trying to remove a toast, call function
removeToast()
. It adds class.out
to that toast. So you can control when to play thetoastout
animation, then after the animation is played, remove the element using eventanimationend
. -
Each time a new toast is added, try to call
removeToast()
on every existing toast.
Here's the full solution:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function createToast() {
// remove all existing toasts
[...document.querySelectorAll('.toast')].forEach(removeToast);
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
setTimeout(() => removeToast(elem), 3000);
document.body.appendChild(elem);
}
function removeToast(toast) {
// if a toast is already removed, ignore
if (!toast || toast.classList.contains('out')) return;
toast.classList.add('out');
toast.addEventListener('animationend', () => toast.remove());
}
<!-- language: lang-css -->
body {
background-color: #d7d7d7;
}
.toast {
display: flex;
position: fixed;
justify-content: center;
align-items: center;
padding: 5px 10px;
max-width: 200px;
transform: translate(0, 0);
background-color: #fff;
border-radius: 50px;
animation-name: toastin;
animation-duration: 0.3s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}
.toast.out {
animation-name: toastout;
}
@keyframes toastin {
from {
opacity: 0;
transform: translate(0, -40px);
}
to {
opacity: 1;
transform: translate(0, 20px);
}
}
@keyframes toastout {
from {
opacity: 1;
transform: translate(0, 20px);
}
to {
opacity: 0;
transform: translate(50vw, 20px);
}
}
<!-- language: lang-html -->
<body>
<button onclick="createToast()">Add toast</button>
<body>
<!-- end snippet -->
答案2
得分: 0
您需要在函数再次调用时保留对"elem"的引用,并在函数内部将其移除。也许可以像这样实现:
let activeToast;
function clearToast() {
if (!activeToast) {
return;
}
activeToast.remove();
activeToast = null;
}
function createToast() {
clearToast();
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
activeToast = elem;
document.body.appendChild(elem);
setTimeout(() => clearToast(), 3000);
}
setInterval(createToast, 3000);
但如果您想跟踪动画本身,您可能会希望使用一些动画事件,您可以参考这篇文章:
CSS Animation Events in JavaScript
英文:
You need to keep a reference to elem and remove it when the function is called again. Maybe something like this:
let activeToast;
function clearToast() {
if(!activeToast){
return;
}
activeToast.remove();
activeToast = null;
}
function createToast() {
clearToast();
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
activeToast = elem;
document.body.appendChild(elem);
setTimeout(() => clearToast(), 3000);
}
setInterval(createToast, 3000);
But if you want to keep track of the animation itself there are some animation events you might want to use.
https://medium.com/front-end-weekly/css-animation-events-in-javascript-d2bfe702636d
答案3
得分: 0
你会觉得这是可能的,对吗?也许你需要添加一个变量来检查是否有一个提示框正在显示。如果有提示框正在显示,你可以立即应用 elem.remove()
,只显示新的提示框。请查看下面以查看是否适用于您。
let isToastDisplayed = false;
function createToast() {
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
document.body.appendChild(elem);
if (isToastDisplayed) {
const currentToast = document.querySelector(".toast");
currentToast.remove();
}
isToastDisplayed = true;
setTimeout(() => {
elem.remove();
isToastDisplayed = false;
}, 3000);
}
setInterval(createToast, 3000);
body {
background-color: #d7d7d7;
}
.toast {
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
max-width: 200px;
transform: translate(0, 0);
background-color: #fff;
border-radius: 50px;
animation-name: toastin, toastout;
animation-duration: 0.3s, 0.5s;
animation-iteration-count: 1, 1;
animation-delay: 0s, 2.5s;
animation-fill-mode: forwards, forwards;
}
@keyframes toastin {
from {
opacity: 0;
transform: translate(0, -40px);
}
to {
opacity: 1;
transform: translate(0, 20px);
}
}
@keyframes toastout {
from {
opacity: 1;
transform: translate(0, 20px);
}
to {
opacity: 0;
transform: translate(50vw, 20px);
}
}
<body></body>
英文:
You'd think it's possible, right? Maybe you need to add a variable to check whether there's a toast being displayed or not. If there's a toast being displayed, you can apply elem.remove() right away to only show the new toast. Check below to see if that works for you.
<!-- begin snippet: js hide: false console: true babel: null -->
<!-- language: lang-js -->
let isToastDisplayed = false;
function createToast() {
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
document.body.appendChild(elem);
if (isToastDisplayed) {
const currentToast = document.querySelector(".toast");
currentToast.remove();
}
isToastDisplayed = true;
setTimeout(() => {
elem.remove();
isToastDisplayed = false;
}, 3000);
}
setInterval(createToast, 3000);
<!-- language: lang-css -->
body {
background-color: #d7d7d7;
}
.toast {
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
max-width: 200px;
transform: translate(0, 0);
background-color: #fff;
border-radius: 50px;
animation-name: toastin, toastout;
animation-duration: 0.3s, 0.5s;
animation-iteration-count: 1, 1;
animation-delay: 0s, 2.5s;
animation-fill-mode: forwards, forwards;
}
@keyframes toastin {
from {
opacity: 0;
transform: translate(0, -40px);
}
to {
opacity: 1;
transform: translate(0, 20px);
}
}
@keyframes toastout {
from {
opacity: 1;
transform: translate(0, 20px);
}
to {
opacity: 0;
transform: translate(50vw, 20px);
}
}
<!-- language: lang-html -->
<body><body>
<!-- end snippet -->
答案4
得分: 0
不确定这是否直接回答了你的问题,但你可以尝试将动画分离到一个单独的类中...然后使用 JavaScript 检查现有的动画并添加该类!
检查一下:
function createToast() {
const existing = document.querySelectorAll('.toast');
if (existing.length > 0) {
existing.forEach(e => {
e.classList.add('toast-out');
});
}
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
document.body.appendChild(elem);
setTimeout(() => elem.remove(), 3000);
}
setInterval(createToast, 2000);
body {
background-color: #d7d7d7;
}
.toast {
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
max-width: 200px;
transform: translate(0, 0);
background-color: #fff;
border-radius: 50px;
animation-name: toastin;
animation-duration: 0.3s;
animation-iteration-count: 1;
animation-delay: 0s;
animation-fill-mode: forwards;
}
.toast-out {
animation-name: toastout;
animation-duration: 0.5s;
animation-iteration-count: 1;
animation-delay: 0s;
animation-fill-mode: forwards;
}
@keyframes toastin {
from {
opacity: 0;
transform: translate(0, -40px);
}
to {
opacity: 1;
transform: translate(0, 20px);
}
}
@keyframes toastout {
from {
opacity: 1;
transform: translate(0, 20px);
}
to {
opacity: 0;
transform: translate(50vw, 20px);
}
}
<body></body>
以上是你提供的代码的翻译部分。
英文:
Not sure if this directly answer your question but you might be able to separate the animation out on a separate class... then with js you can check for existing ones and add the class out!
check it out
<!-- begin snippet: js hide: false console: true babel: null -->
<!-- language: lang-js -->
function createToast() {
const existing = document.querySelectorAll('.toast')
if(existing.length > 0) {
existing.forEach(e => {
e.classList.add('toast-out')
})
}
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
document.body.appendChild(elem);
setTimeout(() => elem.remove(), 3000);
}
setInterval(createToast, 2000);
<!-- language: lang-css -->
body {
background-color: #d7d7d7;
}
.toast {
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
max-width: 200px;
transform: translate(0, 0);
background-color: #fff;
border-radius: 50px;
animation-name: toastin;
animation-duration: 0.3s;
animation-iteration-count: 1;
animation-delay: 0s;
animation-fill-mode: forwards;
}
.toast-out {
animation-name: toastout;
animation-duration: 0.5s;
animation-iteration-count: 1;
animation-delay: 0s;
animation-fill-mode: forwards;
}
@keyframes toastin {
from {
opacity: 0;
transform: translate(0, -40px);
}
to {
opacity: 1;
transform: translate(0, 20px);
}
}
@keyframes toastout {
from {
opacity: 1;
transform: translate(0, 20px);
}
to {
opacity: 0;
transform: translate(50vw, 20px);
}
}
<!-- language: lang-html -->
<body><body>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论