在 CSS3 中重新启动动画:比删除元素更好的方法吗?
Posted
技术标签:
【中文标题】在 CSS3 中重新启动动画:比删除元素更好的方法吗?【英文标题】:Restart animation in CSS3: any better way than removing the element? 【发布时间】:2011-09-10 05:49:59 【问题描述】:我有一个 CSS3 动画,需要在单击时重新启动。这是一个显示剩余时间的条形图。我正在使用 scaleY(0) 变换来创建效果。
现在我需要通过将条恢复到 scaleY(1) 并让它再次转到 scaleY(0) 来重新启动动画。 我第一次尝试设置 scaleY(1) 失败了,因为它需要同样的 15 秒才能恢复到全长。即使我将持续时间更改为 0.1 秒,我也需要延迟或链接 scaleY(0) 的分配以完成条形补充。 这么简单的任务感觉太复杂了。
我还发现了一个有趣的技巧,通过从文档中删除元素,然后重新插入它的克隆来重新启动动画: http://css-tricks.com/restart-css-animation/
它有效,但有没有更好的方法来重新启动 CSS 动画? 我正在使用 Prototype 和 Move.js,但我不限于它们。
【问题讨论】:
How do I re-trigger a WebKit CSS animation via javascript?的可能重复 您可以在更新的博客文章中阅读另一种强制重排元素的技术:element.offsetWidth = element.offsetWidth;
根据您的 CSS-Tricks 链接,我发现克隆是最好的解决方案。
TL;DR: e.style.animation = 'none'; e.offsetHeight; e.style.animation = ...;
或者,如果你正在使用类:e.classList.remove('a'); e.offsetHeight; e.classList.add('a');
【参考方案1】:
无需超时,使用reflow 应用更改:
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>
【讨论】:
你也可以通过调用properties/method中的任何一个来触发回流,而不仅仅是offsetHeight
。
不触发回流会影响性能吗?在性能方面,这与所选答案相比如何?
这不适用于应用了超过 1 个动画的元素。
@card100,你能举个例子吗?
这段代码中el.style.animation = null;
的行是什么?【参考方案2】:
只需通过 JavaScript 将 animation
属性设置为 "none"
,然后设置将属性更改为 ""
的超时,因此它再次从 CSS 继承。
这里的 Webkit 演示:http://jsfiddle.net/leaverou/xK6sa/ 但是,请记住,在实际使用中,您还应该包括 -moz-(至少)。
【讨论】:
谢谢莉亚。快到了:),如果我将动画更改为仅运行一次,我将无法获得相同的效果。当我单击时,动画不会重新开始。 非常感谢! - 但不幸的是,我无法让它在 Safari 中工作。 Chrome、Edge 和 Firefox 正在按预期工作。我使用以下代码:var anim = jQuery(mutation.target).find(".background.background-image:first").get(0); anim.style.WebkitAnimation = 'none'; anim.style.animation = 'none'; setTimeout(function() anim.style.WebkitAnimation = ''; anim.style.animation = ''; , 10);
不够好,因为有时timeout太长会出现漏洞,timeout太短则不生效。如果您需要在动画仍在播放时重新启动,则不推荐。
现在是 2019 年,vendor prefix 应该不再需要了。
为避免@Eric 描述的超时问题,您可以调用void element.offsetWidth;
来强制在动画属性更改之间进行重排,而不是使用超时。【参考方案3】:
如果你有一些 css3 动画类,例如 .blink 那么你可以 removeClass 一些元素和 addClass 考虑这个元素setTimeout,点击 1 毫秒。
$("#element").click(function()
$(this).removeClass("blink");
setTimeout(function()
$(this).addClass("blink);
,1 ///it may be only 1 milisecond, it's enough
);
【讨论】:
似乎不可靠。 1 ms 计时器对我不起作用,开始使用更高的值(在 Firefox 67 中)。 @FabienSnauwaert 感谢您的评论,您可能是对的,因为我在 ~FF 50 上进行了测试【参考方案4】:-
将动画实现为 CSS 描述符
将描述符添加到元素以启动动画
使用
animationend
事件处理函数在动画完成时删除描述符,以便下次您想要重新启动动画时再次添加它。
---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;
在这里试试:
https://jsfiddle.net/bigjosh/avp9Lk6r/50/
【讨论】:
使用animationend 事件是个好主意,尽管您并不真的想在动画运行时添加一个新的事件监听器。只需将其添加到 div 一次。【参考方案5】:@ZachB 关于 Web Animation API 的回答似乎是“正确的”™ 方式来做到这一点,但不幸的是,它似乎要求您通过 JavaScript 定义动画。但是它引起了我的注意,我发现了一些有用的相关内容:
Element.getAnimations()
和 Document.getAnimations()
截至 2021 年,对它们的支持非常好。
就我而言,我想同时重新启动页面上的所有动画,所以我要做的就是:
const replayAnimations = () =>
document.getAnimations().forEach((anim) =>
anim.cancel();
anim.play();
);
;
但在大多数情况下,人们可能希望选择他们重新启动的动画...
getAnimations
返回一堆 CSSAnimation
和 CSSTransition
对象,如下所示:
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
所以你可以使用animationName
和target
属性来选择你想要的动画(虽然有点迂回)。
编辑
这是一个方便的函数,仅使用 Document.getAnimations
可能会更兼容,并使用 TypeScript 进行演示/娱乐:
// 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();
;
【讨论】:
如果所有浏览器都支持这一点,Safari 总是迟到.. 请注意,Element.getAnimations()
和 Document.getAnimations()
的支持是不同的。后者似乎受到 Safari 的支持,除了 IE 之外的一切。所以为了更可靠,使用后者,你只需要做一些额外的手动过滤。【参考方案6】:
你也可以使用 display 属性,只需将 display 设置为 none。
display:none;
并且更改支持它阻止(或您想要的任何其他属性)。
display:block;
使用 JavaScript。
而且效果会非常好。
【讨论】:
【参考方案7】:Animation API 让您可以完全控制播放的时间和内容,并受到所有现代浏览器(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();
演示:https://jsfiddle.net/cstz9L8v/
参考资料:
https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect https://developer.mozilla.org/en-US/docs/Web/API/Animation【讨论】:
【参考方案8】:MDN上有一个答案,类似于reflow的做法:
<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";
);
);
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Tips
【讨论】:
对requestAnimationFrame
的两个嵌套调用不能保证足够,并且没有技术原因它们必须足够。这在功能上看起来比 setTimeout
更好,但实际上并非如此。
@AdamLeggett 你能详细说明一下吗?我最近不得不考虑这样做。
我并不像我希望的那样精通浏览器架构,但我的理解是重新启动动画取决于布局引擎,它与渲染引擎位于不同的线程中。 requestAnimationFrame
等待渲染引擎。技术上最正确的答案是使用animate
函数;不幸的是,第二个在技术上最正确的是void element.offsetWidth
。【参考方案9】:
在此页面上,您可以阅读有关重新启动元素动画的内容:https://css-tricks.com/restart-css-animation/ 这是我的例子:
<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>
但如果你愿意,你可以删除元素动画然后返回它:
function animation()
var elm = document.getElementById('circle');
elm.style.animation='';
setTimeout(function () elm.style.animation='selectss 2s ease-out';,10)
希望我能帮上忙!
【讨论】:
【参考方案10】:创建第二个“keyframe@”来重启你的动画,唯一的问题是你不能为重启的动画设置任何动画属性(它只是有点弹回来)
----- 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%;
【讨论】:
以上是关于在 CSS3 中重新启动动画:比删除元素更好的方法吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 jquery transit 中暂停和重新启动 css3 转换