Making sense of CSS matrices

A small example to calculate cumulative transformations on nested elements and the conditions that this holds true, like in SVG

by Ibrahim Tanyalcin

HTML

<div id="x">
  <div id="y">
    
  </div>
</div>

<div id="u">U</div>
<div id="w">W</div>

CSS

* {
  margin: 0px;
  box-sizing: border-box;
  transform-origin: 0px 0px;
}

#x {
  position: relative;
  background: red;
  width: 300px;
  height: 300px;
  transform: translate(-20px, -20px) rotate(-30deg) translate(150px, 100px) rotate(20deg);
}

#y {
  position: relative;
  background: blue;
  width: 100px;
  height: 100px;
  transform: rotate(-5deg) rotate(10deg) translate(200px, 50px) rotate(75deg) translate(35px, 10px) rotate(-25deg);
}

#u {
  position: absolute;
  top: 0px;
  width: 100px;
  height: 100px;
  background: magenta;
  opacity: 0.5;
}

#w {
  position: absolute;
  top: 0px;
  width: 100px;
  height: 100px;
  background: yellow;
  opacity: 0.5;
}

JavaScript

/*THIS EXAMPLE WORKS IF THE MECHANISM OF TRANSFORMATION MATRICES IN CSS DOM IS CLOSE ENOUGH TO SVG, WHICH MEANS LIKE IN SVG, ROTATE SHOULD NOT BE AROUND AN ELEMENTS CENTER (default) BUT TO THE TOP LEFT CORNER OF THE ELEMENT. IN SVG, ALL TRANFORMATIONS ARE RELATIVE TO THE TOP LEFT CORNER OF THE VIEWBOX WHICH CANNOT BE 100% TRANSPORTED TO CSS. THE CLOSEST I COULD FIND WAS TO SET TRANSFORM-ORIGIN: 0px 0px WHICH SEEMS TO WORK */

/*There is red parent square and blue child square inside which both have cascading transformations. The goal is to combine their transformation matrices and make yellow (W) and magenta (U) squares superimpose with the blue square*/

//get all the elements in variables
const [elX, elY, elU, elW] = [...document.getElementsByTagName("div")];
//make a regexp to extract the entries of the matrix, with match, it returns [a, b, c, d, e, f]
const rgx = /(-?[0-9]+(?:\.[0-9]+)?)/gi;
//DOMMatrices expect an array of 6 numbers for 2D transformations
//get the matrix of big red parent square
const matrix_elX = new DOMMatrix(getComputedStyle(elX).transform.match(rgx));
//get the matrix of the small blue child square
const matrix_elY = new DOMMatrix(getComputedStyle(elY).transform.match(rgx));
/*
'result' is MX * MY and 'result2' is MY * MX
magenta square U uses result,
yellow square W uses result2,
magenta square U correctly superimposes with blue square,
but yellow square W does NOT,
therefore MX * MY and MY * MX are not the same, as you can expect that matrice multiplication is NOT commutative
Below uses DOMMatrix.fromMatrix is create clones of the matrices because operations does mutate the matrix itself.
*/
let result = DOMMatrix.fromMatrix(matrix_elY).preMultiplySelf(DOMMatrix.fromMatrix(matrix_elX)),
		result2 = DOMMatrix.fromMatrix(matrix_elX).preMultiplySelf(DOMMatrix.fromMatrix(matrix_elY));
elU.style.transform = result;
elW.style.transform = result2;