In pattern making, a motif is an arrangement of two or more elements. This demo's motif will be a series of text rotated around a point. The <defs> and <use> elements will compose a motif from a single <text> element.
The <defs> element contains svg elements without rendering them. The example below defines a <text> element inside a <defs> element. Elements in <defs> can be referenced by an id.
consthelloDefs=svg`
<defs>
<text id="chars">Hello defs!</text>
</defs>
`;
To reuse the <text> element previously defined in <defs>, set the href property on a <use> element to #chars like the example below.
consthelloDefs=svg`
<defs>
<text id="chars">Hello defs!</text>
</defs>
<use href="#chars"></use>
`;
A key feature that makes the combination of <defs> and <use> so powerful is the fact that attributes and properties applied to <use> do not affect the referenced element in <defs>. This allows developers to compound properties like transform.
In the example below, the <text> element in <defs> has no rotation. However, we can apply rotation to <use> without affecting the layout of the <text> element in <defs>.
consthelloDefs=svg`
<defs>
<text id="chars">Hello defs!</text>
</defs>
<use
href="#chars"
transform="rotate(180, 0,0)">
</use>
`;
Lastly, <g> is a special element that applies its properties to all child elements. This is helpful when we need to distribute a property to a number of elements.
In the example below, the <g> element will apply its transform to every child element. In this case, the <use> element will both rotateandtranslate.
Provide an id to the text element in createElement.
constcreateElement= (
chars: string,
): SVGTemplateResult=>svg`
<text
id="chars"
dominant-basline="hanging"
font-family="monospace"
font-size="24px">
${chars}
</text>`;
constcreateElement= (chars) =>svg`
<text
id="chars"
dominant-basline="hanging"
font-family="monospace"
font-size="24px">
${chars}
</text>`;
Call createElement inside <defs> in render.
render() {
returnhtml`
<svg>
<defs>
${createElement(this.chars)}
</defs>
...
</svg>
`;
}
Create a function called createMotif that generates a series of rotated texts. createMotif could take two arguments, numPrints which is the number of times the element will be printed, and an optional offset property which is the initial rotation offset. createMotif should calculate the rotation given the numPrints to print, and apply the rotation to a <use> element that references #chars.
constcreateMotif= (
numPrints: number,
offset: number=0
): SVGTemplateResult=> {
constrotation=360/numPrints;
constprints= [];
letcurrRotation=offset;
for (letindex=0; index<numPrints; index++) {
currRotation+=rotation;
prints.push(svg`
<use
href="#chars"
transform="rotate(${currRotation}, 0, 0)">
</use>
`);
}
};
constcreateMotif= (numPrints, offset=0) => {
constrotation=360/numPrints;
constprints= [];
letcurrRotation=offset;
for (letindex=0; index<numPrints; index++) {
currRotation+=rotation;
prints.push(svg`
<use
href="#chars"
transform="rotate(${currRotation}, 0, 0)">
</use>
`);
}
};
Wrap the prints list in a <g> element. Move the group down and to the right 50px to keep the entire motif in frame by setting the transform property on <g> to translate(50, 50). This applies the transform in <g> to all prints. Return this group.
Lit's reactive properties can also be set via attributes. Try changing the chars, rotation-offset, and num-prints attributes on the <repeat-pattern> HTML element in index.html!