【问题标题】:Calculate absolute dimensions of a div rotated in perspective with css3计算用css3透视旋转的div的绝对尺寸
【发布时间】:2020-01-14 07:35:56
【问题描述】:

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

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

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

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

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

透视(

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

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

【问题讨论】:

  • 这是一个很好的问题——我一直不太清楚透视值是如何工作的。
  • 我添加了来自 W3C 的透视函数的官方定义。仍然不确定如何计算。
  • 尽管获得了数学学位并且对矩阵与线性代数的关系有所了解,但我仍然不确定如何将其用于 3d 图形。如果你知道一些要求,你可以选择垃圾选项,用不同的视角测量宽度,把它放在电子表格中,然后手动拟合一条曲线。无论如何可能会比完整计算更快,并且有足够的精度(对于像素你必须四舍五入到最接近的像素,所以
  • 如果 div 具有固定大小但最终我需要支持可变大小,这将是一个解决方案。

标签: javascript math css perspective


【解决方案1】:

我对矩阵感到头疼,所以我用比例来做这个。

如果您从上方看到 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)

,逆时针旋转,原点在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) / (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)

您可以从中计算出它的宽度和位置——这很重要,因为它离观众最近的那一半看起来比另一半大。

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

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);
});
* {
  margin: 0px;
  padding: 0px;
}
body {
  padding: 40px 100px;
}
.the_div {
  height: 100px;
  border: 2px solid black;
  background-color: rgba(255, 192, 0, 0.5);
}
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"&gt;&lt;/script&gt;

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

【讨论】:

  • 当然,这适用于支持 -webkit-perspective 属性的浏览器,目前它要么是 safari,要么是 Mac 上的浏览​​器——我猜。否则,只需使用 p = infinity,这意味着将 calc() 方法的返回表达式替换为 x,因此丢弃 z(当您无限远时,这无关紧要)。
  • 哇,真是太棒了!它就像一个魅力!您认为您的算法对于计算旋转 div 的(显示的)高度和角度是否也有用?
  • 角度是指由投影多边形形成的角度,对吗?您可以调整算法以在所有三个维度上工作,而不仅仅是在平面 xz 中。您应该通过角度函数找到旋转后矩形的四个坐标,然后使用坐标 *=p/(p+z) 得到投影坐标。然后你就有了多边形,并且可以用它做你想做的事=)
  • 谢谢,*=p/(p+z) 实际上是破解它的隐藏拼图。再次感谢您的精彩回答!你摇滚!
  • 你是明星。这正是我需要的!
猜你喜欢
  • 2018-12-23
  • 2019-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多