Ответ 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. Я выводил это из того факта, что при проецировании невращенных divs они остаются того же размера.
Если вы берете точку с расстоянием 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)
что в общем случае означает, что при проецировании точки (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>
Обратите внимание, что если вы не даете перспективного значения, результат будет равен бесконечному значению для него.