代码之家  ›  专栏  ›  技术社区  ›  George V.M.

在固定轴上旋转CSS立方体

  •  4
  • George V.M.  · 技术社区  · 7 年前

    我有一个使用CSS构建的立方体。它由6个面组成,每个面被转换成立方体的一个面,所有6个面都在一个下面 <div> .cube .我对立方体所做的任何旋转都是在这个封闭空间上完成的 cube

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

    transform: rotateX(xdeg) rotateY(ydeg)
    

    CSS属性。问题是,旋转的y轴随着x轴的旋转而旋转。

    rotateX/Y/Z 属性,而我可能不得不使用3d矩阵或旋转3d属性?

    我知道这可能不是使用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>

    我真的不能添加任何javascript,因为我实际上是在用purescript编写逻辑。但代码只是注册了一个mousedown处理程序,该处理程序接受当前鼠标x和y,将其与最后一个x和y进行比较,并通过更改 具有类似的值。

      {transform: "rotateX(90deg) rotateY(90deg)"}
    
    2 回复  |  直到 7 年前
        1
  •  6
  •   George V.M.    7 年前

    注: 事实证明,这个问题在CSS中有点难解决。如果您真的需要这样一个复杂的转换,其中新的转换应该应用到以前的状态,那么可以尝试其他方法。

    解释

    transform 财产,像这样

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

    正如@ihazkode所建议的那样, rotate3d 就是要走的路。它允许绕任意轴旋转,而不限于X、Y和Z轴。 接受3个参数

    rotate3d(x, y, z, angle).
    

    x、y和z指定旋转轴。看待它的方式是这样的:想象一下从 (x,y,z) transform-origin 您指定了。这条线将是旋转轴。现在想象一下你在看 朝着 从…起 。从该视图中,对象将旋转 顺时针方向的 通过 angle 度。

    然而,我仍然面临一个问题。虽然 旋转3d

    matrix3d 所有物基本上,每次mousedown和mousemove事件发生时,我都会遵循这些步骤

    1. 我将根据mousedown发生的位置和mousemove中的当前鼠标位置计算一个向量。例如,如果mousedown发生在(123145)处,然后mousemove发生在(120143)处,则可以从这两点生成向量[x,y,z,m],其中

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

      z=0,因为鼠标无法在z方向上移动

      2. +y 2.

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

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

    使改变

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

    所以现在,我想出了如何根据鼠标移动正确旋转立方体,而不必面对之前的问题 rotateX rotateY

    1. 现在立方体可以正确旋转了。但是如果我放开鼠标,然后再次执行鼠标下移并尝试旋转立方体,会怎么样。如果我遵循上面相同的步骤,会发生的是我传递到的新向量 旋转3d 从…起

    要做到这一点,我需要 新旋转到上一个旋转上。所以我可以这样做

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

    毕竟,这将执行第一个旋转,然后在此基础上执行第二个旋转。但是,想象一下执行100个旋转。这个 使改变 房产需要100英镑 s、 这不是最好的解决方法。

    使改变 类节点的css属性

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

    matrix2d(...) )如果只执行了2D变换,或者3D变换矩阵(看起来像 matrix3d(...) 否则

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

    这将首先将立方体转换为其最后一个旋转状态,然后在其上应用新的旋转。不需要通过100分 旋转3d s

    1. 我发现了最后一个问题。还有一个相同的问题,物体的轴随着物体旋转。

    假设我将立方体沿x轴旋转90度

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

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

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

    旋转3d 允许您围绕任意旋转轴旋转对象,该任意轴仍然参照对象的轴,并且 通过相对于用户的固定x、y和z轴。这和轴旋转的问题是一样的 对象。

    因此,如果立方体当前处于某种旋转状态,我希望它在步骤1和2中通过鼠标获得的向量(x,y,z)上进一步旋转,我首先需要将该向量转换为

    x
    y
    z
    angle
    

    矩阵3d 矩阵作为4x4矩阵,然后如果我将3D变换矩阵乘以旋转向量,我得到旧的旋转向量,但转换为 位置现在我可以在3d矩阵之后应用这个向量,如步骤3所示,最后立方体的行为完全符合它应该的方式。

    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;
    }
    
        2
  •  4
  •   I haz kode    7 年前

    使用 rotate3d

    它相对容易使用,但仍然需要将当前跟踪脚本链接到正确的参数

    您可以控制 (就学位而言)和 哪个轴受到影响 (x,y,z)。您可以同时再选择一个。

    旋转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>

    旋转Y轴:

    #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(0, 1, 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);
    }
    <
    <水头>;
    <标题(>);PureScript中的3D立方体</标题(>);
    </水头>;
    
    <!--多维数据集的包装器-->
    <div id=“多维数据集包装器”>
    <div id=“front\u face”class=“face”></div>
    <div id=“right\u face”class=“face”></div>
    <div id=“left\u face”class=“face”></div>
    <div id=“top\u face”class=“face”></div>
    </div>
    
    </html>

    例3-

    #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(0, 0, 1, 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>
    
    <水头>;
    <link rel=“stylesheet”type=“text/css”href=“css/cube\u ref.css”/>
    <script type=“text/javascript”src=../js/jquery-3.2.1.min.js></脚本(>);
    </水头>;
    
    <body style=“宽度:100%;高度:100%;”&燃气轮机;
    <!--多维数据集的包装器-->
    <div id=“多维数据集包装器”>
    <div class=“cube”>
    <!--立方体每个面的div-->
    <div id=“back\u face”class=“face”></div>
    <div id=“left\u face”class=“face”></div>
    <div id=“bottom\u face”class=“face”></div>
    </div>
    </车身>
    
    </html>

    同时旋转X、Y和Z:

    #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, 1, 1, 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>
    
    <标题(>);PureScript中的3D立方体</标题(>);
    <link rel=“stylesheet”type=“text/css”href=“css/cube\u ref.css”/>
    </水头>;
    
    <body style=“宽度:100%;高度:100%;”&燃气轮机;
    <div id=“多维数据集包装器”>
    <div class=“cube”>
    <div id=“right\u face”class=“face”></div>
    <div id=“back\u face”class=“face”></div>
    <div id=“left\u face”class=“face”></div>
    <div id=“top\u face”class=“face”></div>
    </div>
    </div>
    <script type=“text/javascript”src=js/cube.js></脚本(>);