Search code examples
svgscaling

possible to use calc() in svg line?


I'm not sure if this is the best approach. I'm trying to make an SVG that has parts that scale and parts that are fixed. It looks like this:

enter image description here

When the web page loads, I don't know what the height of the container for it will be but I know the width. I want the joining lines to scale based on the height, but keep the box with the plus centered like this:

enter image description here I've played around with the line settings for x1, y1, etc., but I can't figure out a way to do it without resorting to javascript. Is SVG not the best option here? Here's what I have so far:

<svg class="s2">
        <line x1="50%" y1="0" x2="50%" y2="10%" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- top joining line -->

        <g id="square" x="50%" y="50%" width="16px" height="16px">
            <line x1="5" y1="8" x2="11" y2="8" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- plus horizontal line -->
            <line x1="8" y1="5" x2="8" y2="11" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- plus vertical line -->

            <rect x="4" y="4" width="8" height="8" style="fill:transparent;stroke:rgba(0,0,0,.5);"></rect>
        </g>

        <line x1="50%" y1="90%" x2="50%" y2="100%" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- bottom joining line -->

        <line x1="90%" y1="50%" x2="100%" y2="50%" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- right joining line -->
    </svg>

Would javascript be my only option here? I tried using values like

calc(50% - 5px) 

for the line positioning but it looks like it's not supported. If it was that would fix the problem.


Solution

  • For the solution, you have to combine two techniques:

    • masking parts of the lines, and
    • combine positioning in px with CSS translations in percentage

    You start by positioning your rect centered on the coordinate origin, giving sizes in pixels. The joining lines are first simply drawn without interruption from -50% to +50%. Then the parts behind your central rect are masked out, the sizing again in px.

    Finally, everything is moved by transform:translate(50%, 50&) to fill the SVG. It is important to note that this is the CSS transform property that can have units, while the SVG transform presentation attribute can only have unitless numbers. It therefore has to be written in a style attribute (or in a stylesheet).

    #outermost {
        transform:translate(50%, 50%);
    }
    g line {
        stroke:rgba(255,0,0,.9);
        stroke-width:1px;
    }
    g rect {
         fill:none;
         stroke:rgba(0,0,0,.5);
    }
    <svg xmlns="http://www.w3.org/2000/svg" class="s2" width="24" height="100">
        <mask id="cp">
            <rect x="-50%" y="-50%" width="100%" height="100%" fill="white"></rect>
            <rect x="-6" y="-6" width="12" height="12" fill="black"></rect>
        </mask>
        <g id="outermost">
            <g mask="url(#cp)">
                <line x1="0" y1="-50%" x2="0" y2="50%"></line>
                <line x1="0" y1="0" x2="50%" y2="0"></line>
            </g>
            <line x1="-3" y1="0" x2="3" y2="0"></line>
            <line x1="0" y1="-3" x2="0" y2="3"></line>
            <rect x="-4" y="-4" width="8" height="8"></rect>
        </g>
    </svg>