Verticon
Verticon

Reputation: 2513

How can I get Firefox to honor the transform origin of an SVG rect being used as a clip path?

I have a situation wherein I am scaling an SVG rectangle that is being used to clip an SVG image. I am encountering a Firefox only issue (works as expected in Chrome and Safari; haven't tested with Edge): Firefox is not honoring the rect's transform-origin css property.

I have put together a snippet that exposes the issue. There are two examples.

Example 1 draws a rect over the top of an image. The propose of Example 1 is to demonstrate that Firefox is capable of correctly responding to the value of a rect's transform-origin property (As well as correctly reporting the rect's bounding box. Did I mention the Bounding Box?). Example 1 works as expected in all three browsers.

Example 2 actually demonstrates the issue by using a rect as a clip path. Example 2 also shows that the bounding box is not properly reported by Firefox.

Can anyone help me to get the scaled rect to transform correctly? In my actual project I am setting the transform-origin to center.

BTW: When you run the snippet, please expand it to full page.

            window.onload = function() {
                setupExample('example1')
                setupExample('example2')
            }

            function setupExample(name) {

                let rect = $(`svg.${name} rect`)
                let scaledCheckBox = $(`div.${name} input[type=checkbox]`)
                let transformOriginFieldset = $(`div.${name} > fieldset`)


                function setTransform() {
                    if (scaledCheckBox[0].checked) {
                        rect.attr('transform', 'scale(1.5)')
                    }
                    else {
                        rect.removeAttr('transform')
                    }
                }
                scaledCheckBox.on('change', setTransform)
                setTransform();


                function setTransformOrigin() {
                    let transformOrigin = document.querySelector(`div.${name} input[name=${name}TransformOrigin]:checked`).value
                    rect.css('transform-origin', transformOrigin)
                }
                transformOriginFieldset.on('change', setTransformOrigin)
                setTransformOrigin()


                let reportButton = $(`div.${name} > input[type=button]`)
                reportButton.on('click', () => {
                    let bb = rect[0].getBBox()
                    alert(`Rect Bounding Box: ${bb.width} x ${bb.height} at ${bb.x}, ${bb.y}`)
                })
            }
        body {
            background-color: black;
        }

        h1 {
            position: fixed;
            left: 50%;
            translate: -50%;

            font: 25px Arial, sans-serif;
            color: white;
            text-align: center;
        }
        h1.example2 {
            top: 50vh;
        }

        h2 {
            font: 18px Arial, sans-serif;
            color: white;
            text-align: center;
            text-decoration: underline;
        }

        svg {
            position: fixed;
            left: 50%;
            translate: -50%;

            width: 200px;
            height: 200px;

            border: 1px solid white;
        }
        svg.example1 {
            top: 10vh;
        }
        svg.example2 {
            top: 60vh;
        }

        rect {
            fill: none;
            stroke: red;
            stroke-width: 20px;
            transform-box: fill-box;
        }

        div {
            display: flex;
            flex-direction: column;
            gap: 5px;
            align-items: center;

            position: fixed;
            left: 10vw;

            color: white;
            background-color: gray;
            border: 1px solid black;
            border-radius: 5px;

            padding: 8px;
        }
        div.example1 {
            top: 10vh;
        }
        div.example2 {
            top: 60vh;
        }
    <head>
        <title>Firefox Problem: transform-origin and getBBox()</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    </head>


    <body>

        <h1>Example 1: Rect Overlaying Image</h1>

        <svg class="example1" viewBox="0 0 2119 2191" preserveAspectRatio="none">
            <image href="https://learn.robertvaessen.com/media/mandala.jpg"/>
            <rect x="530" y="547" width="1060" height="1095"/>
        </svg>

        <div class="example1">
            <h2>Overlay Setup</h2>

            <fieldset>
                <legend align="center">Transform Origin</legend>

                <label>
                    Center
                    <input type="radio" name="example1TransformOrigin" value="center" checked/>
                </label>

                <label>
                    Top Left
                    <input type="radio" name="example1TransformOrigin" value="top left"/>
                </label>
            </fieldset>

            <label>
                Scaled
                <input type="checkbox"/>
            </label>

            <input type="button" value="Report"/>
        </div>


        <h1 class="example2" >Example 2: Rect Clipping Image</h1>

        <svg class="example2" viewBox="0 0 2119 2191" preserveAspectRatio="none">
            <clipPath id="rect">
                <rect x="530" y="547" width="1060" height="1095"/>
            </clipPath>
            <image href="https://learn.robertvaessen.com/media/mandala.jpg" clip-path="url(#rect)"></image>
        </svg>

        <div class="example2" >
            <h2>Clipping Setup</h2>

            <fieldset>
                <legend align="center">Transform Origin</legend>

                <label>
                    Center
                    <input type="radio" name="example2TransformOrigin" value="center" checked/>
                </label>

                <label>
                    Top Left
                    <input type="radio" name="example2TransformOrigin" value="top left"/>
                </label>
            </fieldset>

            <label>
                Scaled
                <input type="checkbox"/>
            </label>

            <input type="button" value="Report"/>
        </div>

    </body>

Upvotes: 1

Views: 80

Answers (1)

LS_
LS_

Reputation: 7129

If I got the issue correctly, it seems you can fix it by applying:

transform-box: view-box;

To the rect element.

Upvotes: 1

Related Questions