在固定轴上旋转CSS立方体

时间:2017-07-01 10:45:42

标签: css css3 3d rotation css-transforms

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

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

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

执行
transform: rotateX(xdeg) rotateY(ydeg)

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

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

我希望能够以x y和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>

我无法真正添加任何javascript,因为我实际上正在编写purescript中的逻辑。但是代码只是注册了一个mousedown处理程序,它接受当前鼠标x和y,将它与最后的x和y进行比较,并相应地通过使用值更改.cube的transform属性来围绕x和y轴旋转立方体等。

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

2 个答案:

答案 0 :(得分:4)

使用rotate3d

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

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

示例1 - 旋转X轴:

    <div id="app">
    <h1>Test</h1>
    <record>
    </record>
    </div>

    <template id="record-template">
    <div>

    <button class="button red-button" v-on:click.stop.prevent="toggleRecording">
    	<i class="stop icon" v-show="isRecording"></i>
      <span v-show="isRecording">Stop recording</span>
    	<i class="record icon" v-show="!isRecording"></i>
      <span v-show="!isRecording">Start recording</span>
    </button>

    <button id="remove-recording" class="remove-recording" v-if="dataUrl.length > 0" v-on:click.stop.prevent="removeRecording">
        <i class="remove icon"></i> Delete recording
    </button>


    <button id="send-recording" class="button green-button" v-if="dataUrl.length > 0">
        <i class="send icon"></i> Send recording
    </button>


    <button class="button green-button" v-if="dataUrl.length > 0" v-on:click.stop.prevent="togglePlay">
        <i class="play icon"></i> Play recording
    </button>
    </div>
    <audio id="audio" preload="auto" v-model="dataUrl"></audio>


    </template>
    class get_data()

    {
     public function get_data_in_table($query){
            $output='';
            $result=  $this->Getdata($query);
            $output.='
                    <table class="table table-bordered table-striped">
                    <tr>
                    <th>Countty</th>
                    <th>Airline Name</th>
                    <th>Arrival Time</th>
                    <th>Status</th>
                    <th>Comment</th>


    </tr>';
            while($row= mysqli_fetch_array($result)){
                $output .='

                        <tr>
    <td>'.$row->country.'</td>
    <td>'.$row->air_line.'</td>
        <td>'.$row->arrival_time.'</td>
          <td>'.$row->status.'</td>
              <td>'.$row->comment.'</td>


    </tr>';


            }
            $output.='</table>';
            return $output;
                    }
?>

示例2 - 旋转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(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>

示例3 - 旋转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(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);
}
<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>

示例4 - 同时旋转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(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>

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

答案 1 :(得分:3)

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

无论如何,我首先要解释我经历的步骤,我遇到的问题以及我采取的解决方法。它确实令人费解和凌乱,但它确实有效。最后,我将我用作JavaScript的代码。

解释

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

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

这些转换不会合并为一个单一的复合转换。而是应用第一个,然后将下一个应用于其上,依此类推。因此,虽然我期望立方体对角旋转90度,但它并没有这样做。

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

rotate3d(x, y, z, angle).

x y和z指定旋转轴。看待它的方式是这样的:想象一下从(x,y,z)到你指定的transform-origin画一条线。该线将是旋转轴。现在想象一下,你正在从 (x,y,z)起源。在此视图中,对象将顺时针旋转angle度。

然而,我仍然遇到了问题。虽然rotate3d让我以更加直观的方式旋转立方体,但我仍然遇到一个问题,即如果我再次点击并尝试旋转立方体,旋转立方体一次(使用鼠标),它会快速回到原来的状态并从那里旋转,这不是我想要的。我想让它从它的当前状态旋转,无论旋转状态如何。

我发现使用matrix3d属性做一个非常混乱的方法。基本上,每次发生mousedown和mousemove事件时,我都会按照这些步骤进行操作

  1. 我根据mousedown发生的位置和mousemove的当前鼠标位置来计算矢量。例如,如果mousedown出现在(123,145),然后一个mousemove出现在(120,143),则可以从这两个点得到一个向量,如[x,y,z,m],其中

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

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

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

    m是矢量的大小,可以计算为平方根(x 2 + y 2 )= 3.606

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

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

  3. 现在我只是将我的多维数据集的transform属性设置为

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

    现在,我想出了如何根据鼠标移动正确旋转立方体,而不会遇到先前尝试制作rotateX然后rotateY的问题。

    1. 现在立方体可以正确旋转。但是,如果我放开鼠标然后再次执行mousedown并尝试旋转立方体,该怎么办?如果我按照上面的相同步骤操作,那么我传递给rotate3d的新矢量将替换旧矢量。因此立方体被重置为它的初始位置,并且新的旋转被应用于它。但那不对。我希望立方体保持其先前的状态,然后表明它应该通过新的旋转向量进一步旋转。
    2. 为此,我需要将新旋转附加到前一个旋转上。所以我可以做这样的事情

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

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

      这就是我的想法。在任何时候,如果您查询节点的transform css属性,如

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

      你得到3个值中的一个:&#34;无&#34;如果对象到目前为止还没有被变换,那么如果只进行2D变换就会产生2D变换矩阵(看起来像matrix2d(...)),或者3D变换矩阵(看起来像matrix3d(...)否则看起来像transform: "matrix3d(saved_matrix_from_last_rotation) rotate3d(new_rotation_vector)"

      所以我可以做的是,在执行旋转操作后立即查询并获取多维数据集的转换矩阵并保存它。下次我执行新的轮换时,请执行以下操作:

      rotate3d

      这首先将立方体转换为它的最后旋转状态,然后在其上应用新的旋转。无需传递100 transform: rotate3d(1,0,0,90deg); s。

      1. 我发现的最后一个问题。对象的轴与对象一起旋转的问题仍然存在。
      2. 假设我使用

        沿x轴旋转立方体90度
        transform: matrix3d(saved values) rotate3d(0,1,0,45deg)
        

        然后用它旋转它绕y轴旋转45度

        rotate3d

        我希望立方体向上旋转90°然后向右旋转45°。但是它向上旋转90°然后围绕当前可见的正面旋转45°而不是向右旋转。这是我在问题中提到的完全相同的问题。问题是,尽管x y z angle 允许您围绕任意旋转轴旋转对象,但任意轴仍然是参考对象的轴而不是是固定的x, y和z轴相对于用户。与使用对象旋转的轴相同的gosh darn问题。

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

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

        matrix3d

        并将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; } 矩阵作为4x4矩阵,然后如果我将3D变换矩阵乘以旋转矢量,我得到旧的旋转矢量但转换成它正确位置。现在,我可以在3d矩阵之后应用此向量,如步骤3中所示,最后立方体的行为与它应该的完全相同。

        JavaScript代码

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

        {{1}}