计算用css3透视旋转的div的绝对尺寸

Eli*_*ias 17 javascript css math css3 perspective

假设我们有一个500x500px大小的div,我们将它在x轴上通过css旋转45度,考虑到webkit-perspective值为1600px.

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

我只计算出一个计算宽度但不考虑透视的公式,所以值有所不同(JavaScript):

var absoluteWidth = Math.cos(45 * (Math.PI / 180)) * 500);
Run Code Online (Sandbox Code Playgroud)

编辑:这是关于-webkit-perspective函数的规范:

透视图(<编号>)

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

关于"透视投影矩阵",这是我在维基百科上发现的:http://en.wikipedia.org/wiki/3D_projection#Perspective_projection

djj*_*eck 8

我对矩阵很头疼,所以我用比例来做这件事.

如果从上面看到div(因此看到它发生在两个维度中的旋转),你会在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)
Run Code Online (Sandbox Code Playgroud)

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

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

现在,投影平面是未旋转的东西所在的平面,z = 0.我推断这是因为当投射未旋转的div时,它们保持相同的大小.如果p从z平面获取距离(透视值)的点,则使用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) / (p + sin*w/2)
Run Code Online (Sandbox Code Playgroud)

这通常意味着当你将点(x, y, z)投射到计划上时,你会得到

x * p / (p + z)
y * p / (p + z)
0
Run Code Online (Sandbox Code Playgroud)

所以你的最终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)
Run Code Online (Sandbox Code Playgroud)

你可以从中计算它的宽度以及它的位置 - 这是非常重要的,因为它最接近观察者的一半看起来比另一半更大.

请查看以下测试以获取更多详细信息(当您太靠近对象时它会失败,我不知道为什么,可能是某些变量溢出)

<!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>
Run Code Online (Sandbox Code Playgroud)

请注意,如果您没有给出透视值,则结果将等于具有无限值.