Подтвердить что ты не робот

Вычислить абсолютные размеры div, повернутые в перспективе с помощью css3

позволяет сказать, что у нас есть div с размером 500x500px, и мы поворачиваем его по оси x через css 45 градусов с учетом значения webkit-перспективы 1600px.

Как бы вы вычислили абсолютные размеры отображаемой трапеции? (ширина, максимальная высота, углы)

Я только вычислил формулу, которая вычисляет ширину, но не рассматривая перспективу, поэтому значение отличается несколькими пикселями (JavaScript):

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

EDIT: Вот спецификация функции -webkit-perspective:

перспектива (< число > )

указывает перспективную матрицу прогноза. Эта матрица отображает куб обзора на пирамиду, основание которой бесконечно удалено от зритель и чей пик представляет позицию зрителя. Видимая область - это область, ограниченная четырьмя краями окна просмотра ( часть окна браузера, используемого для отображения веб-страницы между положение зрителя и точка на расстоянии бесконечности от Зритель). Глубина, заданная как параметр функции, представляет собой расстояние от плоскости z = 0 от зрителя. Более низкие значения дают более сплюснутая пирамида и, следовательно, более выраженная перспектива эффект. Значение задается в пикселях, поэтому значение 1000 дает умеренное количество ракурса и значение 200 дает экстремальное значение количество. Матрица вычисляется, начиная с единичной матрицы и заменяя значение в строке 3, столбец 4 со значением -1/depth. значение для глубины должно быть больше нуля, в противном случае функция недействителен.

Что касается "проекционной матрицы перспективы", то это то, что я нашел в Википедии: http://en.wikipedia.org/wiki/3D_projection#Perspective_projection

4b9b3361

Ответ 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>

Обратите внимание, что если вы не даете перспективного значения, результат будет равен бесконечному значению для него.