X Tutup

Moonphase Resonance Ring

VFX Spritesheet — horizontal strip — 16 frames — 30 FPS
// 1. Load the spritesheet (horizontal strip)
const fxImage = new Image();
fxImage.src = './Moonphase Resonance Ring_Sheet.png';

// 2. Spritesheet config
const TOTAL_FRAMES = 16;
const SIZE         = 256;
let   FRAME_W, FRAME_H;

fxImage.onload = () => {
    FRAME_W = fxImage.width  / TOTAL_FRAMES; // single horizontal row
    FRAME_H = fxImage.height;
};
// 3. Effect class (renders at 256x256)
class VFX {
    constructor(x, y) {
        this.x = x; this.y = y;
        this.frame = 0;
        this.done  = false;
        this.last  = 0;
        this.fps   = 1000 / 30;
    }
    update(now) {
        if (now - this.last > this.fps) {
            this.frame++;
            this.last = now;
            if (this.frame >= TOTAL_FRAMES)
                this.done = true;
        }
    }
    draw(ctx) {
        if (this.done) return;
        ctx.save();
        ctx.globalCompositeOperation = 'lighter';
        ctx.drawImage(fxImage,
            this.frame * FRAME_W, 0, FRAME_W, FRAME_H, // source: Nth slot in strip
            this.x - SIZE/2, this.y - SIZE/2,
            SIZE, SIZE);
        ctx.restore();
    }
}

// 4. Game loop
const effects = [];
function loop(now) {
    ctx.clearRect(0,0,canvas.width,canvas.height);
    for (let i = effects.length-1; i>=0; i--)
        if (effects[i].done) effects.splice(i,1);
    effects.forEach(e => { e.update(now); e.draw(ctx); });
    requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

// 5. Spawn on click
canvas.addEventListener('mousedown', e => {
    const r = canvas.getBoundingClientRect();
    const x = (e.clientX - r.left) * (canvas.width / r.width);
    const y = (e.clientY - r.top) * (canvas.height / r.height);
    effects.push(new VFX(x, y));
});
/* Pure CSS Solution (horizontal strip) */

.vfx-sprite {
    width: 256px;
    height: 256px;
    background-image: url('./Moonphase Resonance Ring_Sheet.png');
    background-size: 1600% 100%;
    animation: vfx-play 0.53s steps(1) infinite;
}

@keyframes vfx-play {
    0.00% { background-position: 0.00% 0%; }
    6.25% { background-position: 6.67% 0%; }
    12.50% { background-position: 13.33% 0%; }
    18.75% { background-position: 20.00% 0%; }
    25.00% { background-position: 26.67% 0%; }
    31.25% { background-position: 33.33% 0%; }
    37.50% { background-position: 40.00% 0%; }
    43.75% { background-position: 46.67% 0%; }
    50.00% { background-position: 53.33% 0%; }
    56.25% { background-position: 60.00% 0%; }
    62.50% { background-position: 66.67% 0%; }
    68.75% { background-position: 73.33% 0%; }
    75.00% { background-position: 80.00% 0%; }
    81.25% { background-position: 86.67% 0%; }
    87.50% { background-position: 93.33% 0%; }
    93.75% { background-position: 100.00% 0%; }
}

/* One-shot variant (plays once then hides) */
.vfx-sprite.once {
    animation: vfx-play 0.53s steps(1) forwards;
}

<!-- HTML usage -->
<div class="vfx-sprite"></div>
CSS Demo (live)

X Tutup