// ---------- Aperçu animé immédiat dans l'éditeur (Phase 8) ---------- // Le cœur de la demande : un personnage posé sur la scène doit s'animer // tout de suite dans le canevas, sans ouvrir la Timeline ni la logique de // flow. static/js/play/actions.js (runSpriteAnimation) n'est PAS chargé // dans l'éditeur — mirror allégé de son moteur, scopé à l'idle de chaque // personnage du canevas (un seul intervalle actif par élément, comme le // moteur de jeu). const _personnageIdleIntervals = {}; function _stopPersonnageIdlePreview(elementId) { if (_personnageIdleIntervals[elementId]) { clearInterval(_personnageIdleIntervals[elementId]); delete _personnageIdleIntervals[elementId]; } } function _startPersonnageIdlePreview(targetEl, elementId, frames, fps) { _stopPersonnageIdlePreview(elementId); if (!frames?.length) return; targetEl.src = frames[0]; if (frames.length === 1) return; let frameIndex = 0; _personnageIdleIntervals[elementId] = setInterval(() => { // Navigation pjax (voir pjax.js) : le canevas précédent est remplacé // sans que ce script ne soit prévenu — s'auto-arrête dès que son // élément a quitté le DOM plutôt que de tourner indéfiniment dans le // vide. if (!document.body.contains(targetEl)) { clearInterval(_personnageIdleIntervals[elementId]); return; } frameIndex = (frameIndex + 1) % frames.length; targetEl.src = frames[frameIndex]; }, 1000 / (fps || 8)); } // Appelée après tout changement de personnage/animations (voir // personnage-panel.js) pour relancer l'aperçu sans recharger la page. function restartPersonnageIdlePreview(elementId, animations) { const targetEl = document.querySelector(`#canvas [data-element-id="${elementId}"][data-personnage="1"]`); if (!targetEl) return; const frames = (animations && (animations.idle || Object.values(animations)[0])) || []; _startPersonnageIdlePreview(targetEl, elementId, frames, 8); } function startAllPersonnagePreviews() { document.querySelectorAll('#canvas [data-personnage="1"]').forEach((targetEl) => { const elementId = Number.parseInt(targetEl.dataset.elementId, 10); const animations = ELEMENT_ANIMATIONS_MAP[elementId]; const frames = (animations && (animations.idle || Object.values(animations)[0])) || null; // Pas d'entrée dans ELEMENT_ANIMATIONS_MAP (personnage vidé/sprite // personnalisé sans frame) : garde l'image déjà posée par le rendu // serveur (ou rien), pas d'intervalle à démarrer. if (frames && frames.length > 1) _startPersonnageIdlePreview(targetEl, elementId, frames, 8); }); } startAllPersonnagePreviews();