mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-11-06 13:40:39 +00:00
Pixel-art layer: animate multi-frame designs when embedded
A multi-frame pixel-art design imported as a layer into another canvas (a cover, slide, custom-art, etc.) was rendering only its first frame as a static image. Now it animates at its stored fps + loop settings, matching what the standalone preview already does. renderPixelArtIntoElement now branches on frames.length > 1: - Multi-frame: pre-render every frame to a native-size canvas (cheap reuse of renderPixelGridToCanvas), then a RAF loop blits the current frame onto a display canvas, advancing by ts deltas to honour the source fps regardless of monitor refresh rate. - The RAF self-stops when the display canvas leaves the DOM (cv.isConnected === false), so a re-render of the editor / preview pane doesn't leak loops. Same pattern as the standalone-design animation. - Single-frame: unchanged static render. Applies everywhere pixel-art-as-layer surfaces: the live editor free-layer overlay, the preview pane in the community browser, and any host design that picks up the layer.
This commit is contained in:
1 parent
95a39cc077
commit
1c205b3ea8
1 file changed
+50
-3
+50
-3
@@ -11204,10 +11204,57 @@
|
||||
// Render a pixel-art layer into its freeLayer element (crisp scaling).
|
||||
function renderPixelArtIntoElement(el, layer) {
|
||||
el.innerHTML = '';
|
||||
const pa = layer.pixelArt || {};
|
||||
try {
|
||||
const cv = renderPixelGridToCanvas(layer.pixelArt || {});
|
||||
cv.style.cssText = 'width:100%;height:100%;display:block;image-rendering:pixelated;image-rendering:crisp-edges;pointer-events:none';
|
||||
el.appendChild(cv);
|
||||
const frames = Array.isArray(pa.frames) ? pa.frames : [];
|
||||
const animate = frames.length > 1;
|
||||
if (animate) {
|
||||
// Multi-frame pixel-art layer: pre-render every frame to its
|
||||
// own native-size canvas, then animate by blitting onto a
|
||||
// display canvas. Mirrors the standalone-preview animation
|
||||
// path (renderPixelArtPreviewDOM). The RAF self-stops when
|
||||
// the host canvas leaves the DOM (cv.isConnected === false)
|
||||
// so a re-render of the editor doesn't leak loops.
|
||||
const fps = Math.max(1, Math.min(60, (pa.fps | 0) || 8));
|
||||
const loop = pa.loop !== false;
|
||||
const w = (pa.width | 0) || 32;
|
||||
const h = (pa.height | 0) || 32;
|
||||
const cached = frames.map((_, i) => renderPixelGridToCanvas(pa, i));
|
||||
const cv = document.createElement('canvas');
|
||||
cv.width = w; cv.height = h;
|
||||
cv.style.cssText = 'width:100%;height:100%;display:block;image-rendering:pixelated;image-rendering:crisp-edges;pointer-events:none';
|
||||
const ctx = cv.getContext('2d');
|
||||
ctx.drawImage(cached[0], 0, 0);
|
||||
let frameIdx = 0;
|
||||
let lastTs = 0, acc = 0;
|
||||
const tick = (ts) => {
|
||||
if (!cv.isConnected) return;
|
||||
const dt = lastTs ? (ts - lastTs) : 0;
|
||||
lastTs = ts;
|
||||
acc += dt;
|
||||
const msPerFrame = 1000 / fps;
|
||||
let ended = false;
|
||||
while (acc >= msPerFrame) {
|
||||
acc -= msPerFrame;
|
||||
const next = frameIdx + 1;
|
||||
if (next >= cached.length) {
|
||||
if (loop) frameIdx = 0;
|
||||
else { frameIdx = cached.length - 1; ended = true; break; }
|
||||
} else {
|
||||
frameIdx = next;
|
||||
}
|
||||
}
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
ctx.drawImage(cached[frameIdx], 0, 0);
|
||||
if (!ended) requestAnimationFrame(tick);
|
||||
};
|
||||
requestAnimationFrame(tick);
|
||||
el.appendChild(cv);
|
||||
} else {
|
||||
const cv = renderPixelGridToCanvas(pa);
|
||||
cv.style.cssText = 'width:100%;height:100%;display:block;image-rendering:pixelated;image-rendering:crisp-edges;pointer-events:none';
|
||||
el.appendChild(cv);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user