在固定轴上旋转CSS立方体

我有一个使用CSS构建的立方体。 它由6个面组成,每个面都被转换成立方体的一个面,并且所有6个面都在一个<div> .cube 。 我对多维数据集做的任何旋转都是在这个封闭的cube类中完成的。

我希望立方体根据鼠标拖动输入进行旋转。 到目前为止它有点作品。 我只是将x和y鼠标移动转换为围绕x和y轴的立方体旋转。

但是这有一个主要问题。 我作为一个简单的执行旋转

transform: rotateX(xdeg) rotateY(ydeg)

CSS属性。 与此相关的问题是,旋转的y轴随x旋转而旋转。

假设我围绕x轴旋转90度。 现在,如果我尝试将立方体沿着y轴旋转90度,我会期望立方体向右或向左旋转90度(从我的角度来看)。 但相反,它是围绕目前可见的正面旋转。 也就是说,y轴旋转了90度,这要归功于先出现的x轴旋转,现在从用户的角度来看,它看起来好像立方体围绕它的z轴旋转。

我希望能够以xy和z轴从用户的角度保持固定的方式旋转立方体。 此外,立方体需要从当前状态旋转,以防用户将手指从按钮上抬起并再次点击并拖动。

我一直觉得这很难做到。 我觉得这可能无法使用rotateX/Y/Z属性,相反,我可能不得不使用3d矩阵或rotate3d属性?

我知道这可能不是使用CSS实现的最简单的事情,但我仍然希望这样做。 有人能指出我如何解决这个问题的正确方向吗?

#cube-wrapper {
  position: absolute;
  left: 50%;
  top: 50%;
  perspective: 1500px;
}

.cube {
  position: relative;
  transform-style: preserve-3d;
}


/* Size and border color for each face */

.face {
  position: absolute;
  width: 200px;
  height: 200px;
  border: solid green 3px;
}


/* Transforming every face into their correct positions */

#front_face {
  transform: translateX(-100px) translateY(-100px) translateZ(100px);
}

#back_face {
  transform: translateX(-100px) translateY(-100px) translateZ(-100px);
}

#right_face {
  transform: translateY(-100px) rotateY(90deg);
}

#left_face {
  transform: translateY(-100px) translateX(-200px) rotateY(90deg);
}

#top_face {
  transform: translateX(-100px) translateY(-200px) rotateX(90deg);
}

#bottom_face {
  transform: translateX(-100px) rotateX(90deg);
}

.cube {
  transform: rotateX(90deg) rotateY(90deg);
}
<!-- Wrapper for the cube -->
<div id="cube-wrapper">
  <div class="cube">
    <!-- A div for each face of the cube -->
    <div id="front_face" class="face"></div>
    <div id="right_face" class="face"></div>
    <div id="back_face" class="face"></div>
    <div id="left_face" class="face"></div>
    <div id="top_face" class="face"></div>
    <div id="bottom_face" class="face"></div>
  </div>
</div>

使用rotate3d

使用起来相对简单,但您仍然需要将当前的跟踪脚本与正确的参数关联起来

您可以控制旋转量 (以度为单位)以及受影响的轴 (x,y,z)。 您可以同时选择一个。

示例1 - 旋转X轴:

#cube-wrapper {
  position: absolute;
  left: 50%;
  top: 50%;
  perspective: 1500px;
}

.cube {
  position: relative;
  transform-style: preserve-3d;
  animation-name: rotate;
  animation-duration: 30s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

@keyframes rotate {
  0% {
    transform: rotate3d(0, 0, 0, 0);
  }
  100% {
    transform: rotate3d(1, 0, 0, 360deg); /*controls rotation amount on one axis) */
    ;
  }
}


/* Size and border color for each face */

.face {
  position: absolute;
  width: 200px;
  height: 200px;
  border: solid green 3px;
}


/* Transforming every face into their correct positions */

#front_face {
  transform: translateX(-100px) translateY(-100px) translateZ(100px);
  background: rgba(255, 0, 0, 0.5);
}

#back_face {
  transform: translateX(-100px) translateY(-100px) translateZ(-100px);
  background: rgba(255, 0, 255, 0.5);
}

#right_face {
  transform: translateY(-100px) rotateY(90deg);
  background: rgba(255, 255, 0, 0.5);
}

#left_face {
  transform: translateY(-100px) translateX(-200px) rotateY(90deg);
  background: rgba(0, 255, 0, 0.5);
}

#top_face {
  transform: translateX(-100px) translateY(-200px) rotateX(90deg);
  background: rgba(0, 255, 255, 0.5);
}

#bottom_face {
  transform: translateX(-100px) rotateX(90deg);
  background: rgba(255, 255, 255, 0.5);
}

.cube {
  transform: rotateX(90deg) rotateY(90deg);
}
<html>

<head>
  <title>3D Cube in PureScript</title>
  <link rel="stylesheet" type="text/css" href="css/cube_ref.css" />
  <script type="text/javascript" src=../js/jquery-3.2.1.min.js></script>
</head>

<body style="width: 100%; height:100%;">
  <!-- Wrapper for the cube -->
  <div id="cube-wrapper">
    <div class="cube">
      <!-- A div for each face of the cube -->
      <div id="front_face" class="face"></div>
      <div id="right_face" class="face"></div>
      <div id="back_face" class="face"></div>
      <div id="left_face" class="face"></div>
      <div id="top_face" class="face"></div>
      <div id="bottom_face" class="face"></div>
    </div>
  </div>
</body>
<script type="text/javascript" src=js/cube.js></script>

</html>

注意:事实证明,这个问题在CSS中很难解决。 如果你真的需要像这样的复杂变换,那么应该将新的变换应用到前一个状态上,也许可以尝试其他方法。

无论如何,我首先要解释我经历的步骤,我遇到的问题以及我解决问题的步骤。 这真是令人费解和杂乱,但它的工作。 最后,我把我用作JavaScript的代码。

说明

所以我已经了解了一些关于CSS转换的内容。 一个主要的问题是,当你将一串转换传递给transform属性时,就像这样

transform: "rotateX(90deg) rotateY(90deg)"

这些转换不会合并为一个单一的复合转换。 取而代之的是第一个被应用,然后下一个应用在其上,等等。 所以,虽然我期望立方体对角旋转90度,但并没有这样做。

正如@ihazkode建议的那样, rotate3d是要走的路。 它允许围绕任何轴旋转,而不是限制在X,Y和Z轴上。 rotate3d需要3个参数

rotate3d(x, y, z, angle).

xy和z指定旋转轴。 查看它的方式如下所示:设想从(x,y,z)到您指定的transform-origin绘制一条线。 这条线将作为旋转轴。 现在想象你正在寻找来自(x,y,z)的起源。 从这个角度看,物体将以angle 顺时针旋转

不过,我仍然面临一个问题。 虽然rotate3d让我以更直观的方式旋转立方体,但如果再次点击并尝试旋转立方体,我仍然面临旋转立方体一次后(用鼠标)的问题,它会回到原始状态并旋转从那里,这不是我想要的。 我希望它从它的当前状态旋转,无论旋转状态如何。

我发现一个非常混乱的方式来使用matrix3d属性。 基本上我会在每次发生mousedown和mousemove事件时遵循这些步骤

  • 我会根据mousedown发生的位置和mousemove当前的鼠标位置计算矢量。 例如,如果在(123,145)发生了mousedown,然后在(120,143)发生了鼠标移动,则可以从这两个点形成矢量,如[x,y,z,m],其中

    x是x分量,它是新x位置减去鼠标向下x位置= 120-123 = -3

    y是y分量,类似于x,它= 143-145 = -2

    z = 0,因为鼠标不能在z方向上移动

    m是可以按平方根(x2 + y2)= 3.606计算的矢量的大小

    所以鼠标移动可以表示为向量[-3,-2,0,3.606]

  • 现在注意立方体的旋转矢量应该垂直于鼠标移动。 例如,如果我将鼠标向上移动3个像素,则鼠标移动向量为[0,-1,0,3](y为负值,因为在浏览器中,左上角是原点)。 但是,如果我使用这个矢量作为旋转矢量并将它传递给rotate3d ,它将围绕y轴顺时针旋转立方体(当从上面看时)。 但那不对! 如果我向上滑动鼠标,它应该围绕x轴旋转! 要解决这个问题,只需交换x和y并取消新的x。 也就是说,矢量应该是[1,0,0,3]。 因此,步骤1中的矢量应该是[2,-3,0,3.606]。

  • 现在我只需将我的多维数据集的transform属性设置为

    transform: "rotate3d(2,-3,0,3.606)"
    

    所以,现在,我想通了,如何正确地基于移动鼠标旋转立方体,没有遇到的努力使先前的问题rotateX ,然后rotateY

  • 现在多维数据集可以正确旋转。 但是,如果我放开鼠标,然后再次执行mousedown并尝试旋转立方体,该怎么办? 如果我遵循上面的相同步骤,会发生什么是我传递给rotate3d的新向量将替换旧向量。 所以立方体被重置到它的初始位置并且新的旋转被应用到它。 但是这不对。 我希望立方体保持之前的状态,然后从该状态开始,它应该进一步旋转新的旋转矢量。
  • 为此,我需要将新的旋转追加到前一个旋转上。 所以我可以做这样的事情

    transform: "rotate3d(previous_rotation_vector) rotate3d(new_rotation_vector)"
    

    毕竟,这将执行第一次旋转,然后执行第二次旋转。 但是,想象一下执行100次旋转。 transform属性需要被馈送100 rotate3d s。 这不会是解决这个问题的最好方法。

    这是我的想法。 在任何时候,如果你查询像一个节点的transform CSS属性

    $('.cube').css('transform');
    

    如果目标尚未完全变形,则返回3个值之一:“无”,2D变换矩阵(看起来像matrix2d(...) ),如果只执行2D变换,或者3D变换矩阵(看起来像matrix3d(...)否则。

    因此,我可以做的是,在执行旋转操作后立即查询并获取立方体的变换矩阵并保存。 下次我执行新的轮换时,请执行以下操作:

    transform: "matrix3d(saved_matrix_from_last_rotation) rotate3d(new_rotation_vector)"
    

    这将首先将多维数据集转换为其最后的旋转状态,然后在其上应用新的旋转。 无需通过100个rotate3d s。

  • 我发现了最后一个问题。 与对象一起旋转的对象的轴仍然存在相同的问题。
  • 假设我用X轴沿x轴旋转90度

    transform: rotate3d(1,0,0,90deg);
    

    然后从那里绕它的y轴旋转45度

    transform: matrix3d(saved values) rotate3d(0,1,0,45deg)
    

    我期望立方体向上旋转90°,然后向右旋转45°。但相反,它旋转了90°,然后将当前可见的正面旋转45°,而不是向右旋转。 这是我在我的问题中提到的完全相同的问题。 问题是,虽然rotate3d允许您围绕任意旋转轴旋转对象,但该任意轴仍然参照对象的轴,而不是固定的x,y和z轴相对于用户。 与物体一起旋转的轴是一样的天哪问题。

    因此,如果立方体当前处于某种旋转状态,并且我希望它在步骤1和2中通过鼠标获得的矢量(x,y,z)上进一步旋转,我首先需要以某种方式将该矢量转换为正确的位置基于立方体当前处于什么状态。

    我注意到的是,如果将旋转矢量作为这样的4x1矩阵

    x
    y
    z
    angle
    

    并采取了matrix3d矩阵为4x4矩阵,那么如果我乘以旋转矢量的3D变换矩阵,给我的老旋转矢量,但转化为它的正确位置。 现在,我可以在第3步中应用这个向量在3D矩阵之后,并且最后,立方体的行为应该与它应该完全相同。

    JavaScript代码

    好的,这已经够了。 这是我使用的代码。 对不起,如果不是很清楚。

    var lastX; //stores x position from mousedown
    var lastY; //y position from mousedown
    var matrix3d = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] //this identity matrix performs no transformation
    
    $(document).ready(function() {
      $('body').on('mousedown', function(event) {
        $('body').on('mouseup', function() {
          $('body').off('mousemove');
          m = $('.cube').css('transform');
          //if this condition is true, transform property is either "none" in initial state or "matrix2d" which happens when the cube is at 0 rotation.
          if(m.match(/matrix3d/) == null) 
            matrix3d = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; //identity matrix for no transformaion
          else
            matrix3d = stringToMatrix(m.substring(8,m.length));
        });
    
        lastX=event.pageX;
        lastY=event.pageY;
    
        $('body').on('mousemove', function (event) {
          var x = -(event.pageY - lastY);
          var y = event.pageX - lastX;
          var angle = Math.sqrt(x*x + y*y);
          var r = [[x],[y],[0],[angle]]; //rotation vector
          rotate3d = multiply(matrix3d, r); //multiply to get correctly transformed rotation vector
          var str = 'matrix3d' + matrixToString(matrix3d)
                + ' rotate3d(' + rotate3d[0][0] + ', ' + rotate3d[1][0] + ', ' + rotate3d[2][0] + ', ' + rotate3d[3][0] + 'deg)';
          $('.cube').css('transform',str);
        });
      });
    });
    
    //converts transform matrix to a string of all elements separated by commas and enclosed in parentheses.
    function matrixToString(matrix) {
      var s = "(";
      for(i=0; i<matrix.length; i++) {
        for(j=0; j<matrix[i].length; j++) {
          s+=matrix[i][j];
          if(i<matrix.length-1 || j<matrix[i].length-1) s+=", ";
        }
      }
      return s+")";
    }
    
    //converts a string of transform matrix into a matrix
    function stringToMatrix(s) {
      array=s.substring(1,s.length-1).split(", ");
      return [array.slice(0,4), array.slice(4,8), array.slice(8,12), array.slice(12,16)];
    }
    
    //matrix multiplication
    function multiply(a, b) {
      var aNumRows = a.length, aNumCols = a[0].length,
          bNumRows = b.length, bNumCols = b[0].length,
          m = new Array(aNumRows);  // initialize array of rows
      for (var r = 0; r < aNumRows; ++r) {
        m[r] = new Array(bNumCols); // initialize the current row
        for (var c = 0; c < bNumCols; ++c) {
          m[r][c] = 0;             // initialize the current cell
          for (var i = 0; i < aNumCols; ++i) {
            m[r][c] += a[r][i] * b[i][c];
          }
        }
      }
      return m;
    }
    
    链接地址: http://www.djcxy.com/p/81815.html

    上一篇: Rotating CSS cube on fixed axes

    下一篇: How to compute the Rotation