You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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