繁体   English   中英

css3 旋转过渡,不需要最短路径

[英]css3 rotate transition, doesn't take shortest way

我想使用 css3 过渡来平滑使用 phonegap 的罗盘运动。 我计算所需的旋转角度从 0 到 359。

问题是,当它应该从例如 359 变为 0 时,它不会顺时针旋转 1 度,而是逆时针旋转 359 度。

有没有办法告诉 css 始终采用最短的旋转方式?

转换完全按照您的要求执行。

它从 359 度开始到 1 度。 您希望将 360 度“翻转”回 1 度,这实际上是 361 度。 转换转换的工作方式是在值之间进行插值。

您的问题的解决方案是制作一个保存旋转度数的计数器变量:

var rot = 0;  // lets start at zero, you can apply whatever later

要应用旋转,请更改值:

rot = 359;
// note the extra brackets to ensure the expression is evaluated before
//   the string is assigned this is require in some browsers
element.style.transform = ("rotate( " + rot + "deg )");

所以如果你这样做:

rot = 1;
element.style.transform = ("rotate( " + rot + "deg )");

它回去了。 因此,您需要查看它是否更接近 360 或 0,无论它经过了多少次旋转。 您可以通过检查element.style.transform的值来完成此操作,该值只是当前的rot值,然后与新的rot值进行比较。 但是,您需要根据可能存在的旋转次数来执行此操作,因此:

var apparentRot = rot % 360;

现在无论它旋转了多少圈,你都知道它有多远,负值等于值 + 360:

if ( apparentRot < 0 ) { apparentRot += 360; } 

现在您已经标准化了任何负值,并且可以询问是否需要正旋转(在您的情况下为 360 度)或负值。 由于您似乎将新的旋转值设置为 0-360 度,因此这可以简化您的问题。 您可以询问新旋转 + 360 是否比新旋转本身更接近旧值:

var aR,          // what the current rotation appears to be (apparentRot shortened)
    nR,          // the new rotation desired (newRot)
    rot;         // what the current rotation is and thus the 'counter'

// there are two interesting events where you have to rotate through 0/360
//   the first is when the original rotation is less than 180 and the new one
//   is greater than 180deg larger, then we go through the apparent 0 to 359...
if ( aR < 180 && (nR > (aR + 180)) ) {
    // rotate back
    rot -= 360;
} 

//   the second case is when the original rotation is over 180deg and the new
//   rotation is less than 180deg smaller
if ( aR >= 180 && (nR <= (aR - 180)) ) {
    // rotate forward
    rot += 360;
}

除此之外,只需将新旋转的值添加到rot即可:

rot += (nR - aR); //  if the apparent rotation is bigger, then the difference is
                  //  'negatively' added to the counter, so the counter is
                  //  correctly kept, same for nR being larger, the difference is
                  //  added to the counter

稍微清理一下:

var el, rot;

function rotateThis(element, nR) {
    var aR;
    rot = rot || 0; // if rot undefined or 0, make 0, else rot
    aR = rot % 360;
    if ( aR < 0 ) { aR += 360; }
    if ( aR < 180 && (nR > (aR + 180)) ) { rot -= 360; }
    if ( aR >= 180 && (nR <= (aR - 180)) ) { rot += 360; }
    rot += (nR - aR);
    element.style.transform = ("rotate( " + rot + "deg )");
}

// this is how to intialize  and apply 0
el = document.getElementById("elementYouWantToUse");
rotateThis(el, 0);

// now call function
rotateThis(el, 359);
rotateThis(el, 1);

计数器可以为正也可以为负,没关系,只需使用 0-359 之间的值来进行新的旋转。

正如你所提到的,CSS 过渡没有找到开始和结束旋转之间的最短路径,而是在开始和结束旋转之间完全动画。 例如,从 0 到 720 将导致指南针动画旋转两次,而不是按照您的意愿静止不动。

解决方案是在每次更新时将旋转角度设置为与当前旋转角度最接近的等效角度。

计算这个角度的函数需要处理大角度和负角度,因为罗盘可以很容易地在任一方向旋转多次。

诀窍是计算旧角度和请求角度之间的差异并将其转换为范围 ( -180 , 180 ),以便动画始终在正确方向上采用最短路径。 您只需将此差异添加到旧角度即可获得新角度。

function closestEquivalentAngle(from, to) {
    var delta = ((((to - from) % 360) + 540) % 360) - 180;
    return from + delta;
}

除了将差异限制为 ( -180 , 180 ) 之外,上述函数非常简单。 它利用模数算法来做到这一点:

  1. 差角只是一个角度,所以我们不再考虑 from 或 to
  2. 使用余数运算符获取 ( -360 , 360 ) 中的角度; 一个问题:余数与负数的数学模数不同
  3. 添加540 ,这会将角度移动到 ( 180 , 900 ); 这摆脱了负值并将角度移动了180
  4. 再次取余数,将我们的180位移值变为 ( 0 , 360 ); 这相当于数学模数,因为我们已经消除了负值
  5. 减去180以将值恢复为 ( -180 , 180 ) 中的原始角度

看看是否可以使用负数。 从 -1deg 到 0deg 是顺时针方向,从 359deg 到 0deg 是逆时针方向。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM