如何使"转换:旋转"使用最短的旋转



我想为元素指定步进旋转,例如90deg。同时,它必须有一个初始偏移旋转,例如1deg,应适用于所有步骤。假设我需要1、91、181和271度。到目前为止很容易。但现在我也想要平滑的CSS转换。

直到271度,这是有效的,但如果我回到1,转换将一直逆时针进行(271->181->91->1(,但我希望它走"视觉上短"的路线,例如271-<272->273…->1(=361(。

下面是一个例子。在浏览器的HTML编辑器中将div的r-271替换为r-1。

body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.r {
transition: transform 1s;
}
.r-1 {
transform: rotate(1deg);
}
.r-91 {
transform: rotate(91deg);
}
.r-181 {
transform: rotate(181deg);
}
.r-271 {
transform: rotate(271deg);
}
<div class="r r-271">X</div>

使用.r-361对我来说不是一个选项,因为在这种情况下,我的应用程序的数据模型只存储4个步骤,我不知道以前的状态。因此,我只需要这一点来处理4个类/状态,也需要处理负原始旋转,例如-1/89/179/269(。请不要使用JavaScript动画。

(90度只是一个例子,它应该与各种步骤配合使用。(

实现这一点的一种方法是使用CSS动画,并有一组"基本"的0、90、180、270旋转,但要利用CSS转换是累积的这一事实,因此您既可以按其中一个值旋转,也可以按步进值旋转。

在这个问题中,它被设置为1deg,但在这个片段中,它设置为-20deg,以表明当步长值为负时它是有效的。CSS变量用于设置步长值,因此您可以很容易地进行更改。

最后一次旋转是为了走"最短的路线",始终从-90度到0度,而不是270度到0°。

在这个演示中,每次单击旋转按钮都会移动到下一个旋转。类的名称已简化为r1-r4,以便更容易地计算下一个要使用的类。

//This script is just to make the button work for the demo.
const button = document.querySelector('button');
const div = document.querySelector('div');
let n = 1;
button.addEventListener('click', function() {
div.classList.remove('r' + n);;
n = (n == 4) ? 1 : n + 1;
div.classList.add('r' + n);
});
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
button {
position: fixed;
top: 0;
left: 0;
}
.r {
--step: -20deg;
}
.r1 {
animation: r1 1s 1 forwards;
}
@keyframes r1 {
0% {
transform: rotate(-90deg) rotate(var(--step));
}
100% {
transform: rotate(0deg) rotate(var(--step));
}
}
.r2 {
animation: r2 1s 1 forwards;
}
@keyframes r2 {
0% {
transform: rotate(0deg) rotate(var(--step));
}
100% {
transform: rotate(90deg) rotate(var(--step));
}
}
.r3 {
animation: r3 1s 1 forwards;
}
@keyframes r3 {
0% {
transform: rotate(90deg) rotate(var(--step));
}
100% {
transform: rotate(180deg) rotate(var(--step));
}
}
.r4 {
animation: r4 1s 1 forwards;
}
@keyframes r4 {
0% {
transform: rotate(180deg) rotate(var(--step));
}
100% {
transform: rotate(270deg) rotate(var(--step));
}
}
<button>Rotate</button>
<div class="r r1">X</div>

根据@a-haworth的回答,我选择了一个将360°预旋转与纯过渡方法相结合的解决方案。我不得不增加两个额外的类:

.r-271-cw {
transform: rotate(-91deg);
transition: none;
}
.r-1-ccw {
transform: rotate(361deg);
transition: none;
}

在将新的旋转类设置到现有元素中之前,我从节点中查询旧类:

  • 如果类是.r-271并且即将变成.r-1,则我为元素分配类.r-1.r-271-cw
  • 如果类是.r-1并且即将变成.r-271,则我将类.r-271.r-1-ccw分配给元素
  • 在任何其他情况下,我只应用新的旋转类,而不应用辅助类
  • 几毫秒后,我删除了DOM中的任何.r-271-cw.r-1-ccw

附加的cw/ccw类将其元素的上一次旋转重置为向前360°/-360°的角度,因此旋转可以在该位置进行适当的(逆时针(顺时针旋转。同时,附加类禁用转换,因此元素跳到新的旋转而不会产生任何视觉影响。当我删除cw/ccw类时,这将重新启用转换,并且元素现在使用"最短旋转"。

这种方法也适用于任何方向的改变,甚至跳过步骤,因为不需要中止任何动画。

(我还切换到CSS变量和额外1°的累积旋转,但这不是这个答案所需要的。(

最新更新