javascript - 在使用 Javascript 进行 CSS 3D 转换后获取 div 的真实 2D 顶点坐标

标签 javascript html css

这几天我一直在努力解决这个问题,但我似乎无法解决问题。

基本上,我有一些 div,其父级应用了 CSS 透视图和 rotateX 3D 转换,我需要获取这些 div 的实际屏幕坐标。

这是一个 jsfiddle,其中包含我的意思的示例(尽管无法正常工作)。

https://jsfiddle.net/6ev6d06z/3/

如您所见,顶点关闭(由于其父项的转换)

我试过

getBoundingClientRect()

但这似乎没有考虑 3D 变换。 我不知道是否有一个已经建立的方法来获得我需要的东西,但除此之外我想一定有一种使用 matrix3D 计算坐标的方法。

感谢任何帮助。

最佳答案

因为它没有内置的方法来获取转换元素的每个顶点的实际二维坐标。对于所有 API(例如 getBoundingClientRect),它们返回表示为 2 点矩形 [(top,left), (bottom,right)] 的转换元素的边界矩形。

也就是说,您完全可以通过一点点努力和矩阵数学来获得实际坐标。最简单的方法是使用预制矩阵库来进行数学运算(我对 math.js 有所了解,但还没有使用过),尽管您自己肯定可以做到。

在伪代码中你需要做什么:

  1. 获取文档坐标系中已转换父元素的未转换边界。
  2. 获取文档坐标系中目标元素的未转换边界。
  3. 计算目标相对于父级未转换边界的未转换边界。 A。从 (2) 的边界中减去 (1) 的顶部/左侧偏移量。
  4. 获取父元素的 css 转换。
  5. 获取父元素的变换原点(默认为 (50%, 50%))。
  6. 获取实际应用的变换(-origin * css transform * origin)
  7. 将 (3) 中的四个顶点乘以 (6) 中计算出的变换。
  8. 执行齐次除法(将 x、y、z 除以 w 分量)以应用透视。
  9. 将投影顶点转换回文档坐标系。
  10. 有趣!

然后在真正的代码中获得乐趣:https://jsfiddle.net/cLnmgvb3/1/

$(".target").on('click', function(){
    $(".vertex").remove();

    // Note: The 'parentOrigin' and 'rect' are computed relative to their offsetParent rather than in doc
    //       coordinates. You would need to change how these offsets are computed to make this work in a
    //       more complicated page. In particular, if txParent becomes the offsetParent of 'this', then the
    //       origin will be wrong.

    // (1) Get the untransformed bounds of the parent element. Here we only care about the relative offset
    //     of the parent element to its offsetParent rather than it's full bounding box. This is the origin
    //     that the target elements are relative to.
    var txParent = document.getElementById('transformed');

    var parentOrigin = [ txParent.offsetLeft, txParent.offsetTop, 0, 0 ];
    console.log('Parent Origin: ', parentOrigin);

    // (2) Get the untransformed bounding box of the target elements. This will be the box that is transformed.
    var rect = { left: this.offsetLeft, top: this.offsetTop, right: this.offsetLeft + this.offsetWidth, bottom: this.offsetTop + this.offsetHeight };

    // Create the vertices in the coordinate system of their offsetParent - in this case <body>.
    var vertices =
        [
            [ rect.left, rect.top, 0, 1 ],
            [ rect.right, rect.bottom, 0, 1 ],
            [ rect.right, rect.top, 0, 1 ],
            [ rect.left, rect.bottom, 0, 1 ]
        ];
    console.log('Original: ', vertices);

    // (3) Transform the vertices to be relative to transformed parent (the element with
    //     the CSS transform on it).
    var relVertices = [ [], [], [], [] ];
    for (var i = 0; i < 4; ++i)
    {
        relVertices[i][0] = vertices[i][0] - parentOrigin[0];
        relVertices[i][1] = vertices[i][1] - parentOrigin[1];
        relVertices[i][2] = vertices[i][2];
        relVertices[i][3] = vertices[i][3];
    }

    // (4) Get the CSS transform from the transformed parent
    var tx = getTransform(txParent);
    console.log('Transform: ', tx);

    // (5) Get the CSS transform origin from the transformed parent - default is '50% 50%'
    var txOrigin = getTransformOrigin(txParent);
    console.log('Transform Origin: ', txOrigin);

    // (6) Compute the full transform that is applied to the transformed parent (-origin * tx * origin)
    var fullTx = computeTransformMatrix(tx, txOrigin);
    console.log('Full Transform: ', fullTx);

    // (7) Transform the vertices from the target element's bounding box by the full transform
    var txVertices = [ ];
    for (var i = 0; i < 4; ++i)
    {
        txVertices[i] = transformVertex(fullTx, relVertices[i]);
    }

    console.log('Transformed: ', txVertices);

    // (8) Perform the homogeneous divide to apply perspective to the points (divide x,y,z by the w component).
    var projectedVertices = [ ];
    for (var i = 0; i < 4; ++i)
    {
        projectedVertices[i] = projectVertex(txVertices[i]);
    }

    console.log('Projected: ', projectedVertices);

    // (9) After the transformed vertices have been computed, transform them back into the coordinate
    // system of the offsetParent.
    var finalVertices = [ [], [], [], [] ];
    for (var i = 0; i < 4; ++i)
    {
        finalVertices[i][0] = projectedVertices[i][0] + parentOrigin[0];
        finalVertices[i][1] = projectedVertices[i][1] + parentOrigin[1];
        finalVertices[i][2] = projectedVertices[i][2];
        finalVertices[i][3] = projectedVertices[i][3];
    }

    // (10) And then add the vertex elements in the 'offsetParent' coordinate system (in this case again
    //      it is <body>).
    for (var i = 0; i < 4; ++i)
    {
       $("<div></div>").addClass("vertex")
          .css('position', 'absolute')
          .css('left', finalVertices[i][0])
          .css('top', finalVertices[i][1])
          .appendTo('body');
    }
  });

function printMatrix(mat)
{
    var str = '';
    for (var i = 0; i < 4; ++i)
    {
        for (var j = 0; j < 4; ++j)
        {
            str += (' ' + mat[i][j]);
        }

        str += '\r\n';
    }

    console.log(str);
}

function getTransform(ele)
{
    var st = window.getComputedStyle(ele, null);

    var tr = st.getPropertyValue("-webkit-transform") ||
             st.getPropertyValue("-moz-transform") ||
             st.getPropertyValue("-ms-transform") ||
             st.getPropertyValue("-o-transform") ||
             st.getPropertyValue("transform");

    var values = tr.split('(')[1],
    values = values.split(')')[0],
    values = values.split(',');

    var mat = [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ];    
    if (values.length === 16)
    {
        for (var i = 0; i < 4; ++i)
        {
            for (var j = 0; j < 4; ++j)
            {
                mat[j][i] = +values[i * 4 + j];
            }
        }
    }
    else
    {
        for (var i = 0; i < 3; ++i)
        {
            for (var j = 0; j < 2; ++j)
            {
                mat[j][i] = +values[i * 2 + j];
            }
        }
    }

    return mat;
}

function getTransformOrigin(ele)
{
    var st = window.getComputedStyle(ele, null);

    var tr = st.getPropertyValue("-webkit-transform-origin") ||
             st.getPropertyValue("-moz-transform-origin") ||
             st.getPropertyValue("-ms-transform-origin") ||
             st.getPropertyValue("-o-transform-origin") ||
             st.getPropertyValue("transform-origin");

    var values = tr.split(' ');

    var out = [ 0, 0, 0, 1 ];
    for (var i = 0; i < values.length; ++i)
    {
        out[i] = parseInt(values[i]);
    }    

    return out;
}

function createTranslateMatrix(x, y, z)
{
    var out = 
    [
        [1, 0, 0, x],
        [0, 1, 0, y],
        [0, 0, 1, z],
        [0, 0, 0, 1]
    ];

    return out;
}

function multiply(pre, post)
{
    var out = [ [], [], [], [] ];

    for (var i = 0; i < 4; ++i)
    {       
        for (var j = 0; j < 4; ++j)
        {
            var sum = 0;

            for (var k = 0; k < 4; ++k)
            {
                sum += (pre[k][i] * post[j][k]);
            }

            out[j][i] = sum;
        }
    }

    return out;
}

function computeTransformMatrix(tx, origin)
{
   var out;

   var preMul = createTranslateMatrix(-origin[0], -origin[1], -origin[2]);
   var postMul = createTranslateMatrix(origin[0], origin[1], origin[2]);

   var temp1 = multiply(preMul, tx);

   out = multiply(temp1, postMul);

   return out;
}

function transformVertex(mat, vert)
{
   var out = [ ];

    for (var i = 0; i < 4; ++i)
    {
        var sum = 0;
        for (var j = 0; j < 4; ++j)
        {
            sum += +mat[i][j] * vert[j];
        }

        out[i] = sum;
    }

   return out;
}

function projectVertex(vert)
{
    var out = [ ];

    for (var i = 0; i < 4; ++i)
    {
        out[i] = vert[i] / vert[3];
    }

    return out;
}

关于javascript - 在使用 Javascript 进行 CSS 3D 转换后获取 div 的真实 2D 顶点坐标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29187014/

相关文章:

html - CSS 帮助 : active button not changing style

html - Div 不在同一高度

javascript - 在本地文件上使用 Javascript/jQuery 时权限被拒绝

javascript - Video.js 重用 ID - destroy()

javascript - 单击时在文本框中设置日期

javascript - jquery-selectBox 在平板电脑和移动设备上不支持

css - 在 Adob​​e flex 4 中使用 Font Awesome

javascript - 在 css 中本地化图像路径

php - 如何编写零售调度程序

javascript - 在 Colorbox 中执行 JS(或其他 jQuery 插件)?