¿Cómo combinar rotateX(50deg) rotateY(20deg) rotateZ(15deg)
en taquigrafía rotate3d()
?
¿Cómo combinar rotateX(50deg) rotateY(20deg) rotateZ(15deg)
en taquigrafía rotate3d()
?
Respuestas:
rotateX(50deg)
es equivalente a rotate3d(1, 0, 0, 50deg)
rotateY(20deg)
es equivalente a rotate3d(0, 1, 0, 20deg)
rotateZ(15deg)
es equivalente a rotate3d(0, 0, 1, 15deg)
Entonces...
rotateX(50deg) rotateY(20deg) rotateZ(15deg)
es equivalente a
rotate3d(1, 0, 0, 50deg) rotate3d(0, 1, 0, 20deg) rotate3d(0, 0, 1, 15deg)
Para un genérico rotate3d(x, y, z, α)
, tienes la matriz
dónde
Ahora obtienes las matrices para cada una de las 3 rotate3d
transformadas y las multiplicas. Y la matriz resultante es la matriz correspondiente al sencillo resultante rotate3d
. No estoy seguro de lo fácil que es extraer los valores rotate3d
de él, pero seguro que es fácil extraerlos para uno solo matrix3d
.
En el primer caso ( rotateX(50deg)
o rotate3d(1, 0, 0, 50deg)
), tienes:
x = 1
, y = 0
,z = 0
,α = 50deg
Entonces, la primera fila de la matriz en este caso es 1 0 0 0
.
El segundo es 0 cos(50deg) -sin(50deg) 0
.
El tercero 0 sin(50deg) cos(50deg) 0
.
Y el cuarto es obviamente 0 0 0 1
.
En el segundo caso, tiene x = 0
, y = 1
,z = 0
,α = 20deg
.
Primera fila: cos(20deg) 0 sin(20deg) 0
.
Segunda fila: 0 1 0 0
.
Tercera fila: -sin(20) 0 cos(20deg) 0
.
Cuarto: 0 0 0 1
En el tercer caso, tiene x = 0
, y = 0
,z = 1
,α = 15deg
.
Primera fila: cos(15deg) -sin(15deg) 0 0
.
Segunda fila sin(15deg) cos(15deg) 0 0
.
Y la tercera y la cuarta fila son 0 0 1 0
y 0 0 0 1
respectivamente.
Nota : puede haber notado que los signos de los valores sin para la transformada rotateY son diferentes a los de las otras dos transformadas. No es un error de cálculo. La razón de esto es que, en la pantalla, el eje Y apunta hacia abajo, no hacia arriba.
Entonces, estas son las tres 4x4
matrices que necesita multiplicar para obtener la 4x4
matriz de la rotate3d
transformación simple resultante . Como dije, no estoy seguro de lo fácil que puede ser sacar los 4 valores, pero los 16 elementos de la matriz 4x4 son exactamente los 16 parámetros del matrix3d
equivalente de la transformación encadenada.
EDITAR :
En realidad, resulta que es bastante fácil ... Calcula la traza (suma de elementos diagonales) de la matriz para la rotate3d
matriz.
4 - 2*2*(1 - cos(α))/2 = 4 - 2*(1 - cos(α)) = 2 + 2*cos(α)
Luego calcula la traza para el producto de las tres 4x4
matrices, equipara el resultado con lo 2 + 2*cos(α)
que extrae α
. A continuación, a calcular x
, y
, z
.
En este caso particular, si calculé correctamente, la traza de la matriz resultante del producto de las tres 4x4
matrices será:
T =
cos(20deg)*cos(15deg) +
cos(50deg)*cos(15deg) - sin(50deg)*sin(20deg)*cos(15deg) +
cos(50deg)*cos(20deg) +
1
Entonces cos(α) = (T - 2)/2 = T/2 - 1
, lo que significa eso α = acos(T/2 - 1)
.
[x,y,z]
vector está normalizado, es decir, solo si la longitud del vector Math.sqrt(x*x + y*y + z*z)
es uno. Si no se normaliza, fácilmente se puede convertir en un uno normalizado, por buceo de cada x
, y
y z
por su longitud.
Sintaxis:
rotate3d(x, y, z, a)
Valores:
x
Es un <number>
descripción de la coordenada x del vector que denota el eje de rotación.y
Es una <number>
descripción de la coordenada y del vector que denota el eje de rotación.z
Es una <number>
descripción de la coordenada z del vector que denota el eje de rotación.a
Es una <angle>
representación del ángulo de rotación. Un ángulo positivo denota una rotación en el sentido de las agujas del reloj, un ángulo negativo en el sentido contrario a las agujas del reloj.Como en :
.will-distort{
transform:rotate3d(10, 10, 10, 45deg);
}
rotate3d
, no la definición de rotate3d
.
Depende de lo que intente hacer, este 'truco' podría ayudarlo. Digamos que está haciendo una animación, y quiere agregar transformación tras transformación y así sucesivamente, y no quiere que el CSS parezca que está haciendo cientos de transformaciones:
Esto funciona en Chrome: 1. Aplique cualquier transformación que desee a un elemento. 2. La próxima vez que desee agregar una transformación, agréguela a la transformación calculada: "window.getComputedStyle (element) .transform", pero asegúrese de poner la nueva transformación a la izquierda. 3. Ahora su transformación se vería como "rotateZ (30deg) matrix3d (......). 4. La próxima vez que desee agregar otra transformación, repita el proceso - Chrome siempre reduce las transformaciones a la notación matrix3d.
TL; DR: aplique las transformaciones que desee y luego obtenga la transformación matrix3d calculada.
Este truco también te permite rápidamente (es decir, sin hacer ningún cálculo por ti mismo) crear una funcionalidad que gire un objeto con respecto a tu marco de referencia en cualquier dirección. Vea la muestra a continuación:
EDITAR : También he agregado traducciones xyz. Con esto, sería muy fácil colocar objetos en ubicaciones 3D específicas con orientaciones específicas en mente. O ... ¡imagina un cubo que rebota y cambia su eje de giro con cada rebote dependiendo de cómo aterrice!
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>