Blink flattens every transform on an SVG child element to a 2D affine
matrix, so perspective(), rotateX()/rotateY()
foreshortening and translateZ() have no 3D effect inside
SVG, while the identical transform on an HTML element renders correctly.
Firefox already supports this (bugzilla 1478088); WebKit plans support with LBSE (webkit 242833).
Each case renders the same green 100×100 square once as an SVG
<rect> and once as an HTML <div>;
the HTML panel is the reference. The Expected tab shows the
captured HTML baseline, the Diff tab the pixel difference to it.
<svg style="perspective: 550px">
<rect style="transform: rotateY(50deg); transform-origin: 100px 100px" .../>
</svg>
This is the construction from the original 2017 report: the
perspective() function at the front of the element's own
transform stack.
<rect style="transform: perspective(550px) rotateY(50deg);
transform-origin: 100px 100px" .../>
With perspective 300px and z = -100px the square should shrink to 300 / (300 + 100) = 75% (75×75). Flattened SVG stays 100×100.
<svg style="perspective: 300px">
<rect style="transform: translateZ(-100px); transform-origin: 100px 100px" .../>
</svg>
Expected The SVG panel matches the HTML
panel in every case: perspective foreshortening on the rotated square
(near edge taller than far edge) and the 75% scale for
translateZ(-100px).
Actual (Chrome) The SVG square only shrinks
horizontally by cos(50°) with parallel edges (cases 1 and 2) and does
not scale at all for translateZ (case 3): the 3D components
are dropped when the transform is flattened to 2D affine.
// third_party/blink/renderer/core/layout/svg/transform_helper.cc
AffineTransform TransformHelper::ComputeTransform(...) {
...
// Flatten any 3D transform.
return AffineTransform::FromTransform(transform);
}
SVG children are painted through 2D AffineTransform paths, so
the perspective row and z-axis components of the computed
gfx::Transform are discarded before painting.
Patch
CL 8462268 implements this
behind the experimental runtime flag SvgCss3DTransforms:
the SVG child transform paint node gets the unflattened 3D matrix, the
svg root's perspective applies to its children, and hit
testing maps through the same paint-property-tree projection that
rendering uses.
With --enable-blink-features=SvgCss3DTransforms all three
cases render pixel-identical to the HTML reference and all hit-test
probes match the 3D projection; without the flag all fail.