<!--
Demo for this article:
http://blustemy.io/making-svg-patterns-with-javascript/
-->
<svg id="illustration"
width="768" height="340" viewBox="0 0 768 340"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol id="triangle" viewBox="0 0 100 100">
<polygon points="0,100 100,100 50,0"/>
</symbol>
<rect width="768" height="340" fill="#1098ad"/>
</svg>
<p>
<button type="button" class="button-redraw">Redraw</button>
<button type="button" class="button-clear">Clear</button>
<a href="#" class="link-download">Download generated SVG</a>
</p>
/*
Demo for this article:
http://blustemy.io/making-svg-patterns-with-javascript/
*/
svg {
/* Make sure browsers don't draw anything outside the <svg> element */
overflow: hidden;
}
/*
Demo for this article:
http://blustemy.io/making-svg-patterns-with-javascript/
*/
class Patterns {
constructor({ svg, symbol }) {
this.svgElement = svg;
this.symbolElement = symbol;
this.symbolLink = `#${symbol.id}`;
}
static get svgNamespace() {
return "http://www.w3.org/2000/svg";
}
static get xlinkNamespace() {
return "http://www.w3.org/1999/xlink";
}
/* Return a random integer between min (included) and max (excluded). */
static getRandomInt(min, max) {
const minInt = Math.ceil(min),
maxInt = Math.floor(max);
return Math.floor(Math.random() * (maxInt - minInt)) + minInt;
}
/* Return a random number between min (inclusive) and max (exclusive). */
static getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
addRandomSymbol(parentNode, fill = "#000000", opacity = 0.5) {
const useElement = document.createElementNS(Patterns.svgNamespace, "use"),
width = this.symbolElement.viewBox.baseVal.width,
height = this.symbolElement.viewBox.baseVal.height,
x = Patterns.getRandomArbitrary(0, this.svgElement.viewBox.baseVal.width),
y = Patterns.getRandomArbitrary(0, this.svgElement.viewBox.baseVal.height),
angle = Patterns.getRandomArbitrary(0, 360),
scale = Patterns.getRandomArbitrary(1, 4);
useElement.setAttributeNS(Patterns.xlinkNamespace, "xlink:href", this.symbolLink);
useElement.setAttribute("width", `${width}`);
useElement.setAttribute("height", `${height}`);
useElement.setAttribute("fill", fill);
useElement.setAttribute("fill-opacity", `${opacity}`);
useElement.setAttribute("transform", `translate(${x} ${y}) scale(${scale}) rotate(${angle})`);
parentNode.appendChild(useElement);
}
drawRandomSymbols(n = 10, fill = "#000000", opacity = 0.5) {
// Prepare a group element containing all the symbol instances.
const gElement = document.createElementNS(Patterns.svgNamespace, "g");
for (let i = 0; i < n; i++) {
this.addRandomSymbol(gElement, fill, opacity);
}
gElement.classList.add("patterns");
// Add the group.
this.svgElement.appendChild(gElement);
}
clearSymbols() {
const gElements = this.svgElement.querySelectorAll("g.patterns");
for (let i = 0; i < gElements.length; i++) {
this.svgElement.removeChild(gElements[i]);
}
}
get svgString() {
return this.svgElement.outerHTML;
}
exportAsSvgFile(clickedLinkElement) {
const blob = new Blob([this.svgString], {
type: "image/svg+xml"
}),
url = window.URL.createObjectURL(blob);
clickedLinkElement.target = "_blank";
clickedLinkElement.download = "Patterns.svg";
clickedLinkElement.href = url;
}
}
document.addEventListener("DOMContentLoaded", () => {
const patterns = new Patterns({
svg: document.getElementById("illustration"),
symbol: document.getElementById("triangle")
});
patterns.drawRandomSymbols(40, "#000000", 0.15);
patterns.drawRandomSymbols(30, "#ffffff", 0.4);
document.querySelector(".button-redraw").addEventListener("click", () => {
patterns.clearSymbols();
patterns.drawRandomSymbols(40, "#000000", 0.15);
patterns.drawRandomSymbols(30, "#ffffff", 0.4);
});
document.querySelector(".button-clear").addEventListener("click", () => {
patterns.clearSymbols();
});
document.querySelector(".link-download").addEventListener("click", (evt) => {
patterns.exportAsSvgFile(evt.target);
});
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.