悬停时旋转元素时的奇怪行为
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
) 和最后一点差不多。
鉴于这两个例子。
使用悬停
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
) 和最后一点差不多。