在固定轴上旋转CSS立方体 [英] Rotating CSS cube on fixed axes

查看:104
本文介绍了在固定轴上旋转CSS立方体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用CSS构造的多维数据集。它由6个面组成,每个面被变换为一个立方体的面,所有6个面都在一个< div> 下,其类别为 .cube 。我对多维数据集所做的任何旋转都在此封闭的 cube 类上完成。

I have a cube constructed using CSS. It's made of 6 faces and each face is transformed to form one face of the cube, and all the 6 faces are under one <div> with the class .cube. Any rotation I do to the cube is done on this enclosing cube class.

我希望多维数据集基于旋转鼠标拖动输入。到目前为止,它仍然有效。我只是将x和y鼠标的运动转换为绕x和y轴旋转的立方体。

I want the cube to rotate based on mouse drag input. So far it kinda works. I just translate x and y mouse movement into cube rotation about the x and y axes.

但是,这有一个主要问题。我以简单的方式执行旋转操作

But there's one major problem with this. I perform the rotation as a simple

transform: rotateX(xdeg) rotateY(ydeg)

CSS属性。问题在于,旋转的y轴随x旋转而旋转。

CSS property. The issue with this is that the y axis of rotation is getting rotated with the x rotation.

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

Suppose I rotate the cube 90 degrees around the x axis. Now, if I try to rotate the cube 90 degrees along the y axis as well, I would expect the cube to rotate 90 degrees to the right or left (from my perspective). But instead, it's rotating about it's currently visible front face. That is, the y axis got rotated 90 degrees thanks to the x axis rotation that came first, and so now from the perspective of the user, it looks as if the cube is rotating around it's z axis.

我希望能够以从用户的角度来看xy和z轴保持固定的方式旋转立方体。另外,如果用户将手指从按钮上移开并再次单击并拖动,则多维数据集也需要从当前状态旋转。

I want to be able to rotate the cube in a way that the x y and z axes remain fixed from the perspective of the user. Also the cube needs to rotate from the current state in case the user lifts their finger off the button and clicks again and drags.

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

I've been finding this difficult to do. I feel this may not be possible using just the rotateX/Y/Z properties and instead I might have to use the 3d matrix or rotate3d properties?

我知道使用CSS可能不是最简单的方法,但我仍然想这样做。有人可以向我指出如何解决此问题的正确方向吗?

I know this may not be the easiest thing to achieve using CSS but I still want to do it. Could someone point me in the right direction on how to solve this problem?

#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的属性相应地围绕x和y轴旋转多维数据集。 的值。

I can't really add any javascript because I'm actually coding the logic in purescript. But the code just registers a mousedown handler that takes the current mouse x and y, compares it to the last x and y and accordingly rotates the cube around the x and y axes by changing the transform property of .cube with a value like.

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


推荐答案

注意:这个问题在CSS中很难解决。如果您确实需要像这样的复杂转换,应该将新转换应用于先前的状态,则可以尝试其他方法。

Note: It turns out this problem is kinda difficult to solve in CSS. If you really need a complex transformation like this where new transformations should be applied onto the previous state maybe try some other method.

无论如何,我首先要解释一下我经历的步骤,遇到的问题以及解决该问题所采取的步骤。确实令人费解且混乱,但可以使用。最后,我放置了用作JavaScript的代码。

Anyway, I'm first going to explain the steps I went through, the problems I faced and the steps I took to solve it. It's really convoluted and messy but it works. At the end, I've put the code I used as JavaScript.

所以我已经开始了解有关CSS转换的两件事。一个主要的事情是,当您将字符串转换传递给 transform 属性时,像这样

So I've come to understand a couple of things about transformations in CSS. One main thing is that when you pass a string of transformations to the transform property, like this

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

这些转换是没有合并为一个单一的复合转换。而是应用第一个,然后在其上应用下一个,依此类推。因此,尽管我希望立方体对角线旋转90度,但并没有这样做。

these transformations are not combined into one single composite transformation. Instead the first one is applied, then the next one is applied on top of that and so on. So while I expected the cube to rotate diagonally by 90degrees, it didn't do that.

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

As @ihazkode suggested, rotate3d was the way to go. It allows rotation around any arbitrary axes instead of being limited to X, Y and Z axes. rotate3d takes 3 arguments

rotate3d(x, y, z, angle).

x y和z指定旋转轴。看起来是这样的:想象从(x,y,z) transform-origin 您指定的。这条线将成为旋转轴。现在,假设您正在寻找方向,从 (x,y,z)。从此视图来看,对象将顺时针旋转角度度。

x y and z specify the rotation axis. The way to look at it is like this: Imagine drawing a line from (x,y,z) to the transform-origin you specified. This line will the be axis of rotation. Now imagine you are looking towards the origin from (x,y,z). From this view, the object will rotate clockwise by angle degrees.

但是,我仍然面临一个问题。尽管 rotate3d 让我以更直观的方式旋转多维数据集,但仍然遇到以下问题:如果再次单击并尝试旋转一次(使用鼠标),则在旋转多维数据集之后多维数据集,它将恢复到其原始状态并从那里旋转,这不是我想要的。我希望它可以从当前状态旋转,而不管旋转状态如何。

However, I still faced a problem. Although rotate3d let's me rotate the cube in a far more intuitive way, I still faced the problem where after rotating the cube once (with the mouse) if I again clicked and tried rotating the cube, it would snap back to its original state and rotate from there, which is not what I wanted. I wanted it to rotate from it's current state, whatever rotation state that may be.

我发现使用 matrix3d可以用一种非常混乱的方式属性。基本上,每次发生mousedown和mousemove事件时,我都会按照以下步骤操作

I found a very messy way to do it using the matrix3d property. Basically I'd follow these steps every time the mousedown and mousemove events occurred


  1. 我会根据位置计算一个向量表示发生了鼠标向下移动,并且鼠标移动到当前位置。例如,如果在(123,145)处发生mousedown,然后在(120,143)处发生mousemove,则可以从这两个点制作矢量[x,y,z,m],其中

  1. I'd calculate a vector based on the position that mousedown occured and the current mouse position from mousemove. For example, if mousedown occured at (123,145) and then a mousemove occured at (120,143), then a vector can be made from these two points as [x, y, z, m] where

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

x is the x component which is the new x position minus the mouse down x position = 120 - 123 = -3

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

y is the y component, similar to x, which = 143-145 = -2

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

z = 0 since the mouse cannot move in the z direction

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

m is the magnitude of the vector which can be calculated as squareroot(x2 + y2) = 3.606

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

So the mouse movement can be represented as the vector [-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]。

Now notice that the rotation vector of the cube should be perpendicular to the mouse movement. For example, if I move my mouse straight up by 3 pixels, the mouse movement vector is [0,-1,0,3] (y is negative because in the browser the top left corner is the origin). But if I use this vector as the rotation vector and pass it into rotate3d, that rotates the cube clockwise (when looking from above) around the y axis. But that's not right! If I swipe my mouse upwards, it should rotate around it's x axis! To solve this, just swap x and y and negate the new x. That is, the vector should be [1,0,0,3]. Therefore, the vector from step 1 should instead be [2,-3,0,3.606].

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

Now I just set the transform property of my cube as

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

现在,我想出了如何旋转可以根据鼠标移动正确地设置多维数据集,而不会遇到尝试先创建 rotateX 然后创建 rotateY 的问题。

So now, I figured out how to rotate the cube correctly based on mouse movement, without facing the previous problem of trying to make a rotateX and then rotateY.


  1. 现在,多维数据集可以正确旋转了。但是,如果我放开鼠标,然后再次执行mousedown并尝试旋转多维数据集,该怎么办?如果我按照上面的相同步骤操作,将会发生什么事,我传递给 rotate3d 的新向量将替换旧向量。因此,将多维数据集重置为其初始位置,并对其应用新的旋转。但这是不对的。我希望多维数据集保持先前的状态,然后从的状态开始,多维数据集应通过新的旋转矢量进一步旋转。

  1. Now the cube can rotate correctly. But what if I let go of the mouse and then again perform a mousedown and try rotating the cube. If I follow the same steps from above, what happens is the new vector that I pass to rotate3d will replace the old one. So the cube is reset to it's initial position and the new rotation is applied to it. But that's not right. I want the cube to remain in the state it was in previously, and then from that state it should rotate further by the new rotation vector.

要执行此操作,我需要将新轮播追加到上一个轮播中。所以我可以做这样的事情

To do this, I need to append the new rotation onto the previous rotation. So I could do something like this

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

毕竟,这将执行第一次旋转,然后再执行第二次旋转。但是然后想象执行100转。 transform 属性将需要添加100个 rotate3d s。那不是解决这个问题的最佳方法。

After all, this would perform the first rotation and then perform the second rotation on top of that. But then imagine performing 100 rotations. The transform property would need to be fed 100 rotate3ds. That wouldn't be the best way to go about this.

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

Here's what I figured. At any point if you query the transform css property of a node like

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

您将获得3个值之一:如果对象完全没有被转换,则返回 none到目前为止,如果仅执行过2D转换,则使用2D转换矩阵(看起来像 matrix2d(...)),或者使用3D转换矩阵(看起来像 matrix3d(...)否则。

you get back one of 3 values: "none" if the object hasn't been transformed at all so far, a 2D transformation matrix (looks like matrix2d(...)) if only 2D transformations have peen performed, or a 3D transformation matrix (looks like matrix3d(...) otherwise.

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

So what I can do is, immediately after performing a rotate operation, query and get the transformation matrix of the cube and save it. Next time I perform a new rotation, do this:

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

这将首先将多维数据集转换为其最后的旋转状态,然后应用不需要再传递100个 rotate3d s。

This would first transform the cube to it's last state of rotation and then apply the new rotation on top of that. No need to pass a 100 rotate3ds.


  1. 我发现了最后一个问题。仍然存在相同的问题,即对象的轴随对象一起旋转。

假设我使用x变换将立方体沿x轴旋转90度

Suppose I rotate the cube 90 degrees along the x axis with

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

然后使用

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

我希望立方体向上旋转90度,然后向右旋转45度。但是相反,它向上旋转90度,然后围绕当前可见的正面旋转45度向右旋转。这是我在问题中提到的完全相同的问题。问题是,尽管 rotate3d 允许您围绕任意旋转轴旋转对象,但是该任意轴仍以该对象的轴为参考,而不是相对于用户固定的x,y和z轴。这与轴与对象一起旋转时的情况一样。

I would expect the cube to rotate upwards 90 and then rotate to the right by 45. But instead it rotated up by 90 and then rotated around currently visible front face by 45 instead of rotating to the right. It's the exact same problem I mentioned in my question. The problem is, although rotate3d allows you to rotate an object around any arbitrary axis of rotation, that arbitrary axis is still with reference to the axis of the object and not by a fixed x, y and z axes with respect to the user. It's the same gosh darn problem of the axes rotating with the object.

因此,如果多维数据集当前处于某种旋转状态,我想要它要进一步旋转通​​过鼠标在步骤1和2中获得的矢量(x,y,z),我首先需要根据多维数据集的状态将该矢量转换为正确位置

So if the cube is currently in some rotated state and I want it to rotate further on a vector (x,y,z) obtained through the mouse as in step 1 and 2, I first need to somehow transform this vector into it's correct position based on what state the cube is in currently.

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

What I noticed is if you take the rotation vector as a 4x1 matrix like this

x
y
z
angle

并将 matrix3d 矩阵作为4x4矩阵,然后,如果我将3D变换矩阵乘以旋转矢量,我将得到旧的旋转矢量,但将其转换为正确位置。现在,我可以像在第3步中一样,在3d矩阵之后应用此向量,并最终使多维数据集的行为完全符合应有的方式。

and took the matrix3d matrix as a 4x4 matrix, then if I multiplied the 3D transformation matrix by the rotation vector, I get the old rotation vector but transformed into it's correct position. Now I can apply this vector after the 3d matrix as in step 3 and FINALLY the cube is behaving exactly the way it should.

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

Okay that was enough talk. Here's the code I used. Sorry if it's not very clear.

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

这篇关于在固定轴上旋转CSS立方体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆