2011-12-27 7 views
17

cho phép nói rằng chúng tôi có div có kích thước 500x500px và xoay nó trên trục x qua 45 độ c xem xét giá trị góc nhìn của webkit 1600px.Tính toán kích thước tuyệt đối của div được xoay theo quan điểm với css3

Bạn sẽ tính kích thước tuyệt đối của hình thang được hiển thị như thế nào? (Chiều rộng, max-height, góc)

tôi đã chỉ tìm ra một công thức để tính toán chiều rộng nhưng mà không xem xét các quan điểm, vì vậy giá trị khác nhau một số pixel (JavaScript):

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

EDIT: đây là spec về chức năng -webkit-quan điểm:

quan điểm (< số >)

chỉ định ma trận chiếu phối cảnh. Ma trận này ánh xạ một hình lập phương xem lên một kim tự tháp có cơ sở vô cùng cách xa trình xem và có đỉnh đại diện cho vị trí của người xem. Vùng có thể xem được là vùng được bao quanh bởi bốn cạnh của khung nhìn (phần của cửa sổ trình duyệt được sử dụng để hiển thị trang web giữa vị trí của người xem và một điểm ở khoảng cách vô cùng từ trình xem ). Độ sâu, được đưa ra làm tham số cho hàm, đại diện cho khoảng cách của mặt phẳng z = 0 từ trình xem. Giá trị thấp hơn cung cấp cho một hình dạng kim tự tháp phẳng hơn và do đó một góc nhìn rõ rệt hơn là hiệu ứng . Giá trị được tính theo pixel, do đó, giá trị 1000 cho số tiền là vừa phải và giá trị 200 cho số tiền cực đại là . Ma trận được tính bằng cách bắt đầu với ma trận nhận dạng và thay thế giá trị tại hàng 3, cột 4 bằng giá trị -1/chiều sâu. Giá trị cho độ sâu phải lớn hơn 0, nếu không chức năng là không hợp lệ.

Về "ma trận chiếu quan điểm" đây là những gì tôi tìm thấy trên Wikipedia: http://en.wikipedia.org/wiki/3D_projection#Perspective_projection

+2

Đâ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. –

+2

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

+0

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

Trả lời

8

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

+0

Tất nhiên điều này là dành cho các trình duyệt có hỗ trợ thuộc tính -webkit-perspective, mà tại thời điểm này là safari hoặc trình duyệt trên Mac - tôi đoán vậy. Nếu không, chỉ cần sử dụng p = infinity, có nghĩa là thay thế biểu thức trả về của phương thức calc() bằng x, do đó loại bỏ z (không liên quan khi bạn ở xa vô hạn). – djjeck

+0

Chà, điều đó thực sự tuyệt vời! Nó hoạt động như một say mê! Bạn có nghĩ rằng thuật toán của bạn cũng có thể hữu ích để tính toán chiều cao (hiển thị) và góc của div được xoay không? – Elias

+0

Với góc bạn có nghĩa là hình được tạo bởi đa giác chiếu, phải không? Bạn có thể điều chỉnh thuật toán để làm việc trong cả ba chiều, và không chỉ trong mặt phẳng xz. Bạn nên tìm, theo các hàm góc, bốn tọa độ của hình chữ nhật sau khi xoay, và sau đó sử dụng phối hợp * = p/(p + z) để lấy tọa độ dự kiến. Sau đó, bạn có đa giác, và có thể làm những gì bạn muốn với điều đó =) – djjeck