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:
@@ -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;
|
||||||
|
|||||||
@@ -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">📷</div>
|
<div class="preview-erreur-icon">📷</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>
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user