悬停时旋转元素时的奇怪行为

Weird behavior when rotating an element on hover

鉴于这两个例子。

使用悬停

div {
  position: absolute;
  width: 150px;
  height: 40px;
  background: orange;
  left: 50%;
  top: var(--top);
  transition: transform 2s;
  transform: translateX(-50%);
  text-align: center;
  line-height: 40px;
  font-size: 1.2em;
}

div:hover {
  transform: translateX(-50%) rotate(var(--deg));
}

div:nth-child(1) {
  --deg: 180deg;
  --top: 20%;
}

div:nth-child(2) {
  --deg: -180deg;
  --top: 40%;
}

div:nth-child(3) {
  --deg: 360deg;
  --top: 60%;
}
<div>180deg</div>
<div>-180deg</div>
<div>360deg</div>


使用动画

div {
  position: absolute;
  width: 150px;
  height: 40px;
  background: orange;
  left: 50%;
  top: var(--top);
  transform: translateX(-50%);
  text-align: center;
  line-height: 40px;
  font-size: 1.2em;
  animation: rotate 2s linear 2s;
}

@keyframes rotate {
  to {
    transform: translateX(-50%) rotate(var(--deg));
  }
}

div:nth-child(1) {
  --deg: 180deg;
  --top: 20%;
}

div:nth-child(2) {
  --deg: -180deg;
  --top: 40%;
}

div:nth-child(3) {
  --deg: 360deg;
  --top: 60%;
}
<div>180deg</div>
<div>-180deg</div>
<div>360deg</div>


如您所见,rotate(180deg)rotate(-180deg) 行为相同,而 rotate(360deg) 根本不动。

问题是,如果你让它逐渐移动,它就会正常运行。

div {
  position: absolute;
  width: 150px;
  height: 40px;
  background: orange;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  line-height: 40px;
  font-size: 1.2em;
}

div:hover {
  animation: rotate 2s linear;
}

@keyframes rotate {
  0% {
    transform: translate(-50%, -50%) rotate(0deg);
  }
  25% {
    transform: translate(-50%, -50%) rotate(45deg);
  }
  50% {
    transform: translate(-50%, -50%) rotate(90deg);
  }
  75% {
    transform: translate(-50%, -50%) rotate(135deg);
  }
  100% {
    transform: translate(-50%, -50%) rotate(180deg);
  }
}
<div></div>

我找到的解决方案是将 translate(-50%, -50%) 替换为 margins,这是不一致的

div {
  position: absolute;
  width: 150px;
  height: 40px;
  background: orange;
  left: 50%;
  top: var(--top);
  transition: transform 2s;
  /* Minus half the width, hard coded not a good idea*/
  margin: 0 0 0 -75px; 
  text-align: center;
  line-height: 40px;
  font-size: 1.2em;
}

div:hover {
  transform: rotate(var(--deg));
}

div:nth-child(1) {
  --deg: 180deg;
  --top: 20%;
}

div:nth-child(2) {
  --deg: -180deg;
  --top: 40%;
}

div:nth-child(3) {
  --deg: 360deg;
  --top: 60%;
}
<div>180deg</div>
<div>-180deg</div>
<div>360deg</div>

所以主要问题是为什么会发生这种奇怪的行为?

编辑:不只是寻找快速答案(如您所见,有两个可用答案),还需要解释:)

你需要设置一个初始值rotate(0),这样两个状态之间才能有动画。将 div 的初始转换设置为:

transform: translateX(-50%) rotate(0);

过渡:

div {
  position: absolute;
  width: 150px;
  height: 40px;
  background: orange;
  left: 50%;
  top: var(--top);
  transition: transform 2s;
  transform: translateX(-50%) rotate(0);
  text-align: center;
  line-height: 40px;
  font-size: 1.2em;
}

div:hover {
  transform: translateX(-50%) rotate(var(--deg));
}

div:nth-child(1) {
  --deg: 180deg;
  --top: 20%;
}

div:nth-child(2) {
  --deg: -180deg;
  --top: 40%;
}

div:nth-child(3) {
  --deg: 360deg;
  --top: 60%;
}

body {
  overflow: hidden;
}
<div>180deg</div>
<div>-180deg</div>
<div>360deg</div>

动画:

div {
  position: absolute;
  width: 150px;
  height: 40px;
  background: orange;
  left: 50%;
  top: var(--top);
  transform: translateX(-50%) rotate(0);
  text-align: center;
  line-height: 40px;
  font-size: 1.2em;
  animation: rotate 2s linear 2s forwards;
}

@keyframes rotate {
  to {
    transform: translateX(-50%) rotate(var(--deg));
  }
}

div:nth-child(1) {
  --deg: 180deg;
  --top: 20%;
}

div:nth-child(2) {
  --deg: -180deg;
  --top: 40%;
}

div:nth-child(3) {
  --deg: 360deg;
  --top: 60%;
}

body {
  overflow: hidden;
}
<div>180deg</div>
<div>-180deg</div>
<div>360deg</div>

2021:该错误不再出现

这似乎是一个浏览器错误(至少在 Chrome 上),因为如果您在 Firefox 上尝试代码,它工作正常。

让我们参考 the specification来解释这个。这是变换之间的插值应该如何工作的所有不同情况。

在我们的例子中,我们将考虑最后一点,我们没有相同数量的转换函数,浏览器应该通过添加缺失列表中的身份转换函数来处理这个问题,在我们的例子中应该是rotate(0).

所以从技术上讲,从 translate(-50%)translate(-50%) rotate(360deg) 的过渡应该与从 translate(-50%) rotate(0)translate(-50%) rotate(360deg) 的过渡相同。

除非,我遗漏了一些东西,这肯定是一个错误,因为在单独使用 rotate(360deg) 的情况下,Chrome 使用第二点(当一个值是 none) 和最后一点差不多。