|
|
|
|
|
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));
|
|
|
}
|
|
|
}
|