在固定轴上旋转 CSS 立方体
Rotating CSS cube on fixed axes
我有一个使用 CSS 构建的立方体。它由 6 个面组成,每个面变换形成立方体的一个面,所有 6 个面都在一个 <div>
下,其中 class .cube
。我对立方体所做的任何旋转都是在此封闭 cube
class.
上完成的
我希望立方体根据鼠标拖动输入进行旋转。到目前为止它有点管用。我只是将 x 和 y 鼠标移动转换为围绕 x 和 y 轴的立方体旋转。
但这有一个主要问题。我执行旋转作为一个简单的
transform: rotateX(xdeg) rotateY(ydeg)
CSS属性。这个问题是 y 轴旋转随着 x 旋转而旋转。
假设我将立方体绕 x 轴旋转 90 度。现在,如果我尝试将立方体也沿 y 轴旋转 90 度,我希望立方体向右或向左旋转 90 度(从我的角度来看)。但相反,它围绕其当前可见的正面旋转。也就是说,由于首先发生的 x 轴旋转,y 轴旋转了 90 度,因此现在从用户的角度来看,立方体似乎在绕其 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>
我真的无法添加任何内容 javascript 因为我实际上是在用纯脚本编写逻辑代码。但是代码只是注册了一个 mousedown 处理程序,该处理程序采用当前鼠标 x 和 y,将其与最后一个 x 和 y 进行比较,并通过更改 [=16= 的变换 属性 相应地围绕 x 和 y 轴旋转立方体] 的值类似于
{transform: "rotateX(90deg) rotateY(90deg)"}
使用rotate3d
它相对容易使用,但您仍然需要link将您当前的跟踪脚本设置为正确的参数
您可以控制旋转量(以度为单位)和受影响的轴 (x,y,z)。您可以同时select一个。
示例 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>
示例 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>
示例 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>
示例 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>
注意:原来这个问题在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 事件时,我都会按照这些步骤操作
我会根据 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 是向量的大小,可以计算为平方根(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 个值之一:"none" 如果对象到目前为止还没有被转换,一个 2D 转换矩阵(看起来像 matrix2d(...)
)如果只有 2D 转换被喷丸执行,或 3D 变换矩阵(否则看起来像 matrix3d(...)
。
所以我能做的是,在执行旋转操作后,立即查询并获取立方体的变换矩阵并保存。下次我执行新的轮换时,请执行以下操作:
transform: "matrix3d(saved_matrix_from_last_rotation) rotate3d(new_rotation_vector)"
这将首先将立方体变换到它的最后一个旋转状态,然后在其上应用新的旋转。无需通过 100 rotate3d
s.
- 我发现了最后一个问题。仍然存在对象的轴随对象旋转的相同问题。
假设我将立方体沿 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;
}
我有一个使用 CSS 构建的立方体。它由 6 个面组成,每个面变换形成立方体的一个面,所有 6 个面都在一个 <div>
下,其中 class .cube
。我对立方体所做的任何旋转都是在此封闭 cube
class.
我希望立方体根据鼠标拖动输入进行旋转。到目前为止它有点管用。我只是将 x 和 y 鼠标移动转换为围绕 x 和 y 轴的立方体旋转。
但这有一个主要问题。我执行旋转作为一个简单的
transform: rotateX(xdeg) rotateY(ydeg)
CSS属性。这个问题是 y 轴旋转随着 x 旋转而旋转。
假设我将立方体绕 x 轴旋转 90 度。现在,如果我尝试将立方体也沿 y 轴旋转 90 度,我希望立方体向右或向左旋转 90 度(从我的角度来看)。但相反,它围绕其当前可见的正面旋转。也就是说,由于首先发生的 x 轴旋转,y 轴旋转了 90 度,因此现在从用户的角度来看,立方体似乎在绕其 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>
我真的无法添加任何内容 javascript 因为我实际上是在用纯脚本编写逻辑代码。但是代码只是注册了一个 mousedown 处理程序,该处理程序采用当前鼠标 x 和 y,将其与最后一个 x 和 y 进行比较,并通过更改 [=16= 的变换 属性 相应地围绕 x 和 y 轴旋转立方体] 的值类似于
{transform: "rotateX(90deg) rotateY(90deg)"}
使用rotate3d
它相对容易使用,但您仍然需要link将您当前的跟踪脚本设置为正确的参数
您可以控制旋转量(以度为单位)和受影响的轴 (x,y,z)。您可以同时select一个。
示例 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>
示例 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>
示例 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>
示例 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>
注意:原来这个问题在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 事件时,我都会按照这些步骤操作
我会根据 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 是向量的大小,可以计算为平方根(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 个值之一:"none" 如果对象到目前为止还没有被转换,一个 2D 转换矩阵(看起来像 matrix2d(...)
)如果只有 2D 转换被喷丸执行,或 3D 变换矩阵(否则看起来像 matrix3d(...)
。
所以我能做的是,在执行旋转操作后,立即查询并获取立方体的变换矩阵并保存。下次我执行新的轮换时,请执行以下操作:
transform: "matrix3d(saved_matrix_from_last_rotation) rotate3d(new_rotation_vector)"
这将首先将立方体变换到它的最后一个旋转状态,然后在其上应用新的旋转。无需通过 100 rotate3d
s.
- 我发现了最后一个问题。仍然存在对象的轴随对象旋转的相同问题。
假设我将立方体沿 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;
}