Add files via upload
This commit is contained in:
+96
-66
@@ -91,15 +91,17 @@ body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Control Panel */
|
||||
.control-panel {
|
||||
width: 320px;
|
||||
width: 250px;
|
||||
background-color: var(--panel-bg);
|
||||
border-right: 1px solid var(--border-color);
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.control-panel h3 {
|
||||
@@ -177,6 +179,31 @@ input[type="range"]::-moz-range-thumb {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* Selettore aspect ratio */
|
||||
.aspect-ratio-select {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.aspect-ratio-select:focus {
|
||||
outline: 2px solid var(--primary-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
#aspect-ratio-hint {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
font-size: 11px;
|
||||
color: var(--text-light);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
padding: 8px 12px;
|
||||
@@ -228,6 +255,32 @@ input[type="range"]::-moz-range-thumb {
|
||||
background-color: #475569;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background-color: var(--warning-color);
|
||||
border-color: var(--warning-color);
|
||||
color: white;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-warning:hover:not(:disabled) {
|
||||
background-color: #c2410c;
|
||||
border-color: #c2410c;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
.pulse-animation {
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.info-box-warning {
|
||||
background-color: #fff7ed;
|
||||
border-color: #fed7aa;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: var(--success-color);
|
||||
color: white;
|
||||
@@ -305,12 +358,12 @@ input[type="range"]::-moz-range-thumb {
|
||||
.info-box {
|
||||
background-color: #f9fafb;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 12px;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
font-family: 'Monaco', 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
font-size: 11px;
|
||||
color: var(--text-color);
|
||||
line-height: 1.5;
|
||||
line-height: 1.4;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
@@ -331,8 +384,8 @@ input[type="range"]::-moz-range-thumb {
|
||||
}
|
||||
|
||||
.action-buttons .btn {
|
||||
padding: 10px;
|
||||
font-size: 13px;
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -356,25 +409,32 @@ hr {
|
||||
background-color: #fafbfc;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#canvas-container {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||
overflow: hidden;
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
#background-image {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
@@ -470,7 +530,7 @@ hr {
|
||||
/* Responsive Design */
|
||||
@media (max-width: 1024px) {
|
||||
.control-panel {
|
||||
width: 280px;
|
||||
width: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,64 +583,34 @@ textarea:focus {
|
||||
<div class="selector-content">
|
||||
<!-- Control Panel -->
|
||||
<div class="control-panel">
|
||||
<h3>Controlli</h3>
|
||||
<h3>Selezione</h3>
|
||||
|
||||
<!-- Image Upload -->
|
||||
<!-- Selettore Aspect Ratio -->
|
||||
<div class="control-group">
|
||||
<label for="image-upload">Carica Immagine:</label>
|
||||
<input type="file" id="image-upload" accept="image/*" style="display: none;">
|
||||
<button id="upload-btn" class="btn btn-primary">
|
||||
<span>📁</span> Scegli Immagine
|
||||
</button>
|
||||
<small id="image-name">Immagine corrente: picsum.photos</small>
|
||||
<label for="aspect-ratio-select">Aspect Ratio:</label>
|
||||
<select id="aspect-ratio-select" class="aspect-ratio-select">
|
||||
<option value="free" selected>Custom (Free)</option>
|
||||
<option value="1:1">1:1 Square 1024x1024</option>
|
||||
<option value="3:4">3:4 Portrait 896x1152</option>
|
||||
<option value="5:8">5:8 Portrait 832x1216</option>
|
||||
<option value="9:16">9:16 Portrait 768x1344</option>
|
||||
<option value="9:21">9:21 Portrait 640x1536</option>
|
||||
<option value="4:3">4:3 Landscape 1152x896</option>
|
||||
<option value="3:2">3:2 Landscape 1216x832</option>
|
||||
<option value="16:9">16:9 Landscape 1344x768</option>
|
||||
<option value="21:9">21:9 Landscape 1536x640</option>
|
||||
</select>
|
||||
<small id="aspect-ratio-hint">Disegno libero - il ratio verrà calcolato</small>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Zoom Controls -->
|
||||
<div class="control-group">
|
||||
<label>Zoom Selezione:</label>
|
||||
<div class="zoom-buttons">
|
||||
<button id="zoom-out-btn" class="btn btn-small" disabled>🔍- Zoom Out</button>
|
||||
<button id="zoom-in-btn" class="btn btn-small" disabled>🔍+ Zoom In</button>
|
||||
</div>
|
||||
<div class="zoom-display">
|
||||
<span id="zoom-value">100%</span>
|
||||
</div>
|
||||
<small>Zoom del rettangolo selezionato</small>
|
||||
<!-- Scale Info Box (nascosto di default) -->
|
||||
<div id="scale-info" class="info-box info-box-secondary" style="display: none; margin-top: 10px;">
|
||||
<!-- Popolato dinamicamente da JS -->
|
||||
</div>
|
||||
|
||||
<!-- Border Width -->
|
||||
<div class="control-group">
|
||||
<label for="border-slider">
|
||||
Spessore Bordo: <span id="border-value">3px</span>
|
||||
</label>
|
||||
<input type="range" id="border-slider" min="1" max="20" value="3" step="1">
|
||||
<small>Larghezza linea di selezione</small>
|
||||
</div>
|
||||
|
||||
<!-- Border Position -->
|
||||
<div class="control-group">
|
||||
<label>Posizione Bordo:</label>
|
||||
<div class="radio-group">
|
||||
<label>
|
||||
<input type="radio" name="border-position" value="inside" checked>
|
||||
Interno
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="border-position" value="outside">
|
||||
Esterno
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Reset Button -->
|
||||
<div class="control-group">
|
||||
<button id="reset-btn" class="btn btn-secondary" disabled>
|
||||
🔄 Cancella Selezione
|
||||
</button>
|
||||
<!-- Auto-fix Suggestion (nascosto di default) -->
|
||||
<div id="auto-fix-suggestion" class="info-box" style="display: none; margin-top: 10px; background-color: #fff7ed; border-color: #fed7aa;">
|
||||
<!-- Popolato dinamicamente da JS -->
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
@@ -621,6 +651,6 @@ textarea:focus {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/extensions/image_region_select/region_selector_core.js"></script>
|
||||
<script src="region_selector_core.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+67
-27
@@ -142,7 +142,11 @@ function handleCanvasMouseDown(e) {
|
||||
const imgRect = backgroundImage.getBoundingClientRect();
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
|
||||
// Coordinate del click relative all'immagine
|
||||
// Calcola il fattore di scala (rapporto tra dimensioni visuali e dimensioni reali)
|
||||
const scaleX = backgroundImage.naturalWidth / imgRect.width;
|
||||
const scaleY = backgroundImage.naturalHeight / imgRect.height;
|
||||
|
||||
// Coordinate del click relative all'immagine (in coordinate visive)
|
||||
const imgX = e.clientX - imgRect.left;
|
||||
const imgY = e.clientY - imgRect.top;
|
||||
|
||||
@@ -151,6 +155,10 @@ function handleCanvasMouseDown(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Converti a coordinate reali dell'immagine
|
||||
const realImgX = imgX * scaleX;
|
||||
const realImgY = imgY * scaleY;
|
||||
|
||||
// Verifica se stai ridimensionando
|
||||
if (currentRectangle) {
|
||||
const handleRect = detectResizeHandle(e, currentRectangle);
|
||||
@@ -178,16 +186,20 @@ function handleCanvasMouseDown(e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Inizia nuovo rettangolo
|
||||
// Inizia nuovo rettangolo (usando coordinate reali)
|
||||
isDrawing = true;
|
||||
startX = imgX;
|
||||
startY = imgY;
|
||||
baseX = imgX;
|
||||
baseY = imgY;
|
||||
startX = realImgX;
|
||||
startY = realImgY;
|
||||
baseX = realImgX;
|
||||
baseY = realImgY;
|
||||
baseWidth = 1;
|
||||
baseHeight = 1;
|
||||
|
||||
createNewRectangle(imgX, imgY, 1, 1);
|
||||
// Salva i fattori di scala globali per uso in handleMouseMove
|
||||
window.scaleX = scaleX;
|
||||
window.scaleY = scaleY;
|
||||
|
||||
createNewRectangle(realImgX, realImgY, 1, 1);
|
||||
}
|
||||
|
||||
function handleMouseMove(e) {
|
||||
@@ -196,16 +208,24 @@ function handleMouseMove(e) {
|
||||
|
||||
const imgRect = backgroundImage.getBoundingClientRect();
|
||||
|
||||
// Usa i fattori di scala salvati o calcolali se non disponibili
|
||||
const scaleX = window.scaleX || (backgroundImage.naturalWidth / imgRect.width);
|
||||
const scaleY = window.scaleY || (backgroundImage.naturalHeight / imgRect.height);
|
||||
|
||||
if (isResizing && currentRectangle && resizingEdge) {
|
||||
handleResizeRectangle(e, imgRect);
|
||||
handleResizeRectangle(e, imgRect, scaleX, scaleY);
|
||||
} else if (isDrawing) {
|
||||
const imgX = e.clientX - imgRect.left;
|
||||
const imgY = e.clientY - imgRect.top;
|
||||
|
||||
const width = Math.max(10, Math.abs(imgX - startX));
|
||||
const height = Math.max(10, Math.abs(imgY - startY));
|
||||
const x = Math.min(startX, imgX);
|
||||
const y = Math.min(startY, imgY);
|
||||
// Converti a coordinate reali
|
||||
const realImgX = imgX * scaleX;
|
||||
const realImgY = imgY * scaleY;
|
||||
|
||||
const width = Math.max(10, Math.abs(realImgX - startX));
|
||||
const height = Math.max(10, Math.abs(realImgY - startY));
|
||||
const x = Math.min(startX, realImgX);
|
||||
const y = Math.min(startY, realImgY);
|
||||
|
||||
baseX = x;
|
||||
baseY = y;
|
||||
@@ -232,12 +252,22 @@ function createNewRectangle(x, y, width, height) {
|
||||
currentRectangle.remove();
|
||||
}
|
||||
|
||||
// Calcola le coordinate visive basate sulle coordinate reali
|
||||
const imgRect = backgroundImage.getBoundingClientRect();
|
||||
const scaleX = backgroundImage.naturalWidth / imgRect.width;
|
||||
const scaleY = backgroundImage.naturalHeight / imgRect.height;
|
||||
|
||||
const visualX = x / scaleX;
|
||||
const visualY = y / scaleY;
|
||||
const visualWidth = width / scaleX;
|
||||
const visualHeight = height / scaleY;
|
||||
|
||||
const rect = document.createElement('div');
|
||||
rect.className = `rectangle border-${borderPosition}`;
|
||||
rect.style.left = x + 'px';
|
||||
rect.style.top = y + 'px';
|
||||
rect.style.width = width + 'px';
|
||||
rect.style.height = height + 'px';
|
||||
rect.style.left = visualX + 'px';
|
||||
rect.style.top = visualY + 'px';
|
||||
rect.style.width = visualWidth + 'px';
|
||||
rect.style.height = visualHeight + 'px';
|
||||
rect.style.borderWidth = currentBorderWidth + 'px';
|
||||
|
||||
// Aggiungi resize handles
|
||||
@@ -259,17 +289,27 @@ function createNewRectangle(x, y, width, height) {
|
||||
function updateRectanglePosition(x, y, width, height) {
|
||||
if (!currentRectangle) return;
|
||||
|
||||
currentRectangle.style.left = x + 'px';
|
||||
currentRectangle.style.top = y + 'px';
|
||||
currentRectangle.style.width = width + 'px';
|
||||
currentRectangle.style.height = height + 'px';
|
||||
// Calcola le coordinate visive basate sulle coordinate reali
|
||||
const imgRect = backgroundImage.getBoundingClientRect();
|
||||
const scaleX = backgroundImage.naturalWidth / imgRect.width;
|
||||
const scaleY = backgroundImage.naturalHeight / imgRect.height;
|
||||
|
||||
const visualX = x / scaleX;
|
||||
const visualY = y / scaleY;
|
||||
const visualWidth = width / scaleX;
|
||||
const visualHeight = height / scaleY;
|
||||
|
||||
currentRectangle.style.left = visualX + 'px';
|
||||
currentRectangle.style.top = visualY + 'px';
|
||||
currentRectangle.style.width = visualWidth + 'px';
|
||||
currentRectangle.style.height = visualHeight + 'px';
|
||||
}
|
||||
|
||||
function handleResizeRectangle(e, imgRect) {
|
||||
function handleResizeRectangle(e, imgRect, scaleX, scaleY) {
|
||||
if (!currentRectangle) return;
|
||||
|
||||
const deltaX = e.clientX - startX;
|
||||
const deltaY = e.clientY - startY;
|
||||
const deltaX = (e.clientX - startX) * scaleX;
|
||||
const deltaY = (e.clientY - startY) * scaleY;
|
||||
|
||||
let newX = rectStartX;
|
||||
let newY = rectStartY;
|
||||
@@ -424,10 +464,10 @@ function updateAllDimensions() {
|
||||
<strong>Altezza:</strong> ${Math.round(baseHeight)} px<br>
|
||||
<br>
|
||||
<strong>Percentuali:</strong><br>
|
||||
<strong>X%:</strong> ${((baseX / backgroundImage.width) * 100).toFixed(2)}%<br>
|
||||
<strong>Y%:</strong> ${((baseY / backgroundImage.height) * 100).toFixed(2)}%<br>
|
||||
<strong>W%:</strong> ${((baseWidth / backgroundImage.width) * 100).toFixed(2)}%<br>
|
||||
<strong>H%:</strong> ${((baseHeight / backgroundImage.height) * 100).toFixed(2)}%
|
||||
<strong>X%:</strong> ${((baseX / backgroundImage.naturalWidth) * 100).toFixed(2)}%<br>
|
||||
<strong>Y%:</strong> ${((baseY / backgroundImage.naturalHeight) * 100).toFixed(2)}%<br>
|
||||
<strong>W%:</strong> ${((baseWidth / backgroundImage.naturalWidth) * 100).toFixed(2)}%<br>
|
||||
<strong>H%:</strong> ${((baseHeight / backgroundImage.naturalHeight) * 100).toFixed(2)}%
|
||||
`;
|
||||
|
||||
baseCoordinates.innerHTML = baseInfo;
|
||||
|
||||
+510
-509
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
// Variabili di stato
|
||||
let isDrawing = false;
|
||||
let isResizing = false;
|
||||
let isDragging = false;
|
||||
let resizingEdge = null;
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
@@ -43,6 +44,8 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
let rectStartY = 0;
|
||||
let rectStartWidth = 0;
|
||||
let rectStartHeight = 0;
|
||||
let dragOffsetX = 0;
|
||||
let dragOffsetY = 0;
|
||||
let currentRectangle = null;
|
||||
let currentBorderWidth = 0;
|
||||
let borderPosition = 'inside';
|
||||
@@ -54,13 +57,49 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
let baseX = 0;
|
||||
let baseY = 0;
|
||||
|
||||
// Aspect Ratio Mode
|
||||
let aspectRatioMode = "free"; // "free" o ratio specifico (es. "16:9")
|
||||
let aspectRatioValue = null; // Valore numerico (es. 16/9 = 1.777)
|
||||
|
||||
// Fix Image Size - tracking dello stato
|
||||
let displayScaleFactor = 1.0; // Fattore di scala applicato alla preview
|
||||
let isImageFixed = false; // True quando immagine è stata "fixata"
|
||||
|
||||
const defaultBorderWidth = 3;
|
||||
|
||||
// Carica l'immagine iniziale
|
||||
// ========================================================
|
||||
// Ridimensionamento immagini > 1024px lato backend
|
||||
// ========================================================
|
||||
if (imageUrl) {
|
||||
backgroundImage.src = imageUrl;
|
||||
const filename = imageUrl.split('/').pop();
|
||||
imageName.textContent = `Image: ${filename}`;
|
||||
|
||||
fetch("/region_selector/scale", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ filename })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.path) {
|
||||
backgroundImage.src = data.path;
|
||||
backgroundImage.dataset.scaleFactor = data.scale || 1;
|
||||
backgroundImage.dataset.scaled = data.scaled || false;
|
||||
console.log(`[RegionSelectorExt] Immagine caricata - Scale: ${data.scale || 1}`);
|
||||
} else if (data.error) {
|
||||
console.warn(`[RegionSelectorExt] Scale error: ${data.error}`);
|
||||
backgroundImage.src = imageUrl;
|
||||
backgroundImage.dataset.scaleFactor = 1;
|
||||
backgroundImage.dataset.scaled = false;
|
||||
}
|
||||
imageName.textContent = `Image: ${filename}`;
|
||||
})
|
||||
.catch(e => {
|
||||
console.error("[RegionSelectorExt] Errore scale:", e);
|
||||
backgroundImage.src = imageUrl;
|
||||
backgroundImage.dataset.scaleFactor = 1;
|
||||
backgroundImage.dataset.scaled = false;
|
||||
imageName.textContent = `Image: ${filename}`;
|
||||
});
|
||||
}
|
||||
|
||||
// Disabilita drag dell'immagine
|
||||
@@ -81,8 +120,158 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
}
|
||||
});
|
||||
|
||||
// Mouse down - inizio disegno
|
||||
// Aspect Ratio Mode Selector
|
||||
const aspectRatioSelect = container.querySelector('#aspect-ratio-select');
|
||||
const aspectRatioHint = container.querySelector('#aspect-ratio-hint');
|
||||
|
||||
if (aspectRatioSelect) {
|
||||
aspectRatioSelect.addEventListener('change', (e) => {
|
||||
aspectRatioMode = e.target.value;
|
||||
|
||||
// Calcola valore numerico
|
||||
const ratioMap = {
|
||||
"free": null,
|
||||
"1:1": 1/1,
|
||||
"3:4": 3/4,
|
||||
"5:8": 5/8,
|
||||
"9:16": 9/16,
|
||||
"9:21": 9/21,
|
||||
"4:3": 4/3,
|
||||
"3:2": 3/2,
|
||||
"16:9": 16/9,
|
||||
"21:9": 21/9,
|
||||
};
|
||||
|
||||
aspectRatioValue = ratioMap[aspectRatioMode];
|
||||
|
||||
// Aggiorna hint
|
||||
if (aspectRatioMode === "free") {
|
||||
aspectRatioHint.textContent = "Disegno libero - il ratio verrà calcolato";
|
||||
aspectRatioHint.style.color = "#64748b";
|
||||
aspectRatioHint.style.fontWeight = "normal";
|
||||
} else {
|
||||
aspectRatioHint.textContent = `Rettangolo vincolato a ${aspectRatioMode}`;
|
||||
aspectRatioHint.style.color = "#16a34a";
|
||||
aspectRatioHint.style.fontWeight = "600";
|
||||
}
|
||||
|
||||
console.log(`[AspectRatio] Mode: ${aspectRatioMode}, Value: ${aspectRatioValue}`);
|
||||
|
||||
// Se c'è già un rettangolo e passi a modalità vincolata, adattalo
|
||||
if (rectangleExists && aspectRatioValue !== null) {
|
||||
adjustRectangleToAspectRatio();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// FIX IMAGE SIZE - BOTTONE DINAMICO
|
||||
// ========================================================
|
||||
function createFixImageButton() {
|
||||
// Bottone rimosso - la scala viene applicata automaticamente
|
||||
// Niente da fare qui
|
||||
console.log('[FixImage] Scale auto-applied, no button needed');
|
||||
}
|
||||
|
||||
function fixImageScale() {
|
||||
const naturalW = backgroundImage.naturalWidth;
|
||||
const naturalH = backgroundImage.naturalHeight;
|
||||
const maxDim = 1024;
|
||||
|
||||
const maxCurrent = Math.max(naturalW, naturalH);
|
||||
|
||||
if (maxCurrent <= maxDim) {
|
||||
alert("✓ Immagine già piccola!");
|
||||
return;
|
||||
}
|
||||
|
||||
displayScaleFactor = maxDim / maxCurrent;
|
||||
const newW = Math.round(naturalW * displayScaleFactor);
|
||||
const newH = Math.round(naturalH * displayScaleFactor);
|
||||
|
||||
console.log(`[FixImage] Scaling ${naturalW}x${naturalH} → ${newW}x${newH}`);
|
||||
|
||||
backgroundImage.style.width = `${newW}px`;
|
||||
backgroundImage.style.height = `${newH}px`;
|
||||
backgroundImage.style.maxWidth = 'none';
|
||||
backgroundImage.style.maxHeight = 'none';
|
||||
|
||||
isImageFixed = true;
|
||||
const fixImageBtn = container.querySelector('#fix-image-btn');
|
||||
if (fixImageBtn) {
|
||||
fixImageBtn.textContent = "🔄 Reset Scale";
|
||||
fixImageBtn.classList.remove('btn-primary');
|
||||
fixImageBtn.classList.add('btn-warning');
|
||||
}
|
||||
|
||||
const scaleInfo = container.querySelector('#scale-info');
|
||||
if (scaleInfo) {
|
||||
const scalePercent = (displayScaleFactor * 100).toFixed(1);
|
||||
scaleInfo.innerHTML = `
|
||||
<p><strong>📊 Preview Scale:</strong> ${scalePercent}%</p>
|
||||
<p><strong>🖼️ Display Size:</strong> ${newW} × ${newH} px</p>
|
||||
<p><strong>📐 Original Size:</strong> ${naturalW} × ${naturalH} px</p>
|
||||
<p style="color: #16a34a; font-weight: 600; margin-top: 8px;">✓ Selezione fluida attiva</p>
|
||||
`;
|
||||
scaleInfo.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function resetImageScale() {
|
||||
displayScaleFactor = 1.0;
|
||||
console.log("[FixImage] Resetting to original scale");
|
||||
|
||||
backgroundImage.style.width = 'auto';
|
||||
backgroundImage.style.height = 'auto';
|
||||
backgroundImage.style.maxWidth = '100%';
|
||||
backgroundImage.style.maxHeight = '100%';
|
||||
|
||||
isImageFixed = false;
|
||||
const fixImageBtn = container.querySelector('#fix-image-btn');
|
||||
if (fixImageBtn) {
|
||||
fixImageBtn.textContent = "⚡ Fix Image Size";
|
||||
fixImageBtn.classList.remove('btn-warning');
|
||||
fixImageBtn.classList.add('btn-primary');
|
||||
}
|
||||
|
||||
const scaleInfo = container.querySelector('#scale-info');
|
||||
if (scaleInfo) {
|
||||
scaleInfo.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-fixa immagine se è grande (> 1024px)
|
||||
setTimeout(() => {
|
||||
const naturalW = backgroundImage.naturalWidth;
|
||||
const naturalH = backgroundImage.naturalHeight;
|
||||
const maxDim = Math.max(naturalW, naturalH);
|
||||
|
||||
console.log(`[FixImage] Image size: ${naturalW}x${naturalH}, max: ${maxDim}`);
|
||||
|
||||
if (maxDim > 1024) {
|
||||
console.log('[FixImage] Large image detected, creating button and auto-fixing scale...');
|
||||
createFixImageButton();
|
||||
// Applica la scala automaticamente
|
||||
setTimeout(() => {
|
||||
fixImageScale();
|
||||
}, 100);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// Mouse down - inizio disegno o drag
|
||||
canvasContainer.addEventListener('mousedown', (e) => {
|
||||
// Se il rettangolo esiste e clicchi su di esso (non su un handle), inizia il drag
|
||||
if (rectangleExists && e.target === currentRectangle) {
|
||||
isDragging = true;
|
||||
const rect = canvasContainer.getBoundingClientRect();
|
||||
const mouseX = e.clientX - rect.left;
|
||||
const mouseY = e.clientY - rect.top;
|
||||
dragOffsetX = mouseX - parseFloat(currentRectangle.style.left);
|
||||
dragOffsetY = mouseY - parseFloat(currentRectangle.style.top);
|
||||
canvasContainer.style.cursor = 'grab';
|
||||
return;
|
||||
}
|
||||
|
||||
if (rectangleExists) return;
|
||||
if (e.target.classList.contains('resize-handle')) return;
|
||||
|
||||
@@ -95,6 +284,23 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
currentRectangle = document.createElement('div');
|
||||
currentRectangle.className = 'rectangle';
|
||||
|
||||
// Se sei in modalità vincolata, resetta a free (l'utente sta facendo una nuova selezione)
|
||||
if (aspectRatioMode !== "free") {
|
||||
const aspectRatioSelect = container.querySelector('#aspect-ratio-select');
|
||||
if (aspectRatioSelect) {
|
||||
aspectRatioSelect.value = "free";
|
||||
aspectRatioMode = "free";
|
||||
aspectRatioValue = null;
|
||||
const aspectRatioHint = container.querySelector('#aspect-ratio-hint');
|
||||
if (aspectRatioHint) {
|
||||
aspectRatioHint.textContent = "Disegno libero - il ratio verrà calcolato";
|
||||
aspectRatioHint.style.color = "#64748b";
|
||||
aspectRatioHint.style.fontWeight = "normal";
|
||||
}
|
||||
console.log("[AspectRatio] Reset to FREE mode for new selection");
|
||||
}
|
||||
}
|
||||
|
||||
if (borderPosition === 'outside') {
|
||||
currentRectangle.classList.add('border-outside');
|
||||
currentRectangle.style.outlineWidth = currentBorderWidth + 'px';
|
||||
@@ -119,27 +325,69 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
|
||||
// Mouse move
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
// Drag del rettangolo
|
||||
if (isDragging && currentRectangle) {
|
||||
const rect = canvasContainer.getBoundingClientRect();
|
||||
const mouseX = e.clientX - rect.left;
|
||||
const mouseY = e.clientY - rect.top;
|
||||
|
||||
let newLeft = mouseX - dragOffsetX;
|
||||
let newTop = mouseY - dragOffsetY;
|
||||
|
||||
// Vincola il rettangolo dentro il canvas
|
||||
const maxLeft = rect.width - parseFloat(currentRectangle.style.width);
|
||||
const maxTop = rect.height - parseFloat(currentRectangle.style.height);
|
||||
|
||||
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
|
||||
newTop = Math.max(0, Math.min(newTop, maxTop));
|
||||
|
||||
currentRectangle.style.left = newLeft + 'px';
|
||||
currentRectangle.style.top = newTop + 'px';
|
||||
|
||||
baseX = newLeft;
|
||||
baseY = newTop;
|
||||
|
||||
updateAllDimensions();
|
||||
canvasContainer.style.cursor = 'grabbing';
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDrawing) {
|
||||
const rect = canvasContainer.getBoundingClientRect();
|
||||
const currentX = e.clientX - rect.left;
|
||||
const currentY = e.clientY - rect.top;
|
||||
|
||||
const width = currentX - startX;
|
||||
const height = currentY - startY;
|
||||
let width = currentX - startX;
|
||||
let height = currentY - startY;
|
||||
|
||||
// ⚙️ APPLICA VINCOLO SE NECESSARIO
|
||||
if (aspectRatioValue !== null) {
|
||||
// Modalità vincolata: forza l'aspect ratio
|
||||
if (Math.abs(width) / aspectRatioValue > Math.abs(height)) {
|
||||
height = (Math.abs(width) / aspectRatioValue) * (height < 0 ? -1 : 1);
|
||||
} else {
|
||||
width = (Math.abs(height) * aspectRatioValue) * (width < 0 ? -1 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
baseWidth = Math.abs(width);
|
||||
baseHeight = Math.abs(height);
|
||||
|
||||
if (width < 0) {
|
||||
currentRectangle.style.left = currentX + 'px';
|
||||
currentRectangle.style.left = (currentX + width) + 'px';
|
||||
currentRectangle.style.width = Math.abs(width) + 'px';
|
||||
} else {
|
||||
currentRectangle.style.width = width + 'px';
|
||||
}
|
||||
|
||||
if (height < 0) {
|
||||
currentRectangle.style.top = currentY + 'px';
|
||||
currentRectangle.style.top = (currentY + height) + 'px';
|
||||
currentRectangle.style.height = Math.abs(height) + 'px';
|
||||
} else {
|
||||
currentRectangle.style.height = height + 'px';
|
||||
}
|
||||
|
||||
updateAllDimensions();
|
||||
}
|
||||
|
||||
if (isResizing && currentRectangle) {
|
||||
@@ -147,79 +395,185 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
const deltaY = e.clientY - startY;
|
||||
|
||||
switch(resizingEdge) {
|
||||
case 'top':
|
||||
const newTop = rectStartY + deltaY;
|
||||
const newHeight = rectStartHeight - deltaY;
|
||||
if (newHeight > 0) {
|
||||
currentRectangle.style.top = newTop + 'px';
|
||||
currentRectangle.style.height = newHeight + 'px';
|
||||
case 'bottom-right':
|
||||
let brNewWidth = rectStartWidth + deltaX;
|
||||
let brNewHeight = rectStartHeight + deltaY;
|
||||
|
||||
// ⚙️ APPLICA VINCOLO SE NECESSARIO
|
||||
if (aspectRatioValue !== null) {
|
||||
if (brNewWidth / aspectRatioValue > brNewHeight) {
|
||||
brNewHeight = brNewWidth / aspectRatioValue;
|
||||
} else {
|
||||
brNewWidth = brNewHeight * aspectRatioValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (brNewHeight > 0 && brNewWidth > 0) {
|
||||
currentRectangle.style.width = brNewWidth + 'px';
|
||||
currentRectangle.style.height = brNewHeight + 'px';
|
||||
baseWidth = brNewWidth;
|
||||
baseHeight = brNewHeight;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bottom':
|
||||
const bottomHeight = rectStartHeight + deltaY;
|
||||
if (bottomHeight > 0) {
|
||||
currentRectangle.style.height = bottomHeight + 'px';
|
||||
case 'bottom-left':
|
||||
let blNewLeft = rectStartX + deltaX;
|
||||
let blNewHeight = rectStartHeight + deltaY;
|
||||
let blNewWidth = rectStartWidth - deltaX;
|
||||
|
||||
// ⚙️ APPLICA VINCOLO SE NECESSARIO
|
||||
if (aspectRatioValue !== null) {
|
||||
if (blNewWidth / aspectRatioValue > blNewHeight) {
|
||||
blNewHeight = blNewWidth / aspectRatioValue;
|
||||
} else {
|
||||
blNewWidth = blNewHeight * aspectRatioValue;
|
||||
blNewLeft = rectStartX + (rectStartWidth - blNewWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (blNewHeight > 0 && blNewWidth > 0) {
|
||||
currentRectangle.style.left = blNewLeft + 'px';
|
||||
currentRectangle.style.height = blNewHeight + 'px';
|
||||
currentRectangle.style.width = blNewWidth + 'px';
|
||||
baseX = blNewLeft;
|
||||
baseWidth = blNewWidth;
|
||||
baseHeight = blNewHeight;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'left':
|
||||
const newLeft = rectStartX + deltaX;
|
||||
const newWidth = rectStartWidth - deltaX;
|
||||
if (newWidth > 0) {
|
||||
currentRectangle.style.left = newLeft + 'px';
|
||||
currentRectangle.style.width = newWidth + 'px';
|
||||
}
|
||||
break;
|
||||
case 'top-right':
|
||||
let trNewTop = rectStartY + deltaY;
|
||||
let trNewHeight = rectStartHeight - deltaY;
|
||||
let trNewWidth = rectStartWidth + deltaX;
|
||||
|
||||
case 'right':
|
||||
const rightWidth = rectStartWidth + deltaX;
|
||||
if (rightWidth > 0) {
|
||||
currentRectangle.style.width = rightWidth + 'px';
|
||||
// ⚙️ APPLICA VINCOLO SE NECESSARIO
|
||||
if (aspectRatioValue !== null) {
|
||||
if (trNewWidth / aspectRatioValue > trNewHeight) {
|
||||
trNewHeight = trNewWidth / aspectRatioValue;
|
||||
trNewTop = rectStartY + (rectStartHeight - trNewHeight);
|
||||
} else {
|
||||
trNewWidth = trNewHeight * aspectRatioValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (trNewHeight > 0 && trNewWidth > 0) {
|
||||
currentRectangle.style.top = trNewTop + 'px';
|
||||
currentRectangle.style.height = trNewHeight + 'px';
|
||||
currentRectangle.style.width = trNewWidth + 'px';
|
||||
baseY = trNewTop;
|
||||
baseWidth = trNewWidth;
|
||||
baseHeight = trNewHeight;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'top-left':
|
||||
const tlNewTop = rectStartY + deltaY;
|
||||
const tlNewLeft = rectStartX + deltaX;
|
||||
const tlNewHeight = rectStartHeight - deltaY;
|
||||
const tlNewWidth = rectStartWidth - deltaX;
|
||||
let tlNewTop = rectStartY + deltaY;
|
||||
let tlNewLeft = rectStartX + deltaX;
|
||||
let tlNewHeight = rectStartHeight - deltaY;
|
||||
let tlNewWidth = rectStartWidth - deltaX;
|
||||
|
||||
// ⚙️ APPLICA VINCOLO SE NECESSARIO
|
||||
if (aspectRatioValue !== null) {
|
||||
if (tlNewWidth / aspectRatioValue > tlNewHeight) {
|
||||
tlNewHeight = tlNewWidth / aspectRatioValue;
|
||||
tlNewTop = rectStartY + (rectStartHeight - tlNewHeight);
|
||||
} else {
|
||||
tlNewWidth = tlNewHeight * aspectRatioValue;
|
||||
tlNewLeft = rectStartX + (rectStartWidth - tlNewWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (tlNewHeight > 0 && tlNewWidth > 0) {
|
||||
currentRectangle.style.top = tlNewTop + 'px';
|
||||
currentRectangle.style.left = tlNewLeft + 'px';
|
||||
currentRectangle.style.height = tlNewHeight + 'px';
|
||||
currentRectangle.style.width = tlNewWidth + 'px';
|
||||
baseX = tlNewLeft;
|
||||
baseY = tlNewTop;
|
||||
baseWidth = tlNewWidth;
|
||||
baseHeight = tlNewHeight;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'top-right':
|
||||
const trNewTop = rectStartY + deltaY;
|
||||
const trNewHeight = rectStartHeight - deltaY;
|
||||
const trNewWidth = rectStartWidth + deltaX;
|
||||
if (trNewHeight > 0 && trNewWidth > 0) {
|
||||
currentRectangle.style.top = trNewTop + 'px';
|
||||
currentRectangle.style.height = trNewHeight + 'px';
|
||||
currentRectangle.style.width = trNewWidth + 'px';
|
||||
case 'right':
|
||||
let rightWidth = rectStartWidth + deltaX;
|
||||
let rightHeight = rightWidth / aspectRatioValue || rectStartHeight;
|
||||
|
||||
// ⚙️ APPLICA VINCOLO SE NECESSARIO
|
||||
if (aspectRatioValue !== null) {
|
||||
rightHeight = rightWidth / aspectRatioValue;
|
||||
}
|
||||
|
||||
if (rightWidth > 0) {
|
||||
currentRectangle.style.width = rightWidth + 'px';
|
||||
if (aspectRatioValue !== null && rightHeight > 0) {
|
||||
currentRectangle.style.height = rightHeight + 'px';
|
||||
baseWidth = rightWidth;
|
||||
baseHeight = rightHeight;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bottom-left':
|
||||
const blNewLeft = rectStartX + deltaX;
|
||||
const blNewHeight = rectStartHeight + deltaY;
|
||||
const blNewWidth = rectStartWidth - deltaX;
|
||||
if (blNewHeight > 0 && blNewWidth > 0) {
|
||||
currentRectangle.style.left = blNewLeft + 'px';
|
||||
currentRectangle.style.height = blNewHeight + 'px';
|
||||
currentRectangle.style.width = blNewWidth + 'px';
|
||||
case 'left':
|
||||
let newLeft = rectStartX + deltaX;
|
||||
let newWidth = rectStartWidth - deltaX;
|
||||
let newHeight = newWidth / aspectRatioValue || rectStartHeight;
|
||||
|
||||
// ⚙️ APPLICA VINCOLO SE NECESSARIO
|
||||
if (aspectRatioValue !== null) {
|
||||
newHeight = newWidth / aspectRatioValue;
|
||||
}
|
||||
|
||||
if (newWidth > 0) {
|
||||
currentRectangle.style.left = newLeft + 'px';
|
||||
currentRectangle.style.width = newWidth + 'px';
|
||||
if (aspectRatioValue !== null && newHeight > 0) {
|
||||
currentRectangle.style.height = newHeight + 'px';
|
||||
baseX = newLeft;
|
||||
baseWidth = newWidth;
|
||||
baseHeight = newHeight;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bottom-right':
|
||||
const brNewWidth = rectStartWidth + deltaX;
|
||||
const brNewHeight = rectStartHeight + deltaY;
|
||||
if (brNewHeight > 0 && brNewWidth > 0) {
|
||||
currentRectangle.style.width = brNewWidth + 'px';
|
||||
currentRectangle.style.height = brNewHeight + 'px';
|
||||
case 'bottom':
|
||||
let bottomHeight = rectStartHeight + deltaY;
|
||||
let bottomWidth = bottomHeight * aspectRatioValue || rectStartWidth;
|
||||
|
||||
// ⚙️ APPLICA VINCOLO SE NECESSARIO
|
||||
if (aspectRatioValue !== null) {
|
||||
bottomWidth = bottomHeight * aspectRatioValue;
|
||||
}
|
||||
|
||||
if (bottomHeight > 0) {
|
||||
currentRectangle.style.height = bottomHeight + 'px';
|
||||
if (aspectRatioValue !== null && bottomWidth > 0) {
|
||||
currentRectangle.style.width = bottomWidth + 'px';
|
||||
baseWidth = bottomWidth;
|
||||
baseHeight = bottomHeight;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'top':
|
||||
let newTop = rectStartY + deltaY;
|
||||
let topNewHeight = rectStartHeight - deltaY;
|
||||
let topNewWidth = topNewHeight * aspectRatioValue || rectStartWidth;
|
||||
|
||||
// ⚙️ APPLICA VINCOLO SE NECESSARIO
|
||||
if (aspectRatioValue !== null) {
|
||||
topNewWidth = topNewHeight * aspectRatioValue;
|
||||
}
|
||||
|
||||
if (topNewHeight > 0) {
|
||||
currentRectangle.style.top = newTop + 'px';
|
||||
currentRectangle.style.height = topNewHeight + 'px';
|
||||
if (aspectRatioValue !== null && topNewWidth > 0) {
|
||||
currentRectangle.style.width = topNewWidth + 'px';
|
||||
baseY = newTop;
|
||||
baseWidth = topNewWidth;
|
||||
baseHeight = topNewHeight;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -271,6 +625,11 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
resizingEdge = null;
|
||||
canvasContainer.style.cursor = 'default';
|
||||
}
|
||||
|
||||
if (isDragging) {
|
||||
isDragging = false;
|
||||
canvasContainer.style.cursor = 'default';
|
||||
}
|
||||
});
|
||||
|
||||
function resetRectangle() {
|
||||
@@ -286,6 +645,21 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
baseCoordinates.innerHTML = '<p>Click and drag to select</p>';
|
||||
}
|
||||
|
||||
function adjustRectangleToAspectRatio() {
|
||||
if (!aspectRatioValue || !rectangleExists || !currentRectangle) return;
|
||||
|
||||
// Mantieni larghezza, adatta altezza al ratio
|
||||
baseHeight = baseWidth / aspectRatioValue;
|
||||
|
||||
// Aggiorna visualizzazione
|
||||
currentRectangle.style.width = baseWidth + 'px';
|
||||
currentRectangle.style.height = baseHeight + 'px';
|
||||
|
||||
updateAllDimensions();
|
||||
|
||||
console.log(`[AspectRatio] Adjusted to ${aspectRatioMode}: ${Math.round(baseWidth)}x${Math.round(baseHeight)}`);
|
||||
}
|
||||
|
||||
function addResizeHandles() {
|
||||
const handles = ['top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
||||
handles.forEach(position => {
|
||||
@@ -319,19 +693,75 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
const baseX2 = baseX + baseWidth;
|
||||
const baseY2 = baseY + baseHeight;
|
||||
|
||||
const w = Math.round(baseWidth);
|
||||
const h = Math.round(baseHeight);
|
||||
const ratio = w / h;
|
||||
|
||||
// 🎯 MODALITÀ CUSTOM: Calcola approssimazione
|
||||
let aspectRatioDisplay;
|
||||
|
||||
if (aspectRatioMode === "free") {
|
||||
// Lista aspect ratio standard
|
||||
const standardRatios = [
|
||||
{ value: 21/9, label: "21:9 Landscape", display: "21:9" },
|
||||
{ value: 16/9, label: "16:9 Landscape", display: "16:9" },
|
||||
{ value: 3/2, label: "3:2 Landscape", display: "3:2" },
|
||||
{ value: 4/3, label: "4:3 Landscape", display: "4:3" },
|
||||
{ value: 1/1, label: "1:1 Square", display: "1:1" },
|
||||
{ value: 3/4, label: "3:4 Portrait", display: "3:4" },
|
||||
{ value: 5/8, label: "5:8 Portrait", display: "5:8" },
|
||||
{ value: 9/16, label: "9:16 Portrait", display: "9:16" },
|
||||
{ value: 9/21, label: "9:21 Portrait", display: "9:21" },
|
||||
];
|
||||
|
||||
// Trova il più vicino
|
||||
let closestRatio = standardRatios[0];
|
||||
let minDiff = Math.abs(ratio - standardRatios[0].value);
|
||||
|
||||
for (const r of standardRatios) {
|
||||
const diff = Math.abs(ratio - r.value);
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
closestRatio = r;
|
||||
}
|
||||
}
|
||||
|
||||
const diffPercent = (minDiff / ratio) * 100;
|
||||
|
||||
// Formato display basato su vicinanza
|
||||
if (diffPercent < 3) {
|
||||
// Molto vicino - mostra come esatto
|
||||
aspectRatioDisplay = `<span style="color: #16a34a; font-weight: bold;">✓ ${closestRatio.label}</span>`;
|
||||
} else if (diffPercent < 8) {
|
||||
// Abbastanza vicino - mostra approssimato
|
||||
aspectRatioDisplay = `<span style="color: #ea580c; font-weight: bold;">~ ${closestRatio.label}</span> <span style="opacity: 0.6; font-size: 11px;">(${ratio.toFixed(2)}:1)</span>`;
|
||||
} else {
|
||||
// Troppo diverso - mostra custom + più vicino
|
||||
aspectRatioDisplay = `<span style="color: #2563eb; font-weight: bold;">${ratio.toFixed(2)}:1</span> <span style="opacity: 0.5; font-size: 11px;">(vicino: ${closestRatio.display})</span>`;
|
||||
}
|
||||
} else {
|
||||
// 🔒 MODALITÀ VINCOLATA: Mostra il vincolo attivo
|
||||
aspectRatioDisplay = `<span style="color: #16a34a; font-weight: bold;">🔒 ${aspectRatioMode}</span> <span style="opacity: 0.6; font-size: 11px;">(${ratio.toFixed(2)}:1)</span>`;
|
||||
}
|
||||
|
||||
baseCoordinates.innerHTML = `
|
||||
<strong>📍 Coordinates:</strong><br>
|
||||
x1 = ${Math.round(baseX1)}px, y1 = ${Math.round(baseY1)}px<br>
|
||||
x2 = ${Math.round(baseX2)}px, y2 = ${Math.round(baseY2)}px<br>
|
||||
<br>
|
||||
<strong>📏 Size:</strong><br>
|
||||
Width: ${Math.round(baseWidth)}px<br>
|
||||
Height: ${Math.round(baseHeight)}px
|
||||
Width: ${w}px<br>
|
||||
Height: ${h}px<br>
|
||||
<br>
|
||||
<strong>✨ Aspect Ratio:</strong> ${aspectRatioDisplay}
|
||||
`;
|
||||
}
|
||||
|
||||
return {
|
||||
getCoordinates: () => {
|
||||
// Leggi il fattore di scala salvato dal backend
|
||||
const scaleFactor = parseFloat(backgroundImage.dataset.scaleFactor || "1");
|
||||
|
||||
const baseX1 = baseX;
|
||||
const baseY1 = baseY;
|
||||
const baseX2 = baseX + baseWidth;
|
||||
@@ -361,6 +791,7 @@ function initializeCanvasSelector(container, imageUrl) {
|
||||
y2: Math.round(effectiveY2),
|
||||
borderWidth: currentBorderWidth,
|
||||
borderPosition: borderPosition,
|
||||
displayScaleFactor: displayScaleFactor,
|
||||
};
|
||||
},
|
||||
getState: () => ({
|
||||
@@ -621,6 +1052,31 @@ async function openRegionDialog(node, app) {
|
||||
<small id="image-name" style="display: block; font-size: 12px; color: #64748b;">Image: ${imageName}</small>
|
||||
</div>
|
||||
|
||||
<!-- Aspect Ratio Selector -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label for="aspect-ratio-select" style="display: block; font-size: 12px; font-weight: 600; margin-bottom: 8px;">Aspect Ratio:</label>
|
||||
<select id="aspect-ratio-select" style="
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
">
|
||||
<option value="free" selected>Custom (Free)</option>
|
||||
<option value="1:1">1:1 Square</option>
|
||||
<option value="3:4">3:4 Portrait</option>
|
||||
<option value="5:8">5:8 Portrait</option>
|
||||
<option value="9:16">9:16 Portrait</option>
|
||||
<option value="9:21">9:21 Portrait</option>
|
||||
<option value="4:3">4:3 Landscape</option>
|
||||
<option value="3:2">3:2 Landscape</option>
|
||||
<option value="16:9">16:9 Landscape</option>
|
||||
<option value="21:9">21:9 Landscape</option>
|
||||
</select>
|
||||
<small id="aspect-ratio-hint" style="display: block; margin-top: 5px; font-size: 11px; color: #64748b; font-style: italic;">Disegno libero - il ratio verrà calcolato</small>
|
||||
</div>
|
||||
|
||||
<!-- Reset Button -->
|
||||
<button id="reset-btn" class="btn" disabled style="
|
||||
|
||||
@@ -228,16 +228,57 @@ function handleSelectorCancel(modal) {
|
||||
|
||||
/**
|
||||
* Carica un'immagine come sfondo nell'interfaccia
|
||||
* (Opzionale - per preview)
|
||||
* Ridimensiona automaticamente se > 1024px
|
||||
*/
|
||||
function loadBackgroundImage(node, widthWidget, heightWidget) {
|
||||
async function loadBackgroundImage(node, widthWidget, heightWidget) {
|
||||
try {
|
||||
// Cerca un nodo con input immagine nel flusso
|
||||
// Per ora, usa l'immagine default di picsum.photos
|
||||
const bgImg = document.getElementById("background-image");
|
||||
if (bgImg) {
|
||||
// Opzionalmente puoi aggiungere logica per caricare immagini
|
||||
console.log("Image loader ready");
|
||||
if (!bgImg) {
|
||||
console.warn("background-image element not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cerca il widget immagine del nodo
|
||||
const imageWidget = node.widgets.find(w => w.type === "image" || w.name === "image");
|
||||
if (!imageWidget || !imageWidget.value) {
|
||||
console.warn("No image widget found on node");
|
||||
return;
|
||||
}
|
||||
|
||||
// Estrai il filename dall'immagine
|
||||
let filename = imageWidget.value;
|
||||
if (typeof filename === "object" && filename.name) {
|
||||
filename = filename.name;
|
||||
}
|
||||
|
||||
console.log(`[RegionSelector] Caricamento immagine: ${filename}`);
|
||||
|
||||
// Chiama l'endpoint per scalare se necessario
|
||||
const scaleResponse = await fetch("/region_selector/scale", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ filename: filename })
|
||||
});
|
||||
|
||||
const scaleResult = await scaleResponse.json();
|
||||
console.log(`[RegionSelector] Scale result:`, scaleResult);
|
||||
|
||||
if (scaleResult.path) {
|
||||
// Carica l'immagine (originale o scalata) nel Region Selector
|
||||
bgImg.src = scaleResult.path;
|
||||
|
||||
// Salva i dati per il calcolo delle coordinate
|
||||
if (scaleResult.scaled) {
|
||||
bgImg.dataset.scaled = "true";
|
||||
bgImg.dataset.scaleFactor = scaleResult.scale || 1;
|
||||
console.log(`[RegionSelector] Immagine scalata caricata, scale factor: ${scaleResult.scale}`);
|
||||
} else {
|
||||
bgImg.dataset.scaled = "false";
|
||||
bgImg.dataset.scaleFactor = 1;
|
||||
console.log(`[RegionSelector] Immagine originale caricata (≤ 1024px)`);
|
||||
}
|
||||
} else if (scaleResult.error) {
|
||||
console.warn(`[RegionSelector] Scale error: ${scaleResult.error}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Could not auto-load image:", e);
|
||||
|
||||
Reference in New Issue
Block a user