Cadre de visee live correspondant au recadrage final pellicule/collage

L'apercu live montrait le plein cadre camera (ratio ~3:2) alors que le
recadrage final de chaque case (creer_strip/creer_collage) est plus carre
(~1.11:1 pour 3 photos en pellicule) : le cadrage visualise pendant le
compte a rebours ne correspondait pas au resultat imprime.

Ajoute un cadre pointille superpose au live preview, dimensionne en
reproduisant cote JS la meme formule de recadrage que backend/collage.py,
visible uniquement en mode multi-shot (strip ou collage).
This commit is contained in:
2026-06-21 22:52:05 +02:00
parent b8f3bef852
commit 0df68da41a
3 changed files with 86 additions and 2 deletions

View File

@@ -327,6 +327,14 @@ html, body {
object-fit: contain; object-fit: contain;
} }
.cadre-guide-recadrage {
position: absolute;
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
border: 3px dashed rgba(255, 255, 255, 0.85);
pointer-events: none;
box-sizing: border-box;
}
.preview-erreur-camera { .preview-erreur-camera {
position: absolute; position: absolute;
top: 0; left: 0; right: 0; bottom: 0; top: 0; left: 0; right: 0; bottom: 0;

View File

@@ -87,6 +87,7 @@
<section id="ecran-capture" class="ecran"> <section id="ecran-capture" class="ecran">
<div id="preview-live" class="preview-live"> <div id="preview-live" class="preview-live">
<img id="img-preview" src="" alt="Preview"> <img id="img-preview" src="" alt="Preview">
<div id="cadre-guide-recadrage" class="cadre-guide-recadrage cache"></div>
<div id="preview-erreur-camera" class="preview-erreur-camera cache"> <div id="preview-erreur-camera" class="preview-erreur-camera cache">
<div class="preview-erreur-icon">&#128247;</div> <div class="preview-erreur-icon">&#128247;</div>
<p>Appareil photo deconnecte</p> <p>Appareil photo deconnecte</p>
@@ -738,7 +739,7 @@
<script src="/js/websocket.js?v=3"></script> <script src="/js/websocket.js?v=3"></script>
<script src="/js/app.js?v=4"></script> <script src="/js/app.js?v=4"></script>
<script src="/js/camera.js?v=6"></script> <script src="/js/camera.js?v=7"></script>
<script src="/js/effects.js?v=3"></script> <script src="/js/effects.js?v=3"></script>
<script src="/js/gallery.js?v=3"></script> <script src="/js/gallery.js?v=3"></script>
<script src="/js/share.js?v=5"></script> <script src="/js/share.js?v=5"></script>

View File

@@ -65,16 +65,91 @@ function arreterPreview() {
previewTimeoutId = null; previewTimeoutId = null;
} }
afficherErreurPreview(false); afficherErreurPreview(false);
document.getElementById('cadre-guide-recadrage')?.classList.add('cache');
} }
wsOnMessage('preview', (msg) => { wsOnMessage('preview', (msg) => {
if (!previewActif) return; if (!previewActif) return;
const img = document.getElementById('img-preview'); const img = document.getElementById('img-preview');
if (img) img.src = msg.image; if (img) {
img.onload = positionnerGuideRecadrage;
img.src = msg.image;
}
afficherErreurPreview(false); afficherErreurPreview(false);
resetPreviewTimeout(); resetPreviewTimeout();
}); });
// --- Cadre de visee : montre en live la zone qui sera gardee apres recadrage ---
// (la pellicule/le collage recadrent chaque photo au centre selon un ratio different du plein cadre camera)
function getRatioCelluleAttendu() {
if (modeActuel !== 'multi') return null;
const nb = getNombrePhotos();
const mode = config.multi_shot?.mode || 'strip';
if (mode === 'strip') {
// Reproduit creer_strip() dans backend/collage.py
const bandeLargeur = 1800, bandeHauteurTotale = 1200, marge = 10, sep = 12, perfHauteur = 34;
const bandeHauteur = Math.floor((bandeHauteurTotale - marge) / 2);
const zonePhotosHauteur = bandeHauteur - perfHauteur * 2;
const espacePhotos = bandeLargeur - marge * 2 - sep * (nb - 1);
const largeurCellule = Math.floor(espacePhotos / nb);
return largeurCellule / zonePhotosHauteur;
} else {
// Reproduit creer_collage() dans backend/collage.py
const colonnes = 2;
const largeurPage = 1200, hauteurPage = 1800, margeC = 25;
const lignes = Math.ceil(nb / colonnes);
const largeurCellule = Math.floor((largeurPage - margeC * (colonnes + 1)) / colonnes);
const hauteurCellule = Math.floor((hauteurPage - margeC * (lignes + 1)) / lignes);
return largeurCellule / hauteurCellule;
}
}
function positionnerGuideRecadrage() {
const guide = document.getElementById('cadre-guide-recadrage');
const img = document.getElementById('img-preview');
const container = document.getElementById('preview-live');
if (!guide || !img || !container) return;
const ratioCible = getRatioCelluleAttendu();
if (!ratioCible || !img.naturalWidth || !img.naturalHeight) {
guide.classList.add('cache');
return;
}
const cw = container.clientWidth, ch = container.clientHeight;
const ratioImg = img.naturalWidth / img.naturalHeight;
const ratioContainer = cw / ch;
// Boite affichee de l'image dans le conteneur (object-fit: contain)
let imgW, imgH, imgTop, imgLeft;
if (ratioImg > ratioContainer) {
imgW = cw; imgH = cw / ratioImg;
imgLeft = 0; imgTop = (ch - imgH) / 2;
} else {
imgH = ch; imgW = ch * ratioImg;
imgTop = 0; imgLeft = (cw - imgW) / 2;
}
// Boite du recadrage final, centree dans l'image affichee (meme logique que _crop_center cote backend)
let guideW, guideH;
if (ratioImg > ratioCible) {
guideH = imgH;
guideW = imgH * ratioCible;
} else {
guideW = imgW;
guideH = imgW / ratioCible;
}
guide.style.left = `${imgLeft + (imgW - guideW) / 2}px`;
guide.style.top = `${imgTop + (imgH - guideH) / 2}px`;
guide.style.width = `${guideW}px`;
guide.style.height = `${guideH}px`;
guide.classList.remove('cache');
}
window.addEventListener('resize', positionnerGuideRecadrage);
// Erreur camera pendant preview idle : retour accueil // Erreur camera pendant preview idle : retour accueil
// Si on est en cours de capture (compte à rebours ou attente API), on ignore : // Si on est en cours de capture (compte à rebours ou attente API), on ignore :
// l'API /api/capturer retournera elle-même une erreur si le DSLR est vraiment mort. // l'API /api/capturer retournera elle-même une erreur si le DSLR est vraiment mort.