javascript - css 3d perspective
Calcular dimensiones absolutas de un div girado en perspectiva con css3 (1)
Me da dolor de cabeza con las matrices, así que estoy haciendo esto con proporciones.
Si ve el div desde arriba (por lo tanto, observa la rotación en las dos dimensiones en las que tiene lugar), lo ve como un segmento en el plano xz, con coordenadas (-250, 0) (250, 0)
o en general (-w/2, 0) (w/2, 0)
Después de una rotación en el eje y, las coordenadas se convertirán, de manera similar a lo que usted indicó
(-Math.cos(angle) * w/2, -Math.sin(angle) * w/2)
( Math.cos(angle) * w/2, Math.sin(angle) * w/2)
, siendo la rotación en sentido contrario a las agujas del reloj, con el origen en el centro del div, y del angle
radianes.
Usar la perspectiva significa que estas coordenadas no se muestran simplemente descartando la z, sino que primero se proyectan según su distancia del observador.
Ahora, el plano de proyección es aquel en el que se encuentran las cosas no giradas, con z = 0. Deduzco esto del hecho de que cuando se proyectan los div sin rotación, siguen siendo del mismo tamaño. Si tomas un punto con la distancia p
(el valor de la perspectiva) desde el plano z, entonces con las coordenadas xz (0, -p), y trazas una línea desde este punto hasta los vértices del segmento girado, hasta cuando cruza el plan de proyección, los puntos que obtienes son las nuevas coordenadas de segmento que producen el tamaño final div.
Con una proporción entre los triángulos (0, -p) (0, 0) (x, 0)
y (0, -p) (0, sin*w/2) (cos*w/2, sin*w/2)
, obtienes eso
p : x = (p + sin*w/2) : cos*w/2
x = (p * cos*w/2) / (p + sin*w/2)
que en general significa que cuando proyecta el punto (x, y, z)
en el plan que obtiene
x * p / (p + z)
y * p / (p + z)
0
Entonces tus coordenadas div finales (en xz, relativas al centro de div) serán
(-Math.cos(angle) * w/2 * p / (p + -Math.sin(angle) * w/2), 0)
( Math.cos(angle) * w/2 * p / (p + Math.sin(angle) * w/2), 0)
Desde el cual puede calcular su ancho, pero también su posición, que no es trivial, ya que su mitad más cercana al espectador aparecerá más grande que la otra mitad.
Mira la siguiente prueba para más detalles (falla cuando estás demasiado cerca de los objetos, no estoy seguro de por qué, probablemente algunos desbordamientos variables)
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
var WIDTH = 500;
var P = 300;
jQuery(function(){
function test(width, angle, p) {
$(''body'').
append($(''<div id="info" />'')).
append($(''<div id="container" />'').
css({
margin: ''50px 0px'',
border: ''1px solid black'',
width: width+''px'',
''-webkit-perspective'': p
}).
append($(''<div id="real" />'').addClass(''the_div'').css({ ''width'': width+''px'' }))).
append($(''<div id="fake" />'').addClass(''the_div''));
setInterval(function() {
angle += 1;
$(''#real'').css({ ''-webkit-transform'': ''rotateY(''+angle+''deg)'' }).html(width);
// initial coordinates
var A = 0;
var B = width;
// translate the center (assuming -perspective-origin at 50%)
A -= width/2;
B -= width/2;
// new coordinates
A = calc(A, angle*Math.PI/180, p);
B = calc(B, angle*Math.PI/180, p);
// translate back
A += width/2;
B += width/2;
if(B < A) { var tmp = A; A = B; B = tmp; } // swap
var realwidth = B-A;
$(''#fake'').html(width+''<br/>''+A+'', ''+B).css({
''width'': realwidth+''px'',
''margin-left'': A+''px''
});
// shows debug information
var debug = function(values) { return values.map(function(i){ return i+'': ''+eval(i); }).join(''<br />''); }
$(''#info'').html($(''<div />'').html(debug([''width'', ''p'', ''angle'', ''A'', ''B'', ''realwidth''])));
}, 40);
}
function calc(oldx, angle, p) {
var x = Math.cos(angle) * oldx;
var z = Math.sin(angle) * oldx;
return x * p / (p+z);
}
test(WIDTH, 0, P);
});
</script>
<style type="text/css">
* { margin: 0px; padding: 0px; }
body { padding: 40px 100px; }
.the_div { height: 100px; border: 2px solid black; background-color: rgba(255, 192, 0, 0.5); }
</style>
</head>
<body></body>
</html>
Tenga en cuenta que si no está dando un valor de perspectiva, el resultado será igual a tener un valor infinito para él.
digamos que tenemos un div con un tamaño de 500x500px y lo rotamos en el eje x a través de css 45 grados, considerando un valor de perspectiva webkit de 1600px.
¿Cómo calcularías las dimensiones absolutas del trapecio mostrado? (ancho, altura máxima, ángulos)
Solo encontré una fórmula que calcula el ancho pero sin considerar la perspectiva, por lo que el valor difiere algunos píxeles (JavaScript):
var absoluteWidth = Math.cos(45 * (Math.PI / 180)) * 500);
EDITAR: Aquí está la especificación sobre la función -webkit-perspective:
perspectiva (<número>)
especifica una matriz de proyección en perspectiva. Esta matriz mapea un cubo de visualización en una pirámide cuya base está infinitamente lejos del observador y cuyo pico representa la posición del espectador. El área visible es la región delimitada por los cuatro bordes de la ventana gráfica (la parte de la ventana del navegador utilizada para representar la página web entre la posición del espectador y un punto a una distancia infinita del espectador). La profundidad, dada como el parámetro de la función, representa la distancia del plano z = 0 desde el visor. Los valores más bajos dan una pirámide más aplanada y, por lo tanto, un efecto de perspectiva más pronunciado. El valor se da en píxeles, por lo que un valor de 1000 proporciona una cantidad moderada de escorzo y un valor de 200 da una cantidad extrema. La matriz se calcula comenzando con una matriz de identidad y reemplazando el valor en la fila 3, columna 4 con el valor -1 / profundidad. El valor para la profundidad debe ser mayor que cero; de lo contrario, la función no es válida.
En cuanto a la "matriz de proyección en perspectiva", esto es lo que encontré en Wikipedia: http://en.wikipedia.org/wiki/3D_projection#Perspective_projection