Skip to content

Commit

Permalink
cool effect
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinBLT committed Dec 14, 2023
1 parent cbca3a9 commit 93fa7a5
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 7 deletions.
46 changes: 39 additions & 7 deletions lib/scratch-card.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
class ScratchCard extends HTMLElement {
canvas = document.createElement('canvas');
span = document.createElement('span');
ctx = this.canvas.getContext('2d', { willReadFrequently: true });
pointer = {x: 0, y: 0};
idleTimer = 0;
canvas = document.createElement('canvas');
particleCanvas = document.createElement('canvas');
span = document.createElement('span');
ctx = this.canvas.getContext('2d', { willReadFrequently: true });
pointer = {x: 0, y: 0};
idleTimer = 0;
worker = new Worker(new URL('scratch-particle.js', import.meta.url), {type: 'module'});

static observedAttributes = [
'code',
Expand Down Expand Up @@ -108,13 +110,16 @@ class ScratchCard extends HTMLElement {
e.stopPropagation();
});

const link = document.createElement('link');
const link = document.createElement('link'),
offscreen = this.particleCanvas.transferControlToOffscreen();

link.rel = 'stylesheet';
link.type = 'text/css';
link.href = new URL('./scratch-card.css', import.meta.url);

this.shadowRoot.append(link, this.span, this.canvas);
this.shadowRoot.append(link, this.span, this.canvas, this.particleCanvas);

this.worker.postMessage(offscreen, [offscreen]);
}

updatePercentageScratched() {
Expand All @@ -131,6 +136,31 @@ class ScratchCard extends HTMLElement {
this.#drawFoil();
}

particles(x, y) {
const image = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);

x = Math.floor(x);
y = Math.floor(y);

const i = (y * this.canvas.width + x) * 4 + 4,
i2 = (y * this.canvas.width + (x + 1)) * 4 + 4,
i3 = ((y + 1) * this.canvas.width + x) * 4 + 4,
i4 = ((y + 1) * this.canvas.width + (x + 1)) * 4 + 4;

if (image.data[i] > 50 && image.data[i2] > 50 && image.data[i3] > 50 && image.data[i4] > 50) {
this.worker.postMessage({
position: {x, y},
scale: window.devicePixelRatio,
fill: this.scratchFill,
offset: this.scratchSize,
size: {
width: this.canvas.width,
height: this.canvas.height
}
});
}
}

draw(x, y) {
this.ctx.shadowColor = 'white';
this.ctx.fillStyle = 'white';
Expand Down Expand Up @@ -163,6 +193,8 @@ class ScratchCard extends HTMLElement {
p.y + this.random(this.scratchSize)
);
}

this.particles(p.x, p.y);

this.ctx.closePath();
this.ctx.fill('evenodd');
Expand Down
108 changes: 108 additions & 0 deletions lib/scratch-particle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
let canvas, settings = {width: 0, height: 0, scale: 1};

/** @type { CanvasRenderingContext2D } */
let ctx;

/**
* @type {{
* created: Date,
* lifetime: number,
* speed: number,
* fill: string,
* position: {
* x: number,
* y: number,
* },
* velocity: {
* x: number,
* y: number,
* }
* }[]}
*/
const particles = [];

const update = () => {
const now = new Date();

Object.assign(canvas, settings);
ctx.scale(settings.scale, settings.scale);

for (let i = 0, p, pec; i < particles.length; i++) {
p = particles[i];

if ((now - p.created) > p.lifetime) {
particles.splice(i--, 1);

continue;
}

p.position.x += p.velocity.x * p.speed;
p.position.y += p.velocity.y * p.speed;

p.velocity.x *= 0.98;
p.velocity.y *= 0.98;

pec = 1 - ((now - p.created) / p.lifetime);

ctx.beginPath();

ctx.fillStyle = p.fill;

const fill = parseInt(ctx.fillStyle.substring(1), 16),
r = (fill >> 16) & 0xFF,
g = (fill >> 8) & 0xFF,
b = fill & 0xFF,
fac = 0.6;

ctx.fillStyle = `rgb(${r * fac}, ${g * fac}, ${b * fac})`;
ctx.globalAlpha = pec;

ctx.fillRect(p.position.x, p.position.y, 1, 1);

ctx.closePath();
}

if (particles.length) {
requestAnimationFrame(update);
}
}

onmessage = (message) => {

if (message.data instanceof OffscreenCanvas) {
canvas = message.data;
ctx = canvas.getContext('2d');
return;
}

const particleCount = 5,
wasEmpty = particles.length == 0;

Object.assign(settings, message.data.size);

settings.scale = message.data.scale;

for (let i = 0; i < particleCount; i++) {

const dir = {
x: -0.5 + Math.random(),
y: -0.5 + Math.random(),
};

particles.push({
created: new Date(),
lifetime: 1000,
speed: Math.random(),
fill: message.data.fill,
position: {
x: message.data.position.x + dir.x * message.data.offset,
y: message.data.position.y + dir.y * message.data.offset
},
velocity: dir
});
}

if (wasEmpty) {
update();
}
}

0 comments on commit 93fa7a5

Please sign in to comment.