Recently I’ve had a fascination with recursive SVG patterns: using multiple repetitions of a basic polygon inside itself to create complex designs. This requires precision alignment of scaled elements, which brings on another challenge: how to scale SVG elements around their centers.
The Fundamentals
Let’s start with the basic pattern unit of a hexagon, with all six points internally joined via a line to their opposite:
As this structure will be used repeatedly in the final design, it is created as a group inside the <defs>
section of the SVG, with a <use>
reference written to place the first iteration of the design:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 750 750">
<defs>
<g id="hex">
<polygon points="229.3,627.5 83.6,375.5 229.3,123.5 520.7,123.5 666.4,375.5 520.7,627.5" />
<line x1="229.3" y1="123.5" x2="520.7" y2="627.5"/>
<line x1="666" y1="375.5" x2="84" y2="375.5"/>
<line x1="520.7" y1="123.5" x2="229.3" y2="627.5"/>
</g>
</defs>
<use xlink:href="#hex" />
</svg>
The SVG is styled with CSS written inside the <defs>
:
polygon, line { fill: none; stroke: #32679D; stroke-width: 7px; }
Next, we want to create a copy of the pattern, but at half its current size, centered on the same point. But the line of code that you might use - a scaled <use>
element:
<use xlink:href="#hex" transform="scale(.5)" />
…doesn’t work in the way you might expect:
Unlike HTML elements in CSS transformations, SVG does not scale around its center by default; instead, it scales from the top left corner. To get around this problem, we can use a simple formula:
translate(-centerX * (factor - 1), -centerY * (factor - 1)) scale(factor)
We know that the this design is centered at the precise center of the SVG i.e.: 375 375
. Therefore, to scale a copy of the design by half on this center point, the complete formula would be:
translate(-375 * (.5 - 1), -375 * (factor - .5)) scale(.5)
Which resolves to:
<use xlink:href="#hex" transform="translate(187.5, 187.5) scale(.5)" />
Creating this result:
The remaining problem - at least for this design - is that the stroke
is scaled along with the design itself. Thankfully, from an earlier article, we know how to address that too: using non-scaling-stroke
in CSS, or as an SVG attribute:
polygon, line {
fill: none; stroke: #32679D; stroke-width: 7px;
vector-effect: non-scaling-stroke;
}
The result:
Once you have that, it’s fairly straightforward to move and scale further copies of the base pattern to create complex designs.
Variants
One of the advantages of using a base pattern in SVG is that any changes to the base unit will be reflected in its copies. That is, if I rotate the original pattern in the <defs>
:
<g id="hex" transform="rotate(30 375 375)">
All of the hexagons that derive from this base design will be rotated as a result, while retaining their individual translation and scale changes. You can see the effect of this in the example at the top of this article, and its associated CodePen.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/gwwJWd