class Wiggler { constructor(canvas, target) { console.log("start init") this.canvas = canvas; this.ctx = canvas.getContext("2d"); this.width = this.canvas.width; this.height = this.canvas.height; let maxx = Math.max(...target.flatMap(l => l.map(k => k[0]))); let maxy = Math.max(...target.flatMap(l => l.map(k => k[1]))) let offsx = (this.width - maxx) / 2 let offsy = (this.height - maxy) / 2 this.target = target.map(c => c.map(l => [l[0] + offsx, l[1] + offsy])); // this.initRandom(); // this.initCorners(); this.current = this.initEdges(maxx, maxy); } initRandom() { return this.target.map(l => { const px = Math.random() * this.width; const py = Math.random() * this.height; const r = Math.PI * 2; const tx = px + Math.cos(r) * 10; const ty = py + Math.sin(r) * 10; return [[px, py], [tx, ty]]; }); } initCorners() { return this.target.map(l => { const px = Math.random() < .5 ? 0 : this.width; const py = Math.random() < .5 ? 0 : this.height; const r = Math.PI * 2; const tx = px + Math.cos(r) * 10; const ty = py + Math.sin(r) * 10; return [[px, py], [tx, ty]]; }); } initEdges() { return this.target.map(() => { var px, py; if (Math.random() < .5) { px = Math.random() < .5 ? 0 : this.width; py = Math.random() * this.height; } else { px = Math.random() * this.width; py = Math.random() < .5 ? 0 : this.height; } return [[px, py], [px, py]]; }); } start() { console.log("start"); if (!this.running) { this.running = true; this.animate(); } } stop() { console.log("stop"); if (this.running) { this.running = false; } } toggle() { this.running = !this.running; if (this.running) { this.animate(); } else { console.log("stop"); } } animate() { if (this.running) { this.update(); this.draw(); window.requestAnimationFrame(() => { this.animate(); }) } } dist(p1, p2) { const dx = p1[0] - p2[0]; const dy = p1[1] - p2[1]; return Math.sqrt(dx * dx + dy * dy); } mv(d) { return Math.random() * d - (d / 2.0); } update() { var changed = false; for (var l = 0; l < this.current.length; l++) { for (var p = 0; p < 2; p++) { const cp = this.current[l][p]; const tp = this.target[l][p]; if (this.dist(cp, tp) > .5) { changed = true; const dist = this.dist(cp, tp); var np; var ndist; do { const dx = this.mv(Math.sqrt(dist) + 2); const dy = this.mv(Math.sqrt(dist) + 2); np = [cp[0] + dx, cp[1] + dy]; ndist = this.dist(np, tp); } while (ndist > dist); this.current[l][p] = np; } } } if (!changed) { this.running = false; } } draw() { this.fillStyle = "white"; this.ctx.clearRect(0, 0, 800, 600); this.ctx.strokeStyle = "black"; this.current.forEach(l => { this.ctx.beginPath(); this.ctx.moveTo(l[0][0], l[0][1]); this.ctx.lineTo(l[1][0], l[1][1]); this.ctx.stroke(); }); } mash() { // replace target lines randomly for (let i = 0; i < this.target.length; i++) { let c0, c1; do { c0 = i c1 = Math.floor(Math.random() * this.target.length) } while (c0 == c1); let l0 = this.target[c0][0] let l1 = this.target[c0][1] this.target[c0][0] = this.target[c1][0] this.target[c0][1] = this.target[c1][1] this.target[c1][0] = l0 this.target[c1][1] = l1 } if (!this.running) { this.running = true; this.animate(); } } } Array.__prototype const textToCoords = (text, scale, alphabet) => { var base = 0; var output = Array.from(text).flatMap(c => { const g = alphabet[c].map(l => l.map(p => [p[0] * scale + base, p[1] * scale]) ); const maxx = alphabet[c].map(l => Math.max(l[0][0], l[1][0])); // width of character plus const inc = (Math.max(1, Math.max(...maxx)) + 0.2) * scale; base += inc; return g; }); return output; }; document.addEventListener('DOMContentLoaded', () => { console.log("kleines büro ready ..."); const canvas = document.getElementById("banner"); const target = textToCoords("KLEINES BÜRO", 20, alphabet); const wiggler = new Wiggler(canvas, target); wiggler.start(); canvas.addEventListener("click", () => { wiggler.mash(); }); new Splide('.splide', { padding: '5rem', }).mount(); });