tôi nhận được một cơn đau đầu với ma trận, vì vậy tôi đang làm điều này với tỷ lệ.
Nếu bạn nhìn thấy div từ trên cao (do đó nhìn thấy vòng quay ở hai chiều nó diễn ra), bạn sẽ thấy nó như một đoạn trên mặt phẳng xz, với tọa độ (-250, 0) (250, 0)
hoặc nói chung (-w/2, 0) (w/2, 0)
Sau xoay trên trục y, tọa độ sẽ trở thành, tương tự như những gì bạn nói
(-Math.cos(angle) * w/2, -Math.sin(angle) * w/2)
(Math.cos(angle) * w/2, Math.sin(angle) * w/2)
, là xoay ngược chiều kim đồng, với nguồn gốc ở trung tâm của div, và angle
radian.
Sử dụng phối cảnh có nghĩa là các toạ độ này không được hiển thị chỉ bằng cách loại bỏ z, nhưng chúng được chiếu lần đầu tiên theo khoảng cách của chúng từ người quan sát.
Bây giờ, mặt phẳng chiếu là mặt phẳng nằm ở vị trí không xoay, với z = 0. Tôi suy ra điều này từ thực tế là khi các div chưa được bảo vệ được chiếu, chúng vẫn giữ nguyên kích thước. Nếu bạn lấy một điểm với khoảng cách p
(giá trị phối cảnh) từ mặt phẳng z, vì vậy với tọa độ xz (0, -p) và vẽ một đường từ điểm này đến đỉnh của đoạn xoay, đến khi nó vượt qua kế hoạch chiếu, các điểm bạn nhận được là các tọa độ phân khúc mới mang lại kích thước cuối cùng div.
Với một tỷ lệ giữa tam giác (0, -p) (0, 0) (x, 0)
và (0, -p) (0, sin*w/2) (cos*w/2, sin*w/2)
, bạn nhận được rằng
p : x = (p + sin*w/2) : cos*w/2
x = (p * cos*w/2)/(p + sin*w/2)
mà nói chung có nghĩa là khi bạn dự án điểm (x, y, z)
lên kế hoạch bạn sẽ có được
x * p/(p + z)
y * p/(p + z)
0
Vì vậy cuối cùng của bạn tọa độ div (trên xz, so với trung tâm của div) sẽ là
(-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)
Từ đó bạn có thể tính toán chiều rộng của nó nhưng cũng là vị trí của nó - đó là không tầm thường, vì nửa gần nhất với người xem sẽ xuất hiện lớn hơn nửa còn lại.
Nhìn vào thử nghiệm sau để biết thêm chi tiết (nó không thành công khi bạn đang quá gần với các đối tượng, tôi không chắc chắn lý do tại sao, có lẽ một số tràn biến)
<!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>
Lưu ý rằng nếu bạn không đưa ra giá trị phối cảnh, kết quả sẽ bằng với giá trị vô hạn cho nó.
Đây là câu hỏi hay - tôi chưa bao giờ rõ ràng về cách giá trị phối cảnh hoạt động. –
Tôi đã thêm định nghĩa chính thức cho chức năng phối cảnh từ W3C. Vẫn không chắc chắn làm thế nào để tính toán điều đó. – Elias
Mặc dù đã thực hiện một mức độ Toán và biết một chút về cách ma trận liên quan đến đại số tuyến tính, tôi vẫn không chắc chắn làm thế nào để sử dụng điều này với đồ họa 3D. Bạn có thể sử dụng tùy chọn rác nếu bạn biết một số yêu cầu và đo chiều rộng với các góc nhìn khác nhau, đặt nó vào một bảng tính sau đó theo cách thủ công một đường cong với nó. Có thể sẽ nhanh hơn bất kỳ tính toán đầy đủ nào, và sẽ có đủ độ chính xác (đối với pixel bạn phải làm tròn gần nhất, vì vậy lỗi <0.5 không tạo nên sự khác biệt) –