如果是简单的颜色更改,我也会推荐像 Martin's answer 这样的 CSS 过渡,但正如您所解释的那样,它更复杂,Osama8DZ's answer 是您的最佳解决方案。但这并没有回答你的问题,它说明了以下内容(重点是我的):
当我点击小按钮时,我想反转动画,但我所做的不起作用,我不明白为什么。
原因:
当您单击触发反向动画的按钮时,类名会更改,因此原始动画替换为反向动画。这实际上是两个步骤 - 首先是删除原始动画,然后是添加反向动画。
只要元素上存在的动画被移除,该元素就会立即捕捉到其原始状态(即动画之前的状态)。此处元素在其原始状态下具有红色背景。因此元素立即获得红色背景,然后反向动画没有视觉效果,因为它是从红色背景动画到红色背景。
您可以在下面的 sn-p 中看到我的意思的示例。我为元素的原始状态添加了左边距和上边距,并在前向动画期间更改了它。执行反向按钮单击后,形状立即恢复到原始状态,然后应用反向动画中提供的左上边距。
var div = document.getElementById('fab');
function anim() {
div.className = "anim";
}
function animrev() {
div.className = "animrev";
}
html,
body {
height: 100%;
width: 100%;
}
#content {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#fab {
margin-right: 30px;
color: white;
font-size: 45px;
outline: 0;
border: 0;
height: 150px;
width: 150px;
border-radius: 75px;
text-align: center;
background-color: red;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
/* added these for demo */
margin-left: 20px;
margin-top: 10px;
}
.anim {
animation-name: example;
animation-duration: 1s;
animation-fill-mode: forwards;
}
@keyframes example {
100% {
background-color: green;
left: 0px;
top: 0px;
/* added these for demo */
margin-left: 40px;
margin-top: 20px;
}
}
@keyframes exampleux {
100% {
background-color: red;
left: 0px;
top: 0px;
/* added these for demo */
margin-left: 30px;
margin-top: 15px;
}
}
.animrev {
animation-name: exampleux;
animation-duration: 1s;
animation-fill-mode: forwards;
}
<div id="content">
<button onclick="anim()" id="fab">+</button>
<button onclick="animrev()" id="fab2">+</button>
</div>
解决方案:
简单且最佳的解决方案是Osama8DZ's answer 中提供的解决方案,因为它使原始元素看起来好像从未恢复到原始状态。我不打算进一步详细说明,因为这看起来像是在抄袭(或)抄袭他的答案。
但是,我将重点介绍一些它不起作用的情况以及如何解决它们。
案例 1:当动画有延迟时 - 当两个动画(或)只有反向动画被延迟时,您仍然可以在运行之前看到它恢复到原始状态动画。下面的 sn-p 显示了实际问题。
var div = document.getElementById('fab');
function anim() {
div.className = "anim";
}
function animrev() {
div.className = "animrev";
}
html,
body {
height: 100%;
width: 100%;
}
#content {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#fab {
margin-right: 30px;
color: white;
font-size: 45px;
outline: 0;
border: 0;
height: 150px;
width: 150px;
border-radius: 75px;
text-align: center;
background-color: red;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
}
.anim {
animation-name: example;
animation-duration: 1s;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes example {
100% {
background-color: green;
left: 0px;
top: 0px;
}
}
@keyframes exampleux {
0% {
/* You need to add this */
background-color: green;
left: 0px;
top: 0px;
}
100% {
background-color: red;
left: 0px;
top: 0px;
}
}
.animrev {
animation-name: exampleux;
animation-duration: 1s;
animation-fill-mode: forwards;
animation-delay: 1s;
}
<div id="content">
<button onclick="anim()" id="fab">+</button>
<button onclick="animrev()" id="fab2">+</button>
</div>
如何解决? 这是因为动画会继续保持其原始状态,直到延迟时间过去,所以即使反向动画中的0% 关键帧是与前向动画的100% 关键帧相同,元素仍会弹回然后再次动画。解决此问题的方法是将animation-fill-mode 设置为both(这意味着同时具有forwards 和backwards 的效果)而不是forwards。这使得元素在延迟期间也保持0% 关键帧的状态(由于backwards),并且即使在动画结束后也保持100% 关键帧的状态(由于forwards)。
var div = document.getElementById('fab');
function anim() {
div.className = "anim";
}
function animrev() {
div.className = "animrev";
}
html,
body {
height: 100%;
width: 100%;
}
#content {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#fab {
margin-right: 30px;
color: white;
font-size: 45px;
outline: 0;
border: 0;
height: 150px;
width: 150px;
border-radius: 75px;
text-align: center;
background-color: red;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
}
.anim {
animation-name: example;
animation-duration: 1s;
animation-fill-mode: both;
animation-delay: 1s;
}
@keyframes example {
100% {
background-color: green;
left: 0px;
top: 0px;
}
}
@keyframes exampleux {
0% {
/* You need to add this */
background-color: green;
left: 0px;
top: 0px;
}
100% {
background-color: red;
left: 0px;
top: 0px;
}
}
.animrev {
animation-name: exampleux;
animation-duration: 1s;
animation-fill-mode: both;
animation-delay: 1s;
}
<div id="content">
<button onclick="anim()" id="fab">+</button>
<button onclick="animrev()" id="fab2">+</button>
</div>
案例 2: 如果反向动画对于多个元素来说很常见,但正向动画却不同,就像下面的 sn-p 一样。在下面的 sn-p 中,第一个元素的正向动画将背景从red 更改为green,而第二个元素将其从red 更改为blue,但反向动画应将两者都设置回red。在这种情况下,我们不能将反向动画的0%关键帧设置为正向动画的100%,因为两个正向动画的结束状态不同。
var div = document.getElementById('fab');
var div2 = document.getElementById('fab3');
function anim() {
div.className = "anim";
}
function animAlt() {
div2.className = "anim-alt";
}
function animrev() {
div.className = "animrev";
div2.className = "animrev";
}
html,
body {
height: 100%;
width: 100%;
}
#content {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#fab, #fab3 {
margin-right: 30px;
color: white;
font-size: 45px;
outline: 0;
border: 0;
height: 150px;
width: 150px;
border-radius: 75px;
text-align: center;
background-color: red;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
}
.anim {
animation-name: example;
animation-duration: 1s;
animation-fill-mode: forwards;
}
@keyframes example {
100% {
background-color: green;
left: 0px;
top: 0px;
}
}
.anim-alt {
animation-name: example2;
animation-duration: 1s;
animation-fill-mode: forwards;
}
@keyframes example2 {
100% {
background-color: blue;
left: 0px;
top: 0px;
}
}
@keyframes exampleux {
0% {
/* You need to add this */
background-color: green;
left: 0px;
top: 0px;
}
100% {
background-color: red;
left: 0px;
top: 0px;
}
}
.animrev {
animation-name: exampleux;
animation-duration: 1s;
animation-fill-mode: forwards;
}
<div id="content">
<button onclick="anim()" id="fab">+</button>
<button onclick="animAlt()" id="fab3">+</button>
<button onclick="animrev()" id="fab2">+</button>
</div>
如何解决? 没有针对这种情况的纯 CSS 解决方案(除非您改用过渡)。但是有一种解决方法,即在元素结束后立即通过内联样式将原始动画的最后一个关键帧的样式添加到元素。这意味着我们正在模仿元素没有恢复到其原始状态的效果。
var div = document.getElementById('fab');
var div2 = document.getElementById('fab3');
function anim() {
div.className = "anim";
}
function animAlt() {
div2.className = "anim-alt";
}
function animrev() {
div.className = "animrev";
div2.className = "animrev";
}
div.addEventListener('animationend', function(e) {
if (e.animationName == "example") { /* meaning the forward animation has ended */
this.style.backgroundColor = 'green';
this.style.marginLeft = '40px';
this.style.marginTop = '20px';
}
else
this.style = null;
})
div2.addEventListener('animationend', function(e) {
if (e.animationName == "example2") { /* meaning the forward animation has ended */
this.style.backgroundColor = 'blue';
this.style.marginLeft = '40px';
this.style.marginTop = '20px';
}
else
this.style = null;
})
html,
body {
height: 100%;
width: 100%;
}
#content {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#fab, #fab3 {
margin-right: 30px;
color: white;
font-size: 45px;
outline: 0;
border: 0;
height: 150px;
width: 150px;
border-radius: 75px;
text-align: center;
background-color: red;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
margin-left: 20px;
margin-top: 10px;
}
.anim {
animation-name: example;
animation-duration: 1s;
animation-fill-mode: forwards;
}
@keyframes example {
100% {
background-color: green;
left: 0px;
top: 0px;
margin-left: 40px;
margin-top: 20px;
}
}
.anim-alt {
animation-name: example2;
animation-duration: 1s;
animation-fill-mode: forwards;
}
@keyframes example2 {
100% {
background-color: blue;
left: 0px;
top: 0px;
margin-left: 40px;
margin-top: 20px;
}
}
@keyframes exampleux {
100% {
background-color: red;
left: 0px;
top: 0px;
margin-left: 20px;
margin-top: 10px;
}
}
.animrev {
animation-name: exampleux;
animation-duration: 1s;
animation-fill-mode: forwards;
}
<div id="content">
<button onclick="anim()" id="fab">+</button>
<button onclick="animAlt()" id="fab3">+</button>
<button onclick="animrev()" id="fab2">+</button>
</div>
注意:我们不应该让内联样式保持原样,因为这会导致原始动画在第二次点击时出现问题。所以,我们必须监听动画的结束事件并移除内联样式(如果有的话)。 animationend 在一些版本中仍然需要浏览器前缀,但我会把这部分留给你。
我希望以上所有内容都为您提供了完整的解释,说明您的代码为何(似乎)不起作用,如何针对各种情况进行修复。