【发布时间】:2015-01-02 06:58:32
【问题描述】:
rotateX(50deg) rotateY(20deg) rotateZ(15deg)如何组合成简写rotate3d()?
【问题讨论】:
标签: css css-transforms
rotateX(50deg) rotateY(20deg) rotateZ(15deg)如何组合成简写rotate3d()?
【问题讨论】:
标签: css css-transforms
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, α),你有矩阵
在哪里
您现在获得了 3 个rotate3d 变换中的每一个的矩阵,并将它们相乘。并且得到的矩阵是对应于得到的单个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 矩阵相乘,以便为生成的单个 rotate3d 变换获得 4x4 矩阵。正如我所说,我不确定取出 4 个值有多容易,但 4x4 矩阵中的 16 个元素正是链式变换的 matrix3d 等效项的 16 个参数。
编辑:
实际上,这很容易......您计算rotate3d 矩阵的矩阵的迹线(对角线元素的总和)。
4 - 2*2*(1 - cos(α))/2 = 4 - 2*(1 - cos(α)) = 2 + 2*cos(α)
然后计算三个4x4 矩阵的乘积的迹线,将结果等同于提取α 的2 + 2*cos(α)。然后你计算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)为1时。如果它没有被规范化,它可以很容易地转换成一个规范化的,通过将每个 x、y 和 z 按它们的长度进行潜水。
语法:
rotate3d(x, y, z, a)
价值观:
x
是一个<number>,描述了表示旋转轴的向量的 x 坐标。y
是一个<number>,描述了表示旋转轴的向量的 y 坐标。z
是一个<number>,描述了表示旋转轴的向量的 z 坐标。a
是一个<angle>,代表旋转的角度。正角表示顺时针旋转,负角表示逆时针旋转。点赞:
.will-distort{
transform:rotate3d(10, 10, 10, 45deg);
}
【讨论】:
rotate3d,而不是rotate3d的定义。
取决于你想要做什么,这个“黑客”可以帮助你。假设您正在制作动画,并且您想要在变换之后添加变换等等,并且您不希望 CSS 看起来像是在进行 100 次变换:
这适用于铬: 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>
【讨论】:
确切的值是rotate3d(133,32,58,58deg)
查看fiddle(对于 chrome 和 Safari,使用 -webkit-transform)
【讨论】: