【问题标题】:CSS rotation animation issue: creating a smooth transition from 270 degrees to -90 degreesCSS 旋转动画问题:创建从 270 度到 -90 度的平滑过渡
【发布时间】:2021-04-03 08:15:04
【问题描述】:

我正在尝试创建一个网络动画,其中一个矩形根据鼠标位置围绕浏览器窗口的中心点旋转(如下面的 gif 所示)。

动画 99% 可以正常工作,但我遇到了无法修复的边缘情况问题。请参阅 gif 以获取视觉参考:当鼠标位于屏幕的右半部分时,动画的行为符合预期 - 当鼠标位于屏幕的左半部分并穿过水平轴时会出现问题,从而导致矩形翻转一个完整的 360 度,而不是平滑过渡。

这是因为矩形快速从 270 度捕捉到 90 度 - 两个角度在视觉上是相同的,但由于过渡动画,您可以观察到矩形翻转 360 度。

如何解决此问题以确保屏幕左侧的平滑过渡?

var rect = document.getElementById('orange_rect'); // Target rectangle
var window_width = $(window).width();
var window_height = $(window).height();

$(window).resize(function() { //set window width and height again everytime the window is resized
  var window_width = $(window).width();
  var window_height = $(window).height();
});

// Update rotation degrees on mousemove
$(document).mousemove(function(e) {
  var x_pos = e.pageX / window_width;
  var y_pos = e.pageY / window_height;
  if (y_pos >= 0.5) {
    var deg = 270 - (x_pos * 180);
  } else if (y_pos < 0.5) {
    var deg = (x_pos * 180) - 90;
  };
  rect.style.webkitTransform = 'rotate(' + deg + 'deg)';
  rect.style.mozTransform = 'rotate(' + deg + 'deg)';
  rect.style.msTransform = 'rotate(' + deg + 'deg)';
  rect.style.oTransform = 'rotate(' + deg + 'deg)';
  rect.style.transform = 'rotate(' + deg + 'deg)';
});
body {
  overflow: hidden;
}

#orange_rect {
  /* Homepage orange rect */
  background-color: #FF4734;
  height: 100vh;
  width: 200vw;
  overflow: hidden;
  position: absolute;
  top: 50vh;
  margin-right: auto;
  margin-left: -50vw;
  transform-origin: 50% 0;
  transition: transform 0.3s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<body>
  <div id="orange_rect"></div>
</body>

【问题讨论】:

    标签: javascript animation rotation css-animations image-rotation


    【解决方案1】:

    跳转是由于deg从360变为0。您可以将鼠标的运行旋转角度存储在mousemove处理程序之外,并计算处理程序中角度的变化,如下所示:

    var rect = document.getElementById('orange_rect'), // Target rectangle
      window_width = $(window).width(), // set window width
      window_height = $(window).height(), // set window height
      angle = 0, // The stored angle of the mouse [rad]
      deg = 0, // The actual rotation angle [deg]
      cX = 200, // The centre point of the rotation
      cY = 200;
    
    $(window).resize(function() { //set window width and height again everytime the window is resized
      var window_width = $(window).width();
      var window_height = $(window).height();
    });
    
    // Update rotation degrees on mousemove
    $(document).mousemove(function(e) { //called whenever mouse moves in browser window
      var x = e.pageX, // proportion representing mouse position from left of screen
        y = e.pageY, // proportion representing mouse position from top of screen
        ang = Math.atan2(y - cY, x - cX), // The current angle of the mouse related to the rotation centre
        delta = ang - angle; // Change to the previous angle
    
      angle += delta;
      deg += delta * 180 / Math.PI;
    
      rect.style.webkitTransform = 'rotate(' + deg + 'deg)';
      rect.style.mozTransform = 'rotate(' + deg + 'deg)';
      rect.style.msTransform = 'rotate(' + deg + 'deg)';
      rect.style.oTransform = 'rotate(' + deg + 'deg)';
      rect.style.transform = 'rotate(' + deg + 'deg)';
    });
    #orange_rect {
      position: fixed;
      top: 100px;
      left: 100px;
      width: 200px;
      height: 200px;
      background: orange;
      border-top: 5px solid red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="orange_rect"></div>

    值得注意的是,您不必将deg 的值限制在 0 - 360 的范围内,CSS 会根据任何给定值计算正确的角度。保持旋转角度中的“轮”使旋转在旋转到下一轮时也可以平滑。

    【讨论】:

    • 感谢尝试,但不幸的是,这有同样的问题。如果添加'transition: transform 0.3s;'到 orange_rect 的 CSS 你会看到它。我认为您的想法是正确的,我认为该解决方案不太有效,因为 atan 返回 -pi 和 pi 之间的值,因此 'deg' 并没有按应有的严格增加。再次感谢!
    • 请填写完整的minimal reproducible example,当我们知道您的真实情况后,我们可以更好地解决问题。
    • 很抱歉,我已经编辑了原始帖子以包含一个示例。代码 sn-p 最好在预览模式下查看——由于某种原因,它在全屏时会出现错误。
    • 是的。这里面恐怕不能包含动画,因为CSS确实会重新计算角度,动画是用最大的变化来计算的。
    • @HassanA 我稍微更新了代码。您可以在jsFiddle 看到它。我已将过渡时间减少到 0.1 秒,并在角度跳跃的位置将其完全删除。它仍然“捕捉”到垂直位置,但比没有过渡时更平滑。
    【解决方案2】:

    我能够使用变换矩阵来解决这个问题,如下面的 sn-p 所示。据我所知,使用基于旋转的变换没有简单的解决方案。

    var rect = document.getElementById('orange_rect'); // Target rectangle
    var window_width = $(window).width();
    var window_height = $(window).height();
    
    $(window).resize(function() { //set window width and height again everytime the window is resized
      var window_width = $(window).width();
      var window_height = $(window).height();
    });
    
    // Update rotation degrees on mousemove
    $(document).mousemove(function(e) {
      var window_width = $(window).width();
      var window_height = $(window).height();
      var x_pos = e.pageX / window_width; // proportion mouse position from left
      var y_pos = e.pageY / window_height; // proportion mouse poisiton from top
    
      if (x_pos <= 0.5) { // If mouse is on the left half of the screen
        if (y_pos <= 0.5) { // If mouse is in top-left quadrant
          n1 = 2 * x_pos;
          n2 = -1 + 2 * x_pos;
          n3 = 1 - 2 * x_pos;
          n4 = n1;
        }
        if (y_pos > 0.5) { // If mouse is in bottom-left quadrant
          n1 = -2 * x_pos;
          n2 = -1 + 2 * x_pos;
          n3 = 1 - 2 * x_pos;
          n4 = n1;
        }
      } else if (x_pos > 0.5 && x_pos <= 1) { // If mouse is on the right half of the screen
        if (y_pos <= 0.5) { // If mouse is in top-right quadrant
          n1 = 1 - 2 * (x_pos - 0.5);
          n2 = 2 * (x_pos - 0.5);
          n3 = -2 * (x_pos - 0.5)
          n4 = n1
        }
        if (y_pos > 0.5) { // If mouse is in bottom-right quadrant
          n1 = -1 + 2 * (x_pos - 0.5);
          n2 = 2 * (x_pos - 0.5);
          n3 = -2 * (x_pos - 0.5)
          n4 = n1
        }
      };
      // console.log(n1)
      rect.style.webkitTransform = 'matrix(' + n1 + ',' + n2 + ',' + n3 + ',' + n4 + ',0,0)';
      rect.style.mozTransform = 'matrix(' + n1 + ',' + n2 + ',' + n3 + ',' + n4 + ',0,0)';
      rect.style.msTransform = 'matrix(' + n1 + ',' + n2 + ',' + n3 + ',' + n4 + ',0,0)';
      rect.style.oTransform = 'matrix(' + n1 + ',' + n2 + ',' + n3 + ',' + n4 + ',0,0)';
      rect.style.transform = 'matrix(' + n1 + ',' + n2 + ',' + n3 + ',' + n4 + ',0,0)';
    });
    body {
      overflow: hidden;
    }
    
    #orange_rect {
      /* Homepage orange rect */
      background-color: #FF4734;
      height: 200vh;
      width: 200vw;
      overflow: hidden;
      position: absolute;
      top: 50vh;
      margin-right: auto;
      margin-left: -50vw;
      transform-origin: 50% 0;
      transition: transform 0.3s;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <body>
      <div id="orange_rect"></div>
    </body>

    【讨论】:

    • 在sn-p 中,div 仍然在转,只是位置发生了变化,现在它在旋转中心线的div 下方。但是矩阵是个好主意,下次我需要一些旋转算法时,我必须记住。
    猜你喜欢
    • 2013-08-02
    • 1970-01-01
    • 2013-04-22
    • 2015-05-07
    • 2014-07-23
    • 2010-10-16
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    相关资源
    最近更新 更多