152 lines
4.1 KiB
JavaScript
152 lines
4.1 KiB
JavaScript
|
const canvas = document.querySelector("canvas");
|
||
|
const context = canvas.getContext("2d");
|
||
|
const img = document.querySelector("img");
|
||
|
|
||
|
let built = false;
|
||
|
let pixels = [];
|
||
|
const config = {
|
||
|
strength: 2000,
|
||
|
easeFactor: 0.1,
|
||
|
glow: true,
|
||
|
stats: false,
|
||
|
pxSize: 15
|
||
|
};
|
||
|
const mousePosition = { x: 0, y: 0 };
|
||
|
|
||
|
function resize() {
|
||
|
canvas.width = window.innerWidth;
|
||
|
canvas.height = window.innerHeight;
|
||
|
buildPixelData();
|
||
|
}
|
||
|
|
||
|
function draw() {
|
||
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||
|
stats.begin();
|
||
|
if (!built) {
|
||
|
this.buildPixelData();
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < pixels.length; i++) {
|
||
|
const pixel = pixels[i];
|
||
|
const deltaX = pixel.x - mousePosition.x;
|
||
|
const deltaY = pixel.y - mousePosition.y;
|
||
|
const angle = Math.atan2(deltaY, deltaX);
|
||
|
let distance =
|
||
|
config.strength / Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||
|
|
||
|
if (config.glow) {
|
||
|
pixel.r += distance;
|
||
|
pixel.g += distance;
|
||
|
pixel.b += distance;
|
||
|
pixel.r += (pixel.or - pixel.r) * config.easeFactor;
|
||
|
pixel.g += (pixel.og - pixel.g) * config.easeFactor;
|
||
|
pixel.b += (pixel.ob - pixel.b) * config.easeFactor;
|
||
|
context.fillStyle = `rgb(${pixel.r}, ${pixel.g}, ${pixel.b})`;
|
||
|
} else {
|
||
|
context.fillStyle = `rgb(${pixel.or}, ${pixel.og}, ${pixel.ob})`;
|
||
|
}
|
||
|
|
||
|
pixel.x += Math.cos(angle) * distance;
|
||
|
pixel.y += Math.sin(angle) * distance;
|
||
|
pixel.x += (pixel.ox - pixel.x) * config.easeFactor;
|
||
|
pixel.y += (pixel.oy - pixel.y) * config.easeFactor;
|
||
|
|
||
|
context.beginPath();
|
||
|
context.arc(pixel.x, pixel.y, pixel.size / 2, 0, Math.PI * 2);
|
||
|
context.fill();
|
||
|
}
|
||
|
stats.end();
|
||
|
window.requestAnimationFrame(draw);
|
||
|
}
|
||
|
|
||
|
function getPixels() {
|
||
|
const data = context.getImageData(0, 0, img.width, img.height);
|
||
|
const pxs = [];
|
||
|
for (let x = 0; x < data.width; x += config.pxSize) {
|
||
|
for (let y = 0; y < data.height; y += config.pxSize) {
|
||
|
let index = x * 4 + y * 4 * img.width;
|
||
|
let alpha = data.data[index + 3];
|
||
|
if (alpha > 0) {
|
||
|
pxs.push({
|
||
|
x: canvas.width / 2 - img.width / 2 + x,
|
||
|
y: canvas.height / 2 - img.height / 2 + y,
|
||
|
ox: canvas.width / 2 - img.width / 2 + x,
|
||
|
oy: canvas.height / 2 - img.height / 2 + y,
|
||
|
size: config.pxSize,
|
||
|
oSize: config.pxSize,
|
||
|
r: data.data[index],
|
||
|
g: data.data[index + 1],
|
||
|
b: data.data[index + 2],
|
||
|
or: data.data[index],
|
||
|
og: data.data[index + 1],
|
||
|
ob: data.data[index + 2],
|
||
|
rgb:
|
||
|
"rgb(" +
|
||
|
data.data[index] +
|
||
|
"," +
|
||
|
data.data[index + 1] +
|
||
|
"," +
|
||
|
data.data[index + 2] +
|
||
|
")"
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return pxs;
|
||
|
}
|
||
|
|
||
|
function buildPixelData() {
|
||
|
if(img.width === 0 || img.height === 0){
|
||
|
return;
|
||
|
}
|
||
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||
|
context.drawImage(img, 0, 0);
|
||
|
pixels = [];
|
||
|
pixels = getPixels();
|
||
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||
|
if (pixels.length > 0) {
|
||
|
built = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function buildGUI() {
|
||
|
const gui = new dat.GUI({ closed: true });
|
||
|
gui.closed = true;
|
||
|
gui.add(config, "strength", 0, 7000);
|
||
|
gui.add(config, "easeFactor", 0.05, 0.2);
|
||
|
gui.add(config, "glow");
|
||
|
const sizeController = gui.add(config, "pxSize", 8, 80, 1);
|
||
|
const statsController = gui.add(config, "stats");
|
||
|
sizeController.onFinishChange(() => {
|
||
|
buildPixelData();
|
||
|
});
|
||
|
statsController.onChange(value => {
|
||
|
if (value) {
|
||
|
document.body.appendChild(stats.dom);
|
||
|
} else {
|
||
|
stats.dom.parentNode.removeChild(stats.dom);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function onMouseMove(event) {
|
||
|
mousePosition.x = event.clientX;
|
||
|
mousePosition.y = event.clientY;
|
||
|
}
|
||
|
|
||
|
function touchMoveHandler(event) {
|
||
|
mousePosition.x = event.touches[0].clientX;
|
||
|
mousePosition.y = event.touches[0].clientY;
|
||
|
}
|
||
|
|
||
|
const stats = new Stats();
|
||
|
stats.showPanel(0);
|
||
|
|
||
|
window.addEventListener("DOMContentLoaded", event => {
|
||
|
buildGUI();
|
||
|
resize();
|
||
|
window.addEventListener("mousemove", onMouseMove);
|
||
|
window.addEventListener("touchmove", touchMoveHandler);
|
||
|
window.addEventListener("resize", resize);
|
||
|
window.requestAnimationFrame(draw);
|
||
|
});
|