CSS rotating cube on fixed axes

I have a cube built using CSS. It is made of 6 faces, and each face is transformed, forming one face of the cube, and all 6 faces are under one <div>with the class .cube. Any rotation that I do in a cube is done on this class cube.

I want the cube to rotate based on the input of the mouse drag. While this is working. I simply translate the x and y movements of the mouse into the rotation of the cube around the x and y axes.

But there is one serious problem with this. I'm turning as simple

transform: rotateX(xdeg) rotateY(ydeg)

CSS property. The problem is that the axis of rotation y rotates with rotation x.

Suppose that you rotate the cube 90 degrees around the x axis. Now, if I try to rotate the cube 90 degrees along the y axis, I would expect the cube to rotate 90 degrees to the right or left (from my point of view). But instead, it revolves around this currently visible face. That is, the y axis is rotated 90 degrees due to the rotation of the x axis, which was the first, and now, from the user's point of view, it seems that the cube rotates around the z axis.

I want to be able to rotate the cube so that the xy and z axes are saved from the user's point of view. Also, the cube should rotate from the current state if the user lifts his finger from the button and again clicks and drags.

. , rotateX/Y/Z, , , 3d matrix 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>
Hide result

javascript, purescript. mousedown, x y, x y x y, .cube like.

  {transform: "rotateX(90deg) rotateY(90deg)"}
+4
2

. , 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. , mousemove.

  • , , mousedown, mousemove. , (123,145) , (120,143) mousemove, [x, y, z, m],

    x - x-, x x position = 120 - 123 = -3

    y - y-, x, = 143-145 = -2

    z = 0, z

    m - , squareroot (x 2 + y 2) = 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.

  1. . , , . , , , rotate3d, . , reset . . , , , .

, . -

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

, , . 100 . transform 100 rotate3d s. .

. , transform css node,

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

: "none", , 2D- ( matrix2d(...)), 2D-, ( matrix3d(...) .

, , . , , :

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

, . 100 rotate3d s.

  1. , . , .

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

, , , (x, y, z), , 1 2, - , , .

, 4x1,

x
y
z
angle

matrix3d 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

rotate3d

, script

( ) , (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>
Hide result

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(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>
Hide result

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, 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>
Hide result

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

<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>
Hide result
+4

Source: https://habr.com/ru/post/1680546/


All Articles