2011-12-27 9 views
17

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.Calcular dimensiones absolutas de un div girado en perspectiva con css3

¿Cómo calcules las dimensiones absolutas del trapecio que se muestra? (Ancho, max-altura, ángulos)

lo hice solamente averiguar una fórmula que calcula la anchura pero sin tener en cuenta la perspectiva, de modo que el valor difiere algunos píxeles (JavaScript):

var absoluteWidth = Math.cos(45 * (Math.PI/180)) * 500); 

EDIT: Aquí está la especificación acerca de la función -webkit-perspectiva:

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 visor 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 mostrar la página web entre y un punto a una distancia infinita del visor ). La profundidad, dada como parámetro para 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 da una cantidad moderada de escorzo de y un valor de 200 da una cantidad extrema de . 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 es no 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

+2

Esta es una buena pregunta: nunca he sido muy claro sobre exactamente cómo funciona el valor de la perspectiva. –

+2

Agregué la definición oficial para la función de perspectiva del W3C. Todavía no estoy seguro de cómo calcular eso. – Elias

+0

A pesar de haber hecho un grado de matemáticas y saber un poco sobre cómo las matrices se relacionan con el álgebra lineal, todavía no estoy seguro de cómo usar esto con gráficos en 3D. Puede tomar la opción de basura si conoce algunos de los requisitos, y mida el ancho con diferentes perspectivas, colóquelo en una hoja de cálculo y luego ajústele manualmente una curva. Probablemente sea más rápido de todos modos que el cálculo completo, y tendría suficiente precisión (para los píxeles debe redondear al más cercano de todos modos, entonces un error de <0.5 no hace la diferencia) –

Respuesta

8

consigo un dolor de cabeza con las matrices, por lo que estoy haciendo esto con proporciones.

Si ve el div desde arriba (de ahí ver la rotación en las dos dimensiones que tiene lugar en), que se está viendo 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 la rotación en el eje y, las coordenadas se convertirá, de manera similar a lo que usted declaró

(-Math.cos(angle) * w/2, -Math.sin(angle) * w/2) 
(Math.cos(angle) * w/2, Math.sin(angle) * w/2) 

, siendo el giro a la izquierda, con el origen en el centro de la div, y de 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. Esto lo deduzco del hecho de que cuando se proyectan divisiones no giradas, siguen siendo del mismo tamaño. Si toma un punto con la distancia p (el valor de la perspectiva) desde el plano z, entonces con las coordenadas xz (0, -p) y dibuja 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 coordenadas del nuevo 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), se obtiene que

p : x = (p + sin*w/2) : cos*w/2 
x = (p * cos*w/2)/(p + sin*w/2) 

que en general significa que cuando se proyecta el punto (x, y, z) en el plan se obtiene

x * p/(p + z) 
y * p/(p + z) 
0 

Así que su último Las coordenadas div (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 que puede calcular su ancho pero también su posición, que no es trivial, ya que su mitad más cercana al visor aparecerá más grande que la otra mitad.

vistazo a la siguiente prueba para más detalles (se produce un error cuando se está demasiado cerca de los objetos, no estoy seguro de por qué, probablemente algunos desbordamientos de 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 estás no dando un valor de perspectiva, el resultado será igual a tener un valor infinito para él.

+0

Por supuesto, esto está destinado a los navegadores que admiten la propiedad -webkit-perspective, que en este momento son safari o navegadores en Mac, supongo. De lo contrario, simplemente use p = infinito, lo que significa reemplazar la expresión de retorno del método calc() con x, descartando por lo tanto z (que no es relevante cuando está infinitamente distante). – djjeck

+0

¡Guau, eso es realmente increíble! ¡Funciona a las mil maravillas! ¿Cree que su algoritmo también podría ser útil para calcular la altura y el ángulo (visualizados) del div girado? – Elias

+0

Con ángulo, ¿te refieres al formado por el polígono proyectado, verdad? Puede adaptar el algoritmo para trabajar en las tres dimensiones, y no solo en el plano xz. Debería encontrar, mediante funciones angulares, las cuatro coordenadas del rectángulo después de la rotación, y luego usar las coordenadas * = p/(p + z) para obtener las coordenadas proyectadas. Entonces tienes el polígono, y puedes hacer lo que quieras con eso =) – djjeck

Cuestiones relacionadas