var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 400;
var particles = [];
function Particle() {
this.position = {
actual : {
x : 0,
y : 0
},
affected : {
x : 0,
y : 0
},
};
}
var gridSize = 25;
var columns = canvas.width / gridSize;
var rows = canvas.height / gridSize;
for (var i = 0; i < rows+1; i++) {
for (var j = 0; j < canvas.width; j+=2) {
var p = new Particle();
p.position.actual.x = j;
p.position.actual.y = i * gridSize;
p.position.affected = Object.create(p.position.actual);
particles.push(p);
}
}
for (var i = 0; i < columns+1; i++) {
for (var j = 0; j < canvas.height; j+=2) {
var p = new Particle();
p.position.actual.x = i * gridSize;
p.position.actual.y = j;
p.position.affected = Object.create(p.position.actual);
particles.push(p);
}
}
var mouse = {
x : -100,
y : -100,
};
var effectRadius = 75;
var twirlAngle = 90;
function draw(e) {
context.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(function (particle) {
particle.position.affected = Object.create(particle.position.actual);
var a = mouse.y - particle.position.actual.y;
var b = mouse.x - particle.position.actual.x;
var dist = Math.sqrt(a*a + b*b);
if (dist < effectRadius) {
var a = angle(mouse.x, mouse.y, particle.position.actual.x, particle.position.actual.y);
var strength = twirlAngle - easeOutQuad(dist, 0, twirlAngle, effectRadius);
a += strength;
var p = rotate(a, dist, mouse.x, mouse.y);
particle.position.affected.x = p.x;
particle.position.affected.y = p.y;
}
context.beginPath();
context.rect(particle.position.affected.x-1, particle.position.affected.y-1, 2, 2);
context.fillStyle = "black";
context.fill();
});
}
draw();
window.addEventListener("mousemove", function (e) {
mouse.x = e.x - canvas.offsetLeft;
mouse.y = e.y - canvas.offsetTop;
requestAnimationFrame(draw);
});
function easeOutQuad(t, b, c, d) {
t /= d;
return -c * t*(t-2) + b;
};
function angle(originX, originY, targetX, targetY) {
var dx = targetX - originX;
var dy = targetY - originY;
var theta = Math.atan2(dy, dx) * (180 / Math.PI);
if (theta < 0) theta = 360 + theta;
return theta;
}
Number.prototype.map = function (in_min, in_max, out_min, out_max) {
return (this - in_min) / (in_max - in_min) * (out_max - out_min) + out_min;
};
function pos(x, y, angle, length) {
angle *= Math.PI / 180;
return {
x : Math.round(x + length * Math.cos(angle)),
y : Math.round(y + length * Math.sin(angle)),
};
}
function rotate(angle, distance, originX, originY) {
return {
x : originX + Math.cos(angle * Math.PI/180) * distance,
y : originY + Math.sin(angle * Math.PI/180) * distance,
}
}
* {
margin: 0;
padding: 0;
box-sizing: inherit;
line-height: inherit;
font-size: inherit;
font-family: inherit;
}
body {
font-family: sans-serif;
box-sizing: border-box;
background-color: hsl(0, 0%, 90%);
}
canvas {
display: block;
background: white;
box-shadow: 0 0 2px rgba(0, 0, 0, .2), 0 1px 1px rgba(0, 0, 0, .1);
margin: 20px auto;
}
<canvas id="canvas"></canvas>