const mouse = { x: 0, y: 0, }; window.addEventListener('mousemove', function(e){ mouse.x = e.x; mouse.y = e.y; }); let canvas; let ctx; let flowfield; let flowfieldAnimation = null; function startFlowfieldAnimation() { if (flowfieldAnimation != null) cancelAnimationFrame(flowfieldAnimation); // canvas.width = window.innerWidth; // canvas.height = window.innerHeight; canvas.width = 300; canvas.height = 400; flowfield = new FlowfieldEffect(ctx, canvas.width, canvas.height); flowfield.animate(0); } window.addEventListener('resize', function(e){ // startFlowfieldAnimation(); }); window.onload = function(){ canvas = document.getElementById('canvas1'); ctx = canvas.getContext('2d'); startFlowfieldAnimation(); } class FlowfieldEffect { #ctx; #width; #height; #count; #radius; constructor(ctx, width, height, gradient) { this.#ctx = ctx; this.#ctx.lineWidth = 0.3; this.#width = width; this.#height = height; this.gradient; this.#createGradient(); this.#ctx.strokeStyle = this.gradient; this.#radius = 1.9; // 1.9 this.vr = 0.03; this.cellSize = 10; this.interval = 1000/60; this.timer = 0; this.lastTime = 0; this.a = Math.random() * 4 - 2; this.b = Math.random() * 4 - 2; this.c = Math.random() * 4 - 2; this.d = Math.random() * 4 - 2; } #createGradient(){ this.gradient = this.#ctx.createLinearGradient(0, 0,this.#width, this.#height); this.gradient.addColorStop("0.1","#ff5c33"); this.gradient.addColorStop("0.2", "#ff66b3"); this.gradient.addColorStop("0.4", "#ccccff"); this.gradient.addColorStop("0.6", "#b3ffff"); this.gradient.addColorStop("0.8", "#80ff80"); this.gradient.addColorStop("0.9", "#ffff33"); } #getValue(x, y){ return (Math.cos(mouse.y/3 * x * 0.00005) + Math.sin(mouse.x/3 * y * 0.00005)) * this.#radius; } #drawLine(x1,y1,x2,y2) { this.#ctx.beginPath(); this.#ctx.moveTo(x1,y1); this.#ctx.lineTo(x2,y2); this.#ctx.stroke(); } #draw(angle,x,y){ let positionX = x; let positionY = y; let dx = mouse.x - positionX; let dy = mouse.y - positionY; let distance = (dx * dx + dy * dy); let length = distance > 150000 ? distance : 150000; if (length > 900000) length = 900000; this.#drawLine(positionX,positionY, positionX+Math.cos(angle) * length/10000, positionY+Math.sin(angle) * length/10000); } cliffordAttractor(x, y, a, b, c, d) { // clifford attractor // http://paulbourke.net/fractals/clifford/ // scale down x and y let scale = 0.005; let x0 = (x - this.#width / 2) * scale; let y0 = (y - this.#height / 2) * scale; // attactor gives new x, y for old one. let x1 = Math.sin(a * y0) + c * Math.cos(a * x0); let y1 = Math.sin(b * x0) + d * Math.cos(b * y0); // find angle from old to new. that's the value. return Math.atan2(y1 - y0, x1 - x0); } // http://paulbourke.net/fractals/henonmap/ henonAttractor(x,y,a,b){ let scale = 100000; let x0 = x*scale; let y0 = y*scale; let x1 = 1 + y - a*x*x; let y1 = b*x; return Math.atan2(y1 - y0, x1 - x0); } // http://paulbourke.net/fractals/henonmap/ thornFractal(x,y,a,b){ let scale = 1; let c1 = a; 0.102 let c2 = b; -0.04 let x0 = x*scale; let y0 = y*scale; let x1 = x0/Math.cos(y0)+c1; let y1 = y0/Math.sin(x0)+c2; return Math.atan2(y1 - y0, x1 - x0); } getValue(x,y){ //return (x + y) * 0.01 * Math.PI * 2; //return (x + y) * 0.001 * Math.PI * 2; //return (Math.sin(x * 0.01) + Math.sin(y * 0.0001)) * Math.PI * 2; return this.cliffordAttractor(x,y,this.cellSize,this.b,this.c,this.d); //return this.henonAttractor(x, y, this.a, this.b); //return this.thornFractal(x, y, this.a, this.b); } drawMethod0() { let rayon = this.cellSize / 2; for (let y = 0; y < this.#height; y += this.cellSize){ for (let x = 0; x < this.#width; x += this.cellSize){ let angle = this.getValue(x,y); let x0 = x + rayon; let y0 = y + rayon; let x1 = x0 + Math.cos(angle)*rayon; let y1 = y0 + Math.sin(angle)*rayon; let x2 = x0 + Math.cos(angle+Math.PI)*rayon*2; let y2 = y0 + Math.sin(angle+Math.PI)*rayon*2; this.#drawLine(x1, y1, x2, y2); } } } drawMethod1() { this.#radius += this.vr; if (this.#radius > 5 || this.#radius < -5) this.vr *= -1 for (let y = 0; y < this.#height; y += this.cellSize){ for (let x = 0; x < this.#width; x += this.cellSize){ const angle = this.#getValue(x, y); this.#draw(angle,x,y); } } } animate(timeStamp){ let deltaTime = timeStamp - this.lastTime; this.lastTime = timeStamp; this.drawMethod = this.drawMethod0; this.a = Math.random() * 4 - 2; this.b = Math.random() * 4 - 2; this.c = Math.random() * 4 - 2; this.d = Math.random() * 4 - 2; if (this.timer > this.interval){ this.#ctx.clearRect(0, 0, this.#width, this.#height); this.drawMethod(); this.timer = 0; } else { this.timer += deltaTime; } flowfieldAnimation = requestAnimationFrame(this.animate.bind(this)); } }