SVG - how to center a rectangle? - svg

I have a simple SVG with two rectangles. I want the "inner" rectangle to be exactly in the middle of the SVG. By setting x and y attributes to 50% the upper left corner is centred. Instead, I want to center the middle of the rectangle. I've tried setting transform-origin to center but it doesn't work.
<svg width="100" height="100">
<rect width="100%" height="100%" fill="gold" />
<rect width="30" height="30" x="50%" y="50%" fill="green" />
</svg>
How to achieve such functionality without manually specifying x and y attributes?

The explanation of the code:
The x and y coordinates of a rectangle represent the position of the upper left corner. So if you give your rectangle x="50" y="50" this will put the upper left corner of the rectangle in the middle of the SVG canvas. To center the rectangle you need to offset it with half width or height: 50 - (30/2) = 35. The solution is <rect width="30" height="30" x="35" y="35" fill="green" />
<svg width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" fill="gold" />
<rect width="30" height="30" x="35" y="35" fill="green" />
</svg>
update:
The op is commenting:
I would actually prefer to set 50% for x and y instead of doing some math
In this case you may need to translate your rect, but you still need some math in order to know how much to translate:
<svg width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" fill="gold" />
<rect width="30" height="30" x="50%" y="50%" transform="translate(-15,-15)" fill="green" />
</svg>
<svg width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" fill="gold" />
<rect width="30" height="30" x="-15" y="-15" transform="translate(50,50)" fill="green" />
</svg>
Yet an other solution would be using a polygon or a path with the center in the origin:
<svg width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" fill="gold" />
<polygon points="-15,-15 15,-15 15,15 -15,15" transform="translate(50,50)" fill="green" />
</svg>

Here is an alternative that may work in some cases:
<svg width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" fill="gold" />
<rect width="100%" height="100%" transform-origin="50% 50%" transform="scale(0.3)" fill="green" />
</svg>

You can also do the calculation without the transform attribute.
<svg width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" fill="gold" />
<rect width="30" height="30" x="calc(50% - 15)" y="calc(50% - 15)" fill="green" />
</svg>

Related

How do I stop svg GaussianBlur from being clipped? [duplicate]

Why does the grid in this SVG not fill the entire 256x256 space? No matter what size I change it to, about 15% of the grid is cut off, which appears to me to be arbitrary in the context of my code.
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="grid" width="18.75" height="18.75" patternUnits="userSpaceOnUse">
<path d="M 18.75 0 L 0 0 0 18.75" fill="none" stroke="black" stroke-width="1"/>
</pattern>
<rect id="gridRect" width="100%" height="100%" fill="url(#grid)" />
<filter id="gridify" width="100%" height="100%" filterUnits = "userSpaceOnUse">
<feImage result="sourceTwo" xlink:href="#gridRect" />
<feComposite in="SourceGraphic" in2="sourceTwo" operator="in"/>
</filter>
</defs>
<g filter="url(#gridify)" >
<rect width="100%" height="100%" fill="url(#linGradient)" />
</g>
<rect width="100%" height="100%" fill="none" stroke="black" stroke-width="1"/>
</svg>
The SVG specification defaults filters to being 10% larger than the filtered object by default.
If ‘x’ or ‘y’ is not specified, the effect is as if a value of -10% were specified.
If ‘width’ or ‘height’ is not specified, the effect is as if a value of 120% were specified.
I imagine that's to stop lots of questions along the lines of "why is my Gaussian blur based drop-shadow filter cut off?"
So, looks like all I needed to do to fix it was add an x="0" and a y="0" to the filter. I don't understand why it is necessary though, as it does not make sense that it would default to "-15%".
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="grid" width="18.75" height="18.75" patternUnits="userSpaceOnUse">
<path d="M 18.75 0 L 0 0 0 18.75" fill="none" stroke="black" stroke-width="1"/>
</pattern>
<rect id="gridRect" width="100%" height="100%" fill="url(#grid)" />
<filter id="gridify" x="0" y="0" width="100%" height="100%" filterUnits = "userSpaceOnUse">
<feImage result="sourceTwo" xlink:href="#gridRect" />
<feComposite in="SourceGraphic" in2="sourceTwo" operator="in"/>
</filter>
</defs>
<g filter="url(#gridify)" >
<rect width="100%" height="100%" fill="url(#linGradient)" />
</g>
<rect width="100%" height="100%" fill="none" stroke="black" stroke-width="1"/>
</svg>

how to set clipPath as a pattern using svg?

I created the pattern, then gave it to the circle inside the . clipPath I set to the image, but the pattern was not set for the image. How can I set a mask as a pattern for an image?
I was expecting to see a mask for the image in the form of a created pattern
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="600">
<pattern id="cube" x="0" y="10" width="20" height="20" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="10" height="10" />
<rect x="10" y="10" width="10" height="10" />
</pattern>
<clipPath id="msk1">
<circle fill="url(#cube)" cx="50%" cy="50%" width="100%" height="100%" r="200" />
</clipPath>
<image xlink:href="wave.jpg" height="100%" clip-path="url(#msk1)"/>
</svg>
If you want a mask, then use a <mask>, not a <clipPath>, which as its name implies will create a clipping area from a path. What you want is for the pixels to create the mask, and that's what a <mask> does.
svg { max-height: 100vh }
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 600">
<pattern fill="white" id="cube" x="0" y="10" width="20" height="20" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="10" height="10" />
<rect x="10" y="10" width="10" height="10" />
</pattern>
<mask id="msk1">
<circle fill="url(#cube)" cx="50%" cy="50%" width="100%" height="100%" r="200" />
</mask>
<image xlink:href="https://picsum.photos/400/400" height="100%" mask="url(#msk1)"/>
</svg>
(Note that I did set the rectangles of the pattern white, we could also have drawn a full white rectangle behind the black ones for the same effect).

(SVG) Transform origin of grouped path element is not getting applied

I want to define grouped shapes in the <def> section and display them via the <use> tag with the transformation-origin being the group's center. Following is a minimal example illustrating my issue:
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
<defs>
<g id="triangle">
<path fill="green" d="m 24 0 l 24 41.569 l -48 0 z" />
<circle cx="24" cy="24" r="1" fill="red" />
<rect width="48" height="41.569" fill="purple" fill-opacity="20%" />
</g>
</defs>
<rect width="100" height="100" />
<rect fill="gray" x="100" width="100" height="100" />
<rect width="100" height="100" />
<rect fill="gray" y="100" width="100" height="100" />
<rect width="100" height="100" x="100" y="100" />
<use href="#triangle" transform-origin="50% 50%" transform="translate(100 100)" />
</svg>
As it is my understanding, the triangle should be centered in the background. But in actuality, the top left corner of the group is centered.
I have tested my example against the MDN transform-origin one. The MDN one works well in the same environment (Edge Browser & VSCode). This lets me believe I am missing some side effects of the used tags or attributes
Observed behavior
Expected behavior
A scale transform has a centre point i.e. a point that doesn't change position.
So does a rotation - there's a centre of rotation.
A translate transform does not, every location moves. There's therefore no origin so transform-origin does nothing.

In nested SVG, inner icon is getting truncated

My challenge is to take two random svg icons and splice them together such that the inner one is located on the lower left quadrant of the overall image. Thanks to a previous answer I have a framework for doing this but the inner icon is getting truncated unless I make manual changes. I need to do this programmatically, so the question becomes, how do I know how large to make my B viewport, algorithmically speaking?
If this is Icon A:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" style="fill:rgb(255,0,0);stroke-width:3; stroke:rgb(0,0,0)"
</svg>
This is a minimal but fairly accurate example of an Icon B:
<svg height="512pt" viewBox="-51 0 512 512.00253" width="512pt" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" style="fill:rgb(0,0,255);stroke-width:30; stroke:rgb(0,0,0)" />
</svg>
This is the framework for putting them together:
<svg viewBox="0 0 100 100">
<defs>
<symbol id="A" viewBox="0 0 100 100"> <!-- making viewbox of symbol A
match viewbox on contained
svg element, which works -->
%ENTIRE CONTENTS OF SVG A%
</symbol>
<symbol id="B" viewBox="51 0 512 512"> <!-- making viewbox of symbol B
match viewbox on contained
svg element, which truncates -->
%ENTIRE CONTENTS OF SVG B%
</symbol>
</defs>
<use xlink:href="#A" x="0" y="0" width="100" height="100" />
<use xlink:href="#B" x="0" y="50" width="50%" height="50%" />
</svg>
This is the current svg:
<svg viewBox="0 0 100 100">
<defs>
<symbol id="A" viewBox="0 0 100 100">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" style="fill:rgb(255,0,0);stroke-width:3; stroke:rgb(0,0,0)"
</svg>
</symbol>
<symbol id="B" viewBox="51 0 512 512">
<svg height="512pt" viewBox="-51 0 512 512.00253" width="512pt" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" style="fill:rgb(0,0,255);stroke-width:30; stroke:rgb(0,0,0)" />
</svg>
</symbol>
</defs>
<use xlink:href="#A" x="0" y="0" width="100" height="100" />
<use xlink:href="#B" x="0" y="50" width="50%" height="50%" />
</svg>
Here's what that looks like. Note the truncation; that inner icon is getting chomped on the right and bottom.
I can manually tweak it like this, with the results below. But I can't do manual tweaking on these.
<svg viewBox="0 0 100 100">
<defs>
<symbol id="A" viewBox="0 0 100 100">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" style="fill:rgb(255,0,0);stroke-width:3; stroke:rgb(0,0,0)"
</svg>
</symbol>
<!-- Note this pair of 680 values, and the 0 x-pos on the line below -->
<symbol id="B" viewBox="0 0 680 680">
<svg height="512pt" viewBox="0 0 512 512.00253" width="512pt" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" style="fill:rgb(0,0,255);stroke-width:30; stroke:rgb(0,0,0)" />
</svg>
</symbol>
</defs>
<use xlink:href="#A" x="0" y="0" width="100" height="100" />
<use xlink:href="#B" x="0" y="50" width="50%" height="50%" />
</svg>
Note the tweaked "680" h/w on the symbol B viewbox. (And also tweaking the viewbox on the inner svg to shift the X position to 0, which I'm really unhappy doing). That "680" number was determined experimentally, which would be fine only if this was a one-time deal and not something I need to make systematic. How do I know how big to make that viewbox programmatically?
Remove the width and the height attributes from the inner SVG
svg{width:90vh}
<svg viewBox="0 0 100 100">
<defs>
<symbol id="A" viewBox="0 0 100 100">
<svg viewBox="0 0 100 100">
<rect width="100%" height="100%" style="fill:rgb(255,0,0);stroke-width:3; stroke:rgb(0,0,0)" />
</svg>
</symbol>
<symbol id="B" viewBox="0 0 512 512">
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" style="fill:rgb(0,0,255);stroke-width:30; stroke:rgb(0,0,0)" />
</svg>
</symbol>
</defs>
<use xlink:href="#A" x="0" y="0" width="100" height="100" />
<use xlink:href="#B" x="0" y="50" width="50" height="50" />
</svg>
UPDATE
The OP comments that they can't remove the width and the height attributes from the inner SVG. In this case I need to add a few lines of JavaScript. First I need to get the size of the second SVG canvas in px.
const pt = 96/72;
let size = 512 * pt;
Also I need to know the stroke width
let strokeWidth = 30 * pt;
Next I need to reset the value for the viewBox attribute for the `#B``
const pt = 96/72;
let size = 512 * pt;
let strokeWidth = 30 * pt;
B.setAttributeNS(null,"viewBox", `-${strokeWidth/2} -${strokeWidth/2} ${size+strokeWidth} ${size+strokeWidth}`)
<svg viewBox="0 0 100 100">
<defs>
<symbol id="A" viewBox="0 0 100 100">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100" viewBox="0 0 100 100">
<rect width="100%" height="100%" style="fill:rgb(255,0,0);stroke-width:3; stroke:rgb(0,0,0)" />
</svg>
</symbol>
<symbol id="B" viewBox="0 0 512 512">
<svg height="512pt" viewBox="0 0 512 512" width="512pt" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" style="fill:rgb(0,0,255);stroke-width:30; stroke:rgb(0,0,0)" />
</svg>
</symbol>
</defs>
<use xlink:href="#A" x="0" y="0" width="100" height="100" />
<use xlink:href="#B" x="0" y="50" width="50%" height="50%" />
</svg>

How can i scale a shape without scaling its pattern?

I have an svg shape which uses a pattern. I want the pattern to NOT scale when i scale the shape.
Here's a fiddle with a minimal example, the bigger circle should show the pattern like the smaller one:
http://jsfiddle.net/cTMrQ/6/
<svg style="position: absolute" width="100%" height="100%" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<defs>
<pattern id="checkerPattern" patternUnits="userSpaceOnUse" x="0" y="0" width="4" height="4">
<image x="0" y="0" xlink:href="http://inwonderland.at/new/lines.png" width="4" height="4" />
</pattern>
<circle fill="url(#checkerPattern)" id="c" cx="50" cy="50" r="50" />
</defs>
<use x="100" y="100" xlink:href="#c" />
<use x="200" y="100" xlink:href="#c" transform="scale(2)" />
</svg>
In the end the shape will be a complex path and the image in the pattern will be a scan of a piece of paper, so just drawing a bigger circle instead of scaling it won't work.
Update
To clarify what i want, here are two images:
this is what it looks like, no matter what i try, when i scale the shape:
http://inwonderland.at/new/ihave.png
this is what i want:
http://inwonderland.at/new/iwant.png
i want the background image (bitmap image) to always have its natural size.
You can't get what you want using a pattern, the transform always happens after the fill, and you can't just move the pattern fill into a wrapper either. My suggestion is to use a filter and apply the filter on a wrapper - like so:
<svg style="position: absolute" width="100%" height="100%" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<defs>
<circle fill="url(#checkerPattern)" id="c1" cx="50" cy="50" r="50" />
<filter id="linepattern" x="0%" y="0%" height="100%" width="100%">
<feImage xlink:href="http://inwonderland.at/new/lines.png" result="pattern" width="4" height="4"/>
<feTile/>
<feComposite operator="in" in2="SourceGraphic"/>
</filter>
</defs>
<use filter="url(#linepattern)" x="100" y="100" xlink:href="#c1" />
<use filter="url(#linepattern)" x="200" y="100" xlink:href="#c1" transform="scale(2)" />
<g filter="url(#linepattern)">
<use x="50" y="100" xlink:href="#c1" transform="scale(2)" />
</g>
</svg>
Using viewport
1:1 no zoom
<svg width="800" height="400" viewBox="0 0 800 400">
2:1 zoom double size
<svg width="800" height="400" viewBox="0 0 400 200">
The following elements can use the viewBox attribute
<svg>
<symbol>
<image>
<marker>
<pattern>
<view>
viewbox is fully animatable; and you can zoom into any center point.
<animate attributeName="viewBox" begin="1s" dur="1s"
values="0 0 600 400; 250 180 300 200" fill="freeze" />
Transform a parent tag
Yes an SVG can be a child element but more commonly shapes made with multible tags are placed inside a group tag.
Transform scale can be used with tags which are parents IE the group tag.
<g transform="scale(1.5)">
/* draw your shape inside the g tag */
<use x="100" y="100" xlink:href="#c" />
<use x="200" y="100" xlink:href="#c" />
</g>
So using your above example scale the shape in a parent tag.
Update
To scale image but not patterns in other words move patterns, or icons, on background image that scales.
<g transform="scale(2)">
/* draw your shape inside the g tag */
<use x="100" y="100" xlink:href="#c" transform="scale(.5)" />
<use x="200" y="100" xlink:href="#c" transform="scale(.5)"/>
</g>
Update full svg
I had to move things around a bit, One full size, (lets call it a map), with an overlay of 1 half size map in the upper left corner. setting the full screen to render between 0 and max of 600. Setting a viewport the same but with the width set to 300 scales it down. I do need to double the radius for this example of scaling.
<svg viewBox="0 0 600 600" style="position: absolute" width="100%" height="100%" version="1.1" baseProfile="full"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<defs>
<pattern id="checkerPattern" patternUnits="userSpaceOnUse" x="0" y="0" width="4" height="4">
<image x="0" y="0" xlink:href="http://inwonderland.at/new/lines.png" width="4" height="4" />
</pattern>
<circle fill="url(#checkerPattern)" id="c" cx="50" cy="50" r="50" />
<circle fill="url(#checkerPattern)" id="c2" cx="50" cy="50" r="100" />
</defs>
<use x="100" y="100" xlink:href="#c" transform="scale(.5)"/>
<use x="200" y="100" xlink:href="#c" transform="scale(1)"/>
<rect width="600" height="600" style="fill: none; stroke: black;" />
<svg viewBox="0 0 600 600" width="300" height="300" x="300">
<use x="100" y="100" xlink:href="#c2" transform="scale(.5)"/>
<use x="200" y="100" xlink:href="#c2" transform="scale(1)"/>
<rect width="600" height="600" style="fill: none; stroke: black;" />
</svg>
</svg>
This example is scaled using the same circle pattern. The radius does not need to be changed here because the location is not in the tag being scaled. I'm making use of svg tags here but other tags can be used.
<svg viewBox="0 0 600 600" style="position: absolute" width="100%" height="100%" version="1.1" baseProfile="full"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<defs>
<pattern id="checkerPattern" patternUnits="userSpaceOnUse" x="0" y="0" width="4" height="4">
<image x="0" y="0" xlink:href="http://inwonderland.at/new/lines.png" width="4" height="4" />
</pattern>
<circle fill="url(#checkerPattern)" id="c" r="50" cx="50" cy="50" />
</defs>
<svg x="100" y="100"><use xlink:href="#c" transform="scale(.5)"/></svg>
<svg x="200" y="100"><use xlink:href="#c" transform="scale(1)"/></svg>
<rect width="600" height="600" style="fill: none; stroke: black;" />
<svg viewBox="0 0 600 600" width="300" height="300" x="300">
<svg x="100" y="100"><use xlink:href="#c" transform="scale(1)"/></svg>
<svg x="200" y="100"><use xlink:href="#c" transform="scale(2)"/></svg>
<rect width="600" height="600" style="fill: none; stroke: black;" />
</svg>
</svg>

Resources