import { SVG } from "https://cdn.skypack.dev/@svgdotjs/svg.js"; function random(min, max, clamp = false) { const value = Math.random() * (max - min) + min; return clamp ? Math.round(value) : value; } class BauBudEyes {} class BauBudPatternGenerator { constructor(svg) { this.svg = svg; this.types = ["dots", "rects"]; } generate(fill) { this.type = this.types[random(0, this.types.length, true)]; return this.svg.pattern(4, 4, (add) => { if (this.type === "rects") { add.rect(2, 2).cx(0).cy(0).fill(fill); } else { add.circle(1).cx(2).cy(2).fill(fill); } }); } } class BauBudBody { constructor(svg, x, y, width, height) { this.svg = svg.group(); this.x = x; this.y = y; this.width = width; this.height = height; this.types = [ this.circle, this.rect, this.equilateralTriangle, this.rightAngleTriangle, this.semiCircle ]; this.patternGenerator = new BauBudPatternGenerator(this.svg); } circle() { this.type = "circle"; return this.svg .circle(Math.max(this.width, this.height)) .cx(this.x) .cy(this.height); } rect() { this.type = "rect"; return this.svg.rect(this.width, this.height).cx(this.x).cy(this.y); } equilateralTriangle() { this.type = "equilateralTriangle"; return this.svg .path( ` M ${this.x} ${this.y - this.height / 2} L ${this.x + this.width / 2} ${this.y + this.height / 2} L ${this.x - this.width / 2} ${this.y + this.height / 2} Z ` ) .rotate(this.randomRotation(180)); } rightAngleTriangle() { this.type = "rightAngleTriangle"; return this.svg .path( ` M ${this.x - this.width / 2} ${this.y - this.height / 2} L ${this.x + this.width / 2} ${this.y + this.height / 2} L ${this.x - this.width / 2} ${this.y + this.height / 2} Z ` ) .rotate(this.randomRotation(90)); } semiCircle() { this.type = "semiCircle"; const rad = Math.max(this.width, this.height) / 2; return this.svg .path( ` M ${this.x - rad} ${this.y + rad / 2} A ${rad} ${rad} 0 0 1 ${this.x + this.width / 2} ${this.y + rad / 2} Z ` ) .rotate(this.randomRotation(180)); } randomRotation(step = 90) { const n = 360 / step; return random(0, n, true) * step; } generate() { this.svg.clear(); this.fill = ["red", "yellow", "blue"][random(0, 2, true)]; const pattern = this.patternGenerator.generate("#000"); this.base = this.types[random(0, this.types.length - 1, true)] .call(this) .fill("none") .stroke("none"); let translateVal = random(5, 15); if (random(0, 1) > 0.5) translateVal *= -1; this.fill = this.base .clone() .addTo(this.svg) .fill(this.fill) .translate(translateVal, -translateVal); this.pattern = this.base .clone() .addTo(this.svg) .fill(pattern) .translate(-translateVal * 2, translateVal * 2) .scale(random(0.625, 0.825)); this.outline = this.base .clone() .addTo(this.svg) .stroke("black") .fill("transparent"); } } class BauBud { constructor(target) { this.svg = SVG().viewbox(0, 0, 200, 200).addTo(target); this.body = new BauBudBody(this.svg, 100, 100, 100, 100); this.eyes = new BauBudEyes(this.svg, 100, 100); } generate() { this.body.generate(); const eyeWidth = random(5, 20); const innerWidth = 8; this.svg .ellipse(16) .stroke("#000") .fill("#fff") .cx(100 - eyeWidth) .cy(100); this.svg .ellipse(16) .stroke("#000") .fill("#fff") .cx(100 + eyeWidth) .cy(100); const choice = random(0, 5, true); let leftEyeInnerX = 100 - eyeWidth; let rightEyeInnerX = 100 + eyeWidth; let eyeY = 100; switch (choice) { case 1: leftEyeInnerX -= 4; rightEyeInnerX -= 4; break; case 2: leftEyeInnerX += 4; rightEyeInnerX += 4; break; case 3: eyeY += 4; break; case 4: eyeY -= 4; break; default: leftEyeInnerX = leftEyeInnerX; rightEyeInnerX = rightEyeInnerX; } this.svg .ellipse(innerWidth) .stroke("#000") .fill("#000") .cx(leftEyeInnerX) .cy(eyeY); this.svg .ellipse(innerWidth) .stroke("#000") .fill("#000") .cx(rightEyeInnerX) .cy(eyeY); } } for (let i = 0; i < 100; i++) { const bauBud = new BauBud(document.querySelector(".buds")); bauBud.generate(); } const buds = document.querySelector(".buds"); setInterval(() => { const y = window.pageYOffset; const windowHeight = window.innerHeight; if (y + 200 + windowHeight >= buds.scrollHeight) { for (let i = 0; i < 100; i++) { const bauBud = new BauBud(buds); bauBud.generate(); } } }, 100);