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ą.
To jest dobre pytanie - nigdy nie byłem całkiem jasny, jak dokładnie działa wartość perspektywy. –
Dodałem oficjalną definicję funkcji perspektywy z W3C. Nadal nie wiem, jak to obliczyć. – Elias
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) –