Files
Forge-Engine/static/js/screen_edit/personnage-preview.js
T
williamandClaude Sonnet 5 ccf836f2c5
Build and deploy / test-python (push) Successful in 11m18s
Build and deploy / test-js (push) Successful in 1m16s
Build and deploy / lint-python (push) Successful in 4m24s
Build and deploy / lint-js (push) Successful in 2m51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Successful in 5m30s
Lot 4 modernisation JS (SonarLint) + restauration CI sonarqube non-bloquante
Corrige les findings SonarQube (via SonarLint IDE, fichier par fichier)
sur ~24 fichiers static/js/ : parseFloat/parseInt -> Number.*,
.replace(/x/g,y) -> .replaceAll, .indexOf() -> .includes()/.startsWith(),
getAttribute/setAttribute -> .dataset, tableaux -> Set, x && x.y -> x?.y
(verifie site par site), extraction de template litteraux imbriques,
ternaires imbriquees, refactors de complexite cognitive (S3776) via
tables de dispatch, Object.hasOwn, .at(), et deduplication de fonctions
identiques (S4144). Deux exceptions S2486 documentees/corrigees
(filter-repeater-rows.js) et un cas S2703 de partage inter-scripts
complete (_collisionWizard, trigger-editor.js <-> collision-rules-editor.js).
Details complets dans CODE_QUALITY.md section 5.

Restaure aussi le job CI "sonarqube" (non-bloquant) dans
.gitea/workflows/deploy.yml maintenant que l'instance prod est
operationnelle.

Suites vertes : 276/276 JS (node --test), 591/591 Python (pytest).

SKIP=djlint sur ce commit : hook djlint bloquant sur le backlog H021
(styles inline, 49 occurrences/6 templates) deja documente comme dette
assumee non traitee dans CODE_QUALITY.md section 6, aucun rapport avec
ce commit (aucun template touche ici) - valide explicitement avec
l'utilisateur avant de contourner ce hook.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 16:38:29 +02:00

56 lines
2.6 KiB
JavaScript

// ---------- 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();