我对矩阵感到头疼,所以我用比例来做这个。
如果您从上方看到 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);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
请注意,如果您没有给出透视值,则结果将等于它具有无限值。