如何将rotateX(50deg) rotateY(20deg) rotateZ(15deg)
速记合并rotate3d()
?
如何将rotateX(50deg) rotateY(20deg) rotateZ(15deg)
速记合并rotate3d()
?
Answers:
rotateX(50deg)
相当于 rotate3d(1, 0, 0, 50deg)
rotateY(20deg)
相当于 rotate3d(0, 1, 0, 20deg)
rotateZ(15deg)
相当于 rotate3d(0, 0, 1, 15deg)
所以...
rotateX(50deg) rotateY(20deg) rotateZ(15deg)
相当于
rotate3d(1, 0, 0, 50deg) rotate3d(0, 1, 0, 20deg) rotate3d(0, 0, 1, 15deg)
对于泛型rotate3d(x, y, z, α)
,您具有矩阵
哪里
现在rotate3d
,您将获得3个变换中每个变换的矩阵,并将它们相乘。生成的矩阵是对应于生成的single的矩阵rotate3d
。不确定从中提取值的方法有多容易,但是从中提取值rotate3d
肯定很容易matrix3d
。
在第一种情况下(rotateX(50deg)
或rotate3d(1, 0, 0, 50deg)
),您具有:
x = 1
,y = 0
,z = 0
,α = 50deg
因此,在这种情况下,矩阵的第一行是1 0 0 0
。
第二个是0 cos(50deg) -sin(50deg) 0
。
第三个0 sin(50deg) cos(50deg) 0
。
显然,第四个是0 0 0 1
。
在第二种情况下,你有x = 0
,y = 1
,z = 0
,α = 20deg
。
第一行:cos(20deg) 0 sin(20deg) 0
。
第二行:0 1 0 0
。
第三行:-sin(20) 0 cos(20deg) 0
。
第四: 0 0 0 1
在第三种情况下,你有x = 0
,y = 0
,z = 1
,α = 15deg
。
第一行:cos(15deg) -sin(15deg) 0 0
。
第二排sin(15deg) cos(15deg) 0 0
。
第三行0 0 1 0
和第四行0 0 0 1
分别是和。
注意:您可能已经注意到,rotateY变换的sin值的符号不同于其他两个变换。这不是计算错误。原因是,对于屏幕,y轴指向下而不是指向上。
因此,这是4x4
您需要相乘的三个矩阵,以获得4x4
用于得到的单个rotate3d
转换的矩阵。就像我说过的那样,我不确定取出4个值有多么容易,但是4x4矩阵中的16个元素恰好是matrix3d
等效链式转换的16个参数。
编辑:
实际上,事实证明这很容易...您可以为rotate3d
矩阵计算矩阵的迹线(对角元素的总和)。
4 - 2*2*(1 - cos(α))/2 = 4 - 2*(1 - cos(α)) = 2 + 2*cos(α)
然后,您可以计算这三个4x4
矩阵的乘积的迹线,并将结果与2 + 2*cos(α)
extract相等α
。然后你计算x
,y
,z
。
在这种特殊情况下,如果我计算正确,则由这三个4x4
矩阵的乘积得出的矩阵的轨迹将是:
T =
cos(20deg)*cos(15deg) +
cos(50deg)*cos(15deg) - sin(50deg)*sin(20deg)*cos(15deg) +
cos(50deg)*cos(20deg) +
1
所以cos(α) = (T - 2)/2 = T/2 - 1
,这意味着α = acos(T/2 - 1)
。
[x,y,z]
向量被归一化时,即,向量长度Math.sqrt(x*x + y*y + z*z)
为一时,旋转矩阵才适用。如果未归一化,则可以轻松地将其转换为归一化的形式,方法是每次潜水x
,y
并按z
其长度进行。
根据您要执行的操作,此“ hack”可以为您提供帮助。假设您正在制作动画,并且想要在转换之后添加转换,依此类推,并且您不希望CSS看起来像是在进行100转换:
这适用于chrome:1.将所需的任何变换应用于元素。2.下次要添加转换时,请将其添加到计算的转换中:“ window.getComputedStyle(element).transform”-但请确保将新转换放在左侧。3.现在,您的变换看起来像是“ rotateZ(30deg)matrix3d(......)。” 4.下次您要添加另一个变换时,请重复此过程-Chrome始终将变换减少为matrix3d表示法。
TL; DR-应用所需的任何变换,然后获取计算出的matrix3d变换。
此技巧还可以使您快速(即,无需自己做任何数学运算)就可以实现使对象相对于参考框架沿任何方向旋转的功能。请参阅以下示例:
编辑:我也添加了xyz翻译。使用此功能,很容易将对象放置在特定3d位置并牢记特定方向。或者...想象一个立方体,该立方体会反弹并根据其着陆方式改变每次旋转的自旋轴!
var boxContainer = document.querySelector('.translator'),
cube = document.getElementById('cube'),
optionsContainer = document.getElementById('options');
var dims = ['x', 'y', 'z'];
var currentTransform;
var currentTranslate;
var init = function () {
optionsContainer.querySelector('.xRotation input')
.addEventListener('input', function (event) {
if (currentTransform != 'none') {
var newTransform = 'rotateX(' + (360 - event.target.value) + 'deg) ' + currentTransform;
} else {
var newTransform = 'rotateX(' + (360 - event.target.value) + 'deg)';
}
cube.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.yRotation input')
.addEventListener('input', function (event) {
if (currentTransform != 'none') {
var newTransform = 'rotateY(' + (360 - event.target.value) + 'deg) ' + currentTransform;
} else {
var newTransform = 'rotateY(' + (360 - event.target.value) + 'deg)';
}
cube.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.zRotation input')
.addEventListener('input', function (event) {
if (currentTransform != 'none') {
var newTransform = 'rotateZ(' + (360 - event.target.value) + 'deg) ' + currentTransform;
} else {
var newTransform = 'rotateZ(' + (360 - event.target.value) + 'deg)';
}
cube.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.xTranslation input')
.addEventListener('input', function (event) {
if (currentTranslate != 'none') {
var newTransform = 'translateX(' + (100 - event.target.value) + 'px) ' + currentTranslate;
} else {
var newTransform = 'translateX(' + (100 - event.target.value) + 'px)';
}
boxContainer.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.yTranslation input')
.addEventListener('input', function (event) {
if (currentTranslate != 'none') {
var newTransform = 'translateY(' + (100 - event.target.value) + 'px) ' + currentTranslate;
} else {
var newTransform = 'translateY(' + (100 - event.target.value) + 'px)';
}
boxContainer.style.transform = newTransform;
}, false);
optionsContainer.querySelector('.zTranslation input')
.addEventListener('input', function (event) {
if (currentTranslate != 'none') {
var newTransform = 'translateZ(' + (500 - event.target.value) + 'px) ' + currentTranslate;
} else {
var newTransform = 'translateZ(' + (500 - event.target.value) + 'px)';
}
boxContainer.style.transform = newTransform;
}, false);
reset();
};
function reset() {
currentTransform = window.getComputedStyle(cube).transform;
currentTranslate = window.getComputedStyle(boxContainer).transform;
optionsContainer.querySelector('.xRotation input').value = 360;
optionsContainer.querySelector('.yRotation input').value = 360;
optionsContainer.querySelector('.zRotation input').value = 360;
optionsContainer.querySelector('.xTranslation input').value = 100;
optionsContainer.querySelector('.yTranslation input').value = 100;
optionsContainer.querySelector('.zTranslation input').value = 500;
}
window.addEventListener('DOMContentLoaded', init, false);
document.addEventListener('mouseup', reset, false);
.translator
{
height: 200px;
position: absolute;
width: 200px;
transform-style: preserve-3d;
}
.threeSpace
{
height: 200px;
moz-perspective: 1200px;
o-perspective: 1200px;
perspective: 200px;
position: absolute;
transform-origin: 50px 50px 100px;
webkit-perspective: 1200px;
width: 100px;
perspective-origin: 100px 25px;
transform-style: preserve-3d;
}
#pointer{
position:relative;
height:2px;
width:2px;
top:25px;
left:100px;
background:blue;
z-index:9999;
}
#cube
{
height: 100%;
moz-transform-origin: 90px 110px 0px;
moz-transform-style: preserve-3d;
o-transform-origin: 90px 110px 0px;
o-transform-style: preserve-3d;
position: absolute;
transform-origin: 90px 110px 0px;
transform-style: preserve-3d;
webkit-transform-origin: 90px 110px 0px;
webkit-transform-style: preserve-3d;
width: 100%;
}
#cube .midPoint{
position:absolute;
top:48px;
left:48px;
height:1px;
width:1px;
background:green;
}
#cube figure
{
border: 2px solid black;
color: white;
display: block;
font-size: 60px;
font-weight: bold;
height: 96px;
line-height: 96px;
position: absolute;
text-align: center;
width: 96px;
/* transform-style: preserve-3d; */
}
#cube .front
{
background: hsl(0, 100%, 50%);
}
#cube .back
{
background: hsl(60, 100%, 50%);
}
#cube .right
{
background: hsl(120, 100%, 50%);
}
#cube .left
{
background: hsl(180, 100%, 50%);
}
#cube .top
{
background: hsl(240, 100%, 50%);
}
#cube .bottom
{
background: hsl(300, 100%, 50%);
}
#cube .front
{
moz-transform: translateZ(50px);
o-transform: translateZ(50px);
transform: translateZ(50px);
webkit-transform: translateZ(50px);
}
#cube .back
{
moz-transform: rotateX(-180deg) translateZ(50px);
o-transform: rotateX(-180deg) translateZ(50px);
transform: rotateX(-180deg) translateZ(50px);
webkit-transform: rotateX(-180deg) translateZ(50px);
}
#cube .right
{
moz-transform: rotateY(90deg) translateZ(50px);
o-transform: rotateY(90deg) translateZ(50px);
transform: rotateY(90deg) translateZ(50px);
webkit-transform: rotateY(90deg) translateZ(50px);
}
#cube .left
{
moz-transform: rotateY(-90deg) translateZ(50px);
o-transform: rotateY(-90deg) translateZ(50px);
transform: rotateY(-90deg) translateZ(50px);
webkit-transform: rotateY(-90deg) translateZ(50px);
}
#cube .top
{
moz-transform: rotateX(90deg) translateZ(50px);
o-transform: rotateX(90deg) translateZ(50px);
transform: rotateX(90deg) translateZ(50px);
webkit-transform: rotateX(90deg) translateZ(50px);
}
#cube .bottom
{
moz-transform: rotateX(-90deg) translateZ(50px);
o-transform: rotateX(-90deg) translateZ(50px);
transform: rotateX(-90deg) translateZ(50px);
webkit-transform: rotateX(-90deg) translateZ(50px);
}
#options{
position:absolute;
width:80%;
top:40%;
}
#options input
{
width: 60%;
}
<body>
<div class="threeSpace">
<div id="pointer"></div>
<div class="translator">
<div id="cube">
<figure class="front"><div class='midPoint'></div></figure>
<figure class="back"></figure>
<figure class="right"></figure>
<figure class="left"></figure>
<figure class="top"></figure>
<figure class="bottom"></figure>
</div>
</div>
</div>
<section id="options">
<p class="xRotation">
<label>xRotation</label>
<input type="range" min="0" max="720" value="360" data-units="deg" />
</p>
<p class="yRotation">
<label>yRotation</label>
<input type="range" min="0" max="720" value="360" data-units="deg" />
</p>
<p class="zRotation">
<label>zRotation</label>
<input type="range" min="0" max="720" value="360" data-units="deg" />
</p>
<p class="xTranslation">
<label>xTranslation</label>
<input type="range" min="0" max="200" value="100" data-units="deg" />
</p>
<p class="yTranslation">
<label>yTranslation</label>
<input type="range" min="0" max="200" value="100" data-units="deg" />
</p>
<p class="zTranslation">
<label>zTranslation</label>
<input type="range" min="0" max="1000" value="500" data-units="deg" />
</p>
</section>
</body>