使用css3计算透视中旋转的div的绝对尺寸 [英] Calculate absolute dimensions of a div rotated in perspective with css3

查看:148
本文介绍了使用css3计算透视中旋转的div的绝对尺寸的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们有一个尺寸为500x500px的div,我们通过css 45度在x轴上旋转它,考虑webkit透视值为1600px。



你计算显示的梯形的绝对尺寸? (width,max-height,angles)



我只是计算出一个计算宽度但不考虑透视效果的公式, :

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






编辑:这里是关于-webkit -perspective function:


透视图(< number>)



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


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

解决方案

我对矩阵感到头痛,所以我用比例来做。 / p>

如果你看到从上面的div(因此看到它发生在两个维度的旋转),你会看到它在xz平面上的一个段,坐标( - 250,0)(250,0),或一般( - w / 2,0)(w /
在y轴上旋转后,坐标将变为

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

,逆时针旋转,原点位于div的中心,和 angle 弧度。



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



现在,投影平面是未旋转的物体所在的位置,z = 0。我推导出当未旋转的div被投影时,它们保持相同的大小。
如果从z平面获取距离 p (透视值)的点,因此使用xz坐标(0,-p)绘制线从这一点到旋转段的顶点,直到它穿过投影计划,你得到的点是新的段坐标,产生div的最终大小。



使用三角形(0,-p)(0,0)(x,0)(0,-p) 0,sin * w / 2)(cos * w / 2,sin * w / 2),你得到



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

这通常意味着当你投影点(x,y,z )到您获得的计划

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

,相对于div的中心)将是

 ( -  Math.cos(angle)* w / 2 * p / -math.sin(angle)* w / 2),0)
(Math.cos(angle)* w / 2 * p /(p + Math.sin(angle)* w /

从中可以计算其宽度,但也可以计算其位置 - 这是不平凡的,



查看下面的测试更多细节(当你太接近对象时,它会失败,我不知道为什么,可能有一些变量溢出)

 <!DOCTYPE html> 
< html>
< head>
< script type =text / javascriptsrc =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 =
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 id =fake/>').addClass('the_div'));

setInterval(function(){
angle + = 1;

$('#real')。css({'-webkit-transform':'rotateY('+ angle +'deg)'})html(width);

//初始坐标
var A = 0;
var B = width;
//翻译中心(假设-perspective-origin at 50%)
A - = width / 2;
B - = width / 2;
//新坐标
A = calc(A,angle * Math.PI / 180,p);
B = calc(B,angle * Math.PI / 180,p);
//翻译回
A + = width / 2;
B + = width / 2;
if(B var realwidth = B-A;
$('#fake')。html(width +'< br />'+ A +','+ B).css({
'width':realwidth +'px',
'margin-left':A +'px'
});

//显示调试信息
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);
}

函数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>

请注意,如果您不提供透视值,结果将等于具有无限值。


lets say we have a div with 500x500px size and we rotate it on the x axis via css 45 degrees considering a webkit-perspective value of 1600px.

How would you calculate the absolute dimensions of the displayed trapezium? (width, max-height, angles)

I did only figure out a formula that calculates the width but without considering the perspective, so the value differ some pixels (JavaScript):

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


EDIT: Here is the spec about the -webkit-perspective function:

perspective(<number>)

specifies a perspective projection matrix. This matrix maps a viewing cube onto a pyramid whose base is infinitely far away from the viewer and whose peak represents the viewer's position. The viewable area is the region bounded by the four edges of the viewport (the portion of the browser window used for rendering the webpage between the viewer's position and a point at a distance of infinity from the viewer). The depth, given as the parameter to the function, represents the distance of the z=0 plane from the viewer. Lower values give a more flattened pyramid and therefore a more pronounced perspective effect. The value is given in pixels, so a value of 1000 gives a moderate amount of foreshortening and a value of 200 gives an extreme amount. The matrix is computed by starting with an identity matrix and replacing the value at row 3, column 4 with the value -1/depth. The value for depth must be greater than zero, otherwise the function is invalid.

Regarding the "perspective projection matrix" this is what I found on Wikipedia: http://en.wikipedia.org/wiki/3D_projection#Perspective_projection

解决方案

I get a headache with matrices, so I'm doing this with proportions.

If you see the div from above (hence seeing the rotation in the two dimensions it takes place in), you're seeing it as a segment on the xz plane, with coordinates (-250, 0) (250, 0), or in general (-w/2, 0) (w/2, 0) After a rotation on the y axis, the coordinates will become, similarly to what you stated

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

, being the rotation counterclockwise, with the origin at the center of the div, and of angle radians.

Using the perspective means that these coordinates are not displayed just by discarding the z, but they are first projected according to their distance from the observer.

Now, the projection plane is the one where the unrotated things lay, with z = 0. I deduce this from the fact that when unrotated divs are projected, they remain the same size. If you take a point with distance p (the perspective value) from the z plane, so with xz coordinates (0, -p), and draw a line from this point to the vertices of the rotated segment, up to when it crosses the projection plan, the points you get are the new segment coordinates which yield the div final size.

With a proportion between the triangles (0, -p) (0, 0) (x, 0) and (0, -p) (0, sin*w/2) (cos*w/2, sin*w/2), you get that

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

which in general means that when you project the point (x, y, z) onto the plan you get

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

So your final div coordinates (on xz, relative to div's center) will be

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

From which you can calculate its width but also its position - which is non trivial, since its nearest-to-the-viewer half will appear bigger than the other half.

Look at the following test for more details (it fails when you're too close to the objects, I'm not sure why, probably some variable overflows)

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

Note that if you're not giving a perspective value, the result will be equal as having an infinite value for it.

这篇关于使用css3计算透视中旋转的div的绝对尺寸的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆