Home > Enterprise >  SVG group to child transforms
SVG group to child transforms

Time:09-21

I have a group that has 2 or more SVG elements. Group has a SVG matrix applied and the same apply for children.

Each element, including group can have any transformations applied through the matrix (rotate, skew, scale etc). Example of SVG with the group and 2 inner elements:

<g transform="matrix(0.679039, -0.734102, 0.734102, 0.679039, -21.7964, 262.861)">
    <path transform="matrix(1, 0, 1.63636, 1, 82.9909, 49.1)" d="M92.35,45.6 A92.35,22.8 0 1 1 92.35016 45.6"></path>
    <rect width="225.1" height="63.3" transform="matrix(0.742794, -0.66952, 0.66952, 0.742794, 242.258, 216.595)" rx="0"></rect>
</g>

I would like to do an ungroup function that removes the <g> element and place inner elements outside of g, but keeps all matrix transformations from group and their own transformations.

Tried multiple methods but they all have different issues where inner elements are not placed in the correct position or transformations are not copied.

The following seems to do something, but works for rotation only, if skew is applied - elements are totally different.

matrixMultiply(...matrices: DOMMatrix[]) : DOMMatrix {
    let args = arguments, i = args.length, m = args[i-1];

    while(i-- > 1) {
        let m1 = args[i-1] as Matrix;
        m = m1.multiply(m);
    }

    return m;
}

let groupMatrix = groupElement.transform.baseVal.getItem(0).matrix;
groupElement.childNodes.forEach(child => {
    let childTransform = (child as SVGGraphicsElement).transform.baseVal.getItem(0),
    childMatrix = childTransform.matrix,
    childInverseMatrix = childMatrix.inverse();
    let gm = matrixMultiply( childInverseMatrix, groupMatrix, childMatrix );

    childTransform.setMatrix(gm);
});

CodePudding user response:

You are making your life much too complicated. All you need to know is that a sequence of transform attributes, read in the direction of parent to child, has the same result as a sequence of transforms written left to right in one transform attribute:

<g transform="skewX(...)">
  <path transform="matrix(...)" d="..." />
</g>

<!-- does the same transformation as -->

<path transform="skewX(...) matrix(...)" d="..." />

That simplifies your code to the following. If you want, you can compute the new transform value yourself by initializing a DOMMatrix from the transform strings. But why would you? The browser will do it for you anyway.

let groupTransform = groupElement.getAttribute('transform');
groupElement.childNodes.forEach(child => {
    let childTransform = child.getAttribute('transform');

    // not really needed...
    let gm = new DOMMatrix(`${groupTransform} ${childTransform}`);
    child.setAttribute('transform', gm.toString());

   //...this would suffice
   child.setAttribute('transform', `${groupTransform} ${childTransform}`);
});
  • Related