2011-12-27 12 views
17

, powiedzmy, że mamy element div o rozmiarze 500x500px i obracamy go na osi X za pomocą css o 45 stopni, biorąc pod uwagę wartość perspektywy 1600 piksla.Oblicz wymiary bezwzględne elementu div obróconego w perspektywie za pomocą css3

Jak można obliczyć bezwzględne wymiary wyświetlanego trapezu? (Szerokość maksymalna wysokości, kątów)

I nie tylko dowiedzieć się wzór, który oblicza szerokość bez uwzględnienia perspektywy, więc wartość różni niektóre piksele (JavaScript):

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

Edycja: Oto spec o funkcji -webkit-Perspective

perspektywicznym (<numer>)

określa macierz rzutowania perspektywicznego. Ta macierz odwzorowuje sześcian widokowy na piramidę, której podstawa jest nieskończenie daleko od przeglądarki i której pik reprezentuje pozycję widza. Widoczny obszar jest obszarem ograniczonym czterema krawędziami okienka ekranu (część okna przeglądarki użyta do renderowania strony internetowej pomiędzy , pozycją widza i punktem w odległości nieskończoności od przeglądarki). Głębokość podana jako parametr funkcji reprezentuje odległość płaszczyzny z = 0 od przeglądarki. Niższe wartości dają bardziej spłaszczoną ostrosłup, a zatem bardziej wyraźny efekt perspektywy. Wartość jest podana w pikselach, więc wartość 1000 daje umiarkowaną wartość skrótu perspektywicznego, a wartość 200 daje ekstremalną wartość . Macierz obliczana jest na podstawie macierzy tożsamości i , zastępując wartość w wierszu 3, kolumnie 4 wartością -1/głębokość. Wartość głębokości musi być większa od zera, w przeciwnym razie funkcja jest nieprawidłowa .

chodzi o „macierz rzutowania perspektywicznego” to co znalazłem na Wikipedii: http://en.wikipedia.org/wiki/3D_projection#Perspective_projection

+2

To jest dobre pytanie - nigdy nie byłem całkiem jasny, jak dokładnie działa wartość perspektywy. –

+2

Dodałem oficjalną definicję funkcji perspektywy z W3C. Nadal nie wiem, jak to obliczyć. – Elias

+0

Mimo że uzyskałem stopień matematyki i wiedziałem trochę o tym, jak macierz odnosi się do algebry liniowej, nadal nie jestem pewien, jak z tego korzystać z grafiką 3D. Możesz wziąć opcję śmieci, jeśli znasz niektóre wymagania i zmierzyć szerokość z różnymi perspektywami, umieść je w arkuszu kalkulacyjnym, a następnie dopasuj do niego krzywą. Prawdopodobnie byłby szybszy niż pełne obliczenie i miałby wystarczającą dokładność (w przypadku pikselów musisz mimo wszystko zaokrąglić do najbliższej, więc błąd <0,5 nie robi różnicy) –

Odpowiedz

8

mam ból głowy z matryc, więc robię to z proporcji.

Jeśli widzisz div z góry (stąd widząc obrót w dwóch wymiarach to ma miejsce w), widzisz go jako segment w płaszczyźnie xz, ze współrzędnymi (-250, 0) (250, 0), albo w ogóle (-w/2, 0) (w/2, 0) po obrót na osi y, współrzędne staną się podobnie do tego, co stwierdzono

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

, będąc obrót w lewo, z pochodzenia na środku div, i angle radianach.

Korzystanie z perspektywy oznacza, że ​​współrzędne te nie są wyświetlane po prostu przez odrzucenie symbolu z, ale są one wyświetlane jako pierwsze zgodnie z odległością od obserwatora.

Płaszczyzna rzutowania jest teraz płaszczyzną, w której znajdują się niezabezpieczone elementy, z z = 0. Pochodzę z tego, że kiedy rzutowane są niezablokowane elementy div, pozostają one tej samej wielkości. Jeśli weźmiesz punkt z odległością p (wartość perspektywy) z płaszczyzny z, to z współrzędnymi xz (0, -p) i narysuj linię od tego punktu do wierzchołków obróconego segmentu, aż do momentu przekroczenia Plan projekcji, punkty, które otrzymujesz, to nowe współrzędne segmentu, które dają ostateczny rozmiar div.

Z proporcji między trójkątami (0, -p) (0, 0) (x, 0) i (0, -p) (0, sin*w/2) (cos*w/2, sin*w/2), to masz

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

co w ogóle oznacza, że ​​podczas projekcji punkt (x, y, z) na planie masz

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

Więc twój ostateczny Współrzędne div (na xz, w stosunku do centrum div) będą wynosić

(-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) 

Z którego można obliczyć jego szerokość, ale także jego położenie - co nie jest banalne, ponieważ jego najbliżej widoczna połowa będzie większa niż druga połowa.

Spójrz na poniższym teście więcej szczegółów (nie powiedzie się, gdy jesteś zbyt blisko obiektów, nie jestem pewien dlaczego, prawdopodobnie niektóre zmienna przepełnienia)

<!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> 

Należy pamiętać, że jeśli jesteś nie dając wartości perspektywy, wynik będzie równy jako mający dla niego wartość nieskończoną.

+0

Oczywiście jest to przeznaczone dla przeglądarek obsługujących właściwość -webkit-perspective, która w tej chwili to safari lub przeglądarki na komputerze Mac - tak myślę. W przeciwnym razie użyj po prostu p = nieskończoności, co oznacza zamianę wyrażenia zwrotu metody calc() na x, a zatem odrzucenie z (co nie ma znaczenia, gdy jesteś nieskończenie daleko). – djjeck

+0

Wow, to naprawdę niesamowite! To działa jak urok! Czy uważasz, że twój algorytm może być również przydatny do obliczania (wyświetlanej) wysokości i kąta obróconego elementu div? – Elias

+0

Pod kątem masz na myśli tę utworzoną przez rzutowany wielokąt, prawda? Możesz dostosować algorytm do pracy we wszystkich trzech wymiarach, a nie tylko w płaszczyźnie xz. Powinieneś znaleźć za pomocą funkcji kątowych cztery współrzędne prostokąta po obrocie, a następnie użyć współrzędnej * = p/(p + z), aby uzyskać współrzędne rzutowane. Wtedy masz już wielobok i możesz robić, co chcesz, dzięki temu =) – djjeck

Powiązane problemy