2011-12-27 92 views
17

可以说我们有一个500x500px的div尺寸,我们将它在x轴上通过css 45度旋转,考虑webkit-perspective值为1600px。计算用css3旋转透视的div的绝对尺寸

如何计算显示的梯形的绝对尺寸? (宽度,最大高度,角度)

我确实只弄清楚,计算的宽度,但不考虑立体的公式,因此该值不同的一些像素(的JavaScript):

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

编辑:这里是关于-webkit视角函数的规格:

透视(<数>)

指定透视投影矩阵。该矩阵将观察立方体映射到金字塔上,金字塔的底部离观察者无限远,并且其峰值表示观察者的位置。可见的 区域是由视口的四个边缘(浏览器窗口的 部分用于呈现浏览器的位置的 和距离 查看器的无穷远处的点之间的网页部分)所限定的区域。作为函数的参数给出的深度表示z = 0平面距观察者的距离。较低的值给出了一个更平坦的金字塔 ,因此一个更明显的角度 的影响。该值以像素为单位给出,因此1000的值会产生 中等数量的缩短值,而200的值会产生极端的数量。该矩阵通过以单位矩阵开始并且用值-1 /深度替换第3行第4列的值来计算。深度值 必须大于零,否则函数 无效。

关于“透视投影矩阵”这是我在维基百科上找到:http://en.wikipedia.org/wiki/3D_projection#Perspective_projection

+2

这是一个很好的问题 - 我从未很清楚透视值的工作原理。 – 2011-12-27 22:15:25

+2

我添加了来自W3C的透视函数的官方定义。仍然不知道如何计算。 – Elias 2011-12-28 11:06:43

+0

尽管已经完成了数学学位并了解了矩阵如何与线性代数相关,但我仍然不确定如何在3D图形中使用它。如果您知道某些要求,则可以选择垃圾选项,并以不同的角度测量宽度,然后将其放入电子表格中,然后手动调整曲线。无论如何可能会比完整的计算更快,并且会有足够的准确性(对于像素来说,无论如何你都必须四舍五入到最接近的一个,所以小于0.5的误差并不会产生影响) – 2011-12-28 12:05:44

回答

8

我与矩阵头疼,所以我用的比例这样做。

如果你看到从上面(因此看到它发生在两个维度的旋转),你看到它在XZ平面段的股利,坐标(-250, 0) (250, 0),或一般(-w/2, 0) (w/2, 0) 后在y轴旋转,坐标将成为类似,你说什么

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

,作为逆时针旋转,与该div的中心为原点,和angle弧度。

使用透视意味着这些坐标不会仅仅通过丢弃z来显示,而是首先根据它们与观察者之间的距离来投影。

现在,投影平面是未旋转的东西所在的位置,z = 0。我从这样一个事实推断出这一点,即当未旋转的div投影时,它们保持相同的大小。 如果您从z平面上获取距离为p(透视值)的点,则用xz坐标(0,-p)绘制一条直线,并从该点到旋转线段的顶点绘制直线,直到它穿过投影计划,得到的点是产生div最终大小的新段坐标。

随着和(0, -p) (0, sin*w/2) (cos*w/2, sin*w/2)三角形之间的比例(0, -p) (0, 0) (x, 0),你得到

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

这一般意味着,当你的项目点(x, y, z)到计划你

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

因此,最终的div坐标(在xz上,相对于div的中心)将是

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

从中你可以计算出它的宽度,也可以计算它的位置 - 这是非平凡的,因为它最接近观察者的一半会比另一半大。

请看下面的测试更多细节(它,当你太靠近物体,我不知道为什么失败,可能是一些变量溢出)

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

请注意,如果你是没有给出一个视角值,结果将是相等的,因为它具有无限的价值。

+0

当然,这是为支持-webkit-perspective属性的浏览器而设计的,目前它是Safari上的浏览器或Mac上的浏览器 - 我猜。否则,只需使用p = infinity,这意味着用x替换calc()方法的返回表达式,因此丢弃z(当你无限远时,它是无关紧要的)。 – djjeck 2011-12-29 15:19:03

+0

哇,真的很棒!它像一个魅力!你认为你的算法对于计算旋转的div的(显示的)高度和角度也很有用吗? – Elias 2011-12-30 22:27:10

+0

有了角度,你的意思是由投影多边形组成的,对吗?您可以使算法适应所有三个维度,而不仅仅是xz平面。您应该通过角度函数找到旋转后矩形的四个坐标,然后使用坐标* = p /(p + z)获取投影坐标。然后你有多边形,并可以做你想要的=) – djjeck 2011-12-30 22:39:13