在 CSS3中重新启动动画: 有比删除元素更好的方法吗?

我有一个 CSS3动画,需要重新启动一次点击。这是一个显示还剩多少时间的酒吧。我使用 scaleY (0)转换来创建效果。

现在我需要重新启动动画,方法是将工具条恢复为 scaleY (1) ,并让它再次恢复为 scaleY (0)。 我第一次尝试设置 scaleY (1)失败,因为它需要15秒才能恢复到完整长度。即使我将持续时间更改为0.1秒,也需要延迟或链接 scaleY (0)的分配,以完成条形码的补充。 对于这么简单的任务来说太复杂了。

我还发现了一个有趣的技巧,通过从文档中删除元素,然后重新插入一个克隆的元素来重新启动动画: Http://css-tricks.com/restart-css-animation/

它工作,但是有没有更好的方法来重新启动一个 CSS 动画? 我使用的是 Prototype 和 动起来,但不限于它们。

114386 次浏览

Just set the animation property via JavaScript to "none" and then set a timeout that changes the property to "", so it inherits from the CSS again.

Demo for Webkit here: http://jsfiddle.net/leaverou/xK6sa/ However, keep in mind that in real world usage, you should also include -moz- (at least).

On this page you can read about restarting the element animation: Restart CSS Animation (CSS Tricks)

Here is my example:

<head>
<style>
@keyframes selectss
{
0%{opacity: 0.7;transform:scale(1);}
100%{transform:scale(2);opacity: 0;}
}
</style>
<script>
function animation()
{
var elm = document.getElementById('circle');
elm.style.animation='selectss 2s ease-out';
var newone = elm.cloneNode(true);
elm.parentNode.replaceChild(newone, elm);
}
</script>
</head>
<body>
<div id="circle" style="height: 280px;width: 280px;opacity: 0;background-color: aqua;border-radius: 500px;"></div>
<button onclick="animation()"></button>
</body>

But if you want to you can just remove the element animation and then return it:

function animation()
{
var elm = document.getElementById('circle');
elm.style.animation='';
setTimeout(function () {elm.style.animation='selectss 2s ease-out';},10)
}

If you have a class for CSS3 animation, for example .blink, then you can removeClass for some element and addClass for this element thought setTimeout with 1 millisecond by click.

$("#element").click(function(){
$(this).removeClass("blink");


setTimeout(function(){
$(this).addClass("blink);
},1 // it may be only 1 millisecond, but it's enough
});

No need in timeout, use reflow to apply the change:

function reset_animation() {
var el = document.getElementById('animated');
el.style.animation = 'none';
el.offsetHeight; /* trigger reflow */
el.style.animation = null;
}
#animated {
position: absolute;
width: 50px; height: 50px;
background-color: black;
animation: bounce 3s ease-in-out infinite;
}
@keyframes bounce {
0% { left: 0; }
50% { left: calc( 100% - 50px ); }
100% { left: 0; }
}
<div id="animated"></div>
<button onclick="reset_animation()">Reset</button>

Create a second "keyframe@" which restarts you animation, only problem with this you cannot set any animation properties for the restarting animation (it just kinda pops back)

HTML

<div class="slide">
Some text..............
<div id="slide-anim"></div>
</div><br>
<button onclick="slider()"> Animation </button>
<button id="anim-restart"> Restart Animation </button>
<script>
var animElement = document.getElementById('slide-anim');
document.getElementById('anim-restart').addEventListener("mouseup", restart_slider);
    

function slider() {
animElement.style.animationName = "slider";             // other animation properties are specified in CSS
}
function restart_slider() {
animElement.style.animation = "slider-restart";
}
</script>

CSS

.slide {
position: relative;
border: 3px black inset;
padding: 3px;
width: auto;
overflow: hidden;
}
.slide div:first-child {
position: absolute;
width: 100%;
height: 100%;
background: url(wood.jpg) repeat-x;
left: 0%;
top: 0%;
animation-duration: 2s;
animation-delay: 250ms;
animation-fill-mode: forwards;
animation-timing-function: cubic-bezier(.33,.99,1,1);
}


@keyframes slider {
to {left: 100%;}
}


@keyframes slider-restart {
to {left: 0%;}
}
  1. Implement the animation as a CSS descriptor
  2. Add the descriptor to an element to start the animation
  3. Use a animationend event handler function to remove the descriptor when the animation completes so that it will be ready to be added again next time you want to restart the animation.

HTML

<div id="animatedText">
Animation happens here
</div>


<script>
function startanimation(element) {
element.classList.add("animateDescriptor");
element.addEventListener( "animationend",  function() {
element.classList.remove("animateDescriptor");
} );
}
</script>


<button onclick="startanimation(
document.getElementById('animatedText') )">
Click to animate above text
</button>

CSS

@keyframes fadeinout {
from { color: #000000; }
25% {color: #0000FF; }
50% {color: #00FF00; }
75% {color: #FF0000; }
to { color : #000000; }
}
        

.animateDescriptor {
animation: fadeinout 1.0s;
}

Try it here: jsfiddle

There is an answer on MDN, which is similar to the reflow approach:

<div class="box">
</div>


<div class="runButton">Click me to run the animation</div>
@keyframes colorchange {
0% { background: yellow }
100% { background: blue }
}


.box {
width: 100px;
height: 100px;
border: 1px solid black;
}


.changing {
animation: colorchange 2s;
}
function play() {
document.querySelector(".box").className = "box";
window.requestAnimationFrame(function(time) {
window.requestAnimationFrame(function(time) {
document.querySelector(".box").className = "box changing";
});
});
}

You can also use display property, just set the display to none.

display:none;

and the change backs it to block (or any other property you want).

display:block;

using JavaScript.

and it will work amazingly.

The Animation API gives you full control over when and what to play, and is supported by all modern browsers (Safari 12.1+, Chrome 44+, Firefox 48+, Edge 79+) .

const effect = new KeyframeEffect(
el, // Element to animate
[ // Keyframes
{transform: "translateY(0%)"},
{transform: "translateY(100%)"}
],
{duration: 3000, direction: "alternate", easing: "linear"} // Keyframe settings
);


const animation = new Animation(effect, document.timeline);


animation.play();

Demo: https://jsfiddle.net/cstz9L8v/

References:

@ZachB's answer about the Web Animation API seems like "right"™ way to do this, but unfortunately seems to require that you define your animations through JavaScript. However it caught my eye and I found something related that's useful:

Element.getAnimations() and Document.getAnimations()

The support for them is pretty good as of 2021.

In my case, I wanted to restart all the animations on the page at the same time, so all I had to do was this:

const replayAnimations = () => {
document.getAnimations().forEach((anim) => {
anim.cancel();
anim.play();
});
};

But in most cases people will probably want to select which animation they restart...

getAnimations returns a bunch of CSSAnimation and CSSTransition objects that look like this:

animationName: "fade"
currentTime: 1500
effect: KeyframeEffect
composite: "replace"
pseudoElement: null
target: path.line.yellow
finished: Promise {<fulfilled>: CSSAnimation}
playState: "finished"
ready: Promise {<fulfilled>: CSSAnimation}
replaceState: "active"
timeline: DocumentTimeline {currentTime: 135640.502}


# ...etc

So you could use the animationName and target properties to select just the animations you want (albeit a little circuitously).


EDIT

Here's a handy function that might be more compatible using just Document.getAnimations, with TypeScript thrown in for demonstration/fun:

// restart animations on a given dom element
const restartAnimations = (element: Element): void => {
for (const animation of document.getAnimations()) {
if (element.contains((animation.effect as KeyframeEffect).target)) {
animation.cancel();
animation.play();
}
}
};

Note that with React, clearing the animation like this, a codesandbox I found helps.

Example I used in my code:

function MyComponent() {
const [shouldTransition, setShouldTransition] = useState(true);


useEffect(() => {
setTimeout(() => {
// in my code, I change a background image here, and call this hook restart then animation,
// which first clears the animationName
setShouldTransition(false);
}, timeout * 1000);
}, [curr]);


useEffect(() => {
// then restore the animation name after it was cleared
if (shouldTransition === false) {
setShouldTransition(true);
}
}, [shouldTransition]);
return (
<div
ref={ref2}
style=\{\{
animationName: shouldTransition ? "zoomin" : "",
}}
/>
);
}
setInterval(() => {
$('#XMLID_640_').css('animation', 'none')


setTimeout(() => {
$('#XMLID_640_').css('animation', '')
}, 3000)
}, 13000)

If you create two identical sets of keyframes, you can "restart" the animation by swapping between them:

function restart_animation(element) {
element.classList.toggle('alt')
}
@keyframes spin1 {
to { transform: rotateY(360deg); }
}
@keyframes spin2 {
to { transform: rotateY(360deg); }
}
.spin {
animation-name: spin1;
animation-duration: 2s;
}
.alt {
animation-name: spin2;
}


div {
width: 100px;
background: #8CF;
padding: 5px;
}
<div id=_square class=spin>
<button onclick="restart_animation(_square)">
Click to restart animation
</button>
</div>

I found out a simple solution today. Using the example provided in this answer, you can just append the element again to the body:

function resetAnimation() {
let element = document.getElementById('animated');
document.body.append(element);
}
#animated {
position: absolute;
width: 50px;
height: 50px;
background-color: LightSalmon;
animation: bounce 3s ease-in-out infinite;
}
@keyframes bounce {
0% {left: 0;}
50% {left: calc(100% - 50px);}
100% {left: 0;}
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="animated"></div>
<button onclick="resetAnimation()">Reset</button>
</body>
</html>

Using Chrome's developer tools, the append does not actually append the element to the body and just replace it, probably because the same reference to the element is used.