mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 13:02:59 +00:00
web preview display improvements
This commit is contained in:
@@ -141,6 +141,16 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.led-canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.display-image {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
@@ -731,6 +741,7 @@
|
||||
<div id="previewStage" class="preview-stage" style="display:none;">
|
||||
<div id="previewMeta" style="position:absolute; top:-28px; left:0; color:#ddd; font-size:12px; opacity:0.85;"></div>
|
||||
<img id="displayImage" class="display-image" alt="LED Matrix Display">
|
||||
<canvas id="ledCanvas" class="led-canvas" style="display:none;"></canvas>
|
||||
<canvas id="gridOverlay" class="grid-overlay"></canvas>
|
||||
</div>
|
||||
<div id="displayPlaceholder" style="color: #666; font-size: 1.2rem;">
|
||||
@@ -762,6 +773,15 @@
|
||||
<input type="checkbox" id="toggleGrid">
|
||||
Show pixel grid
|
||||
</label>
|
||||
<label style="color:#333; background:#f3f3f3; padding:6px 10px; border-radius:8px; display:inline-flex; align-items:center; gap:8px;">
|
||||
<input type="checkbox" id="toggleLedDots">
|
||||
LED dot mode
|
||||
</label>
|
||||
<label style="color:#333; background:#f3f3f3; padding:6px 10px; border-radius:8px; display:inline-flex; align-items:center; gap:8px;">
|
||||
Dot fill
|
||||
<input type="range" id="dotFillRange" min="40" max="95" value="75">
|
||||
<span id="dotFillValue">75%</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1778,6 +1798,7 @@
|
||||
gridCanvas.width = data.width * scale;
|
||||
gridCanvas.height = data.height * scale;
|
||||
drawGrid(gridCanvas, data.width, data.height, scale);
|
||||
renderLedDots();
|
||||
})
|
||||
.catch(() => {});
|
||||
});
|
||||
@@ -1801,6 +1822,20 @@
|
||||
// default hidden
|
||||
gridCanvas.style.display = 'none';
|
||||
}
|
||||
|
||||
// LED dot mode controls
|
||||
const toggleLedDots = document.getElementById('toggleLedDots');
|
||||
const dotFillRange = document.getElementById('dotFillRange');
|
||||
const dotFillValue = document.getElementById('dotFillValue');
|
||||
if (dotFillRange && dotFillValue) {
|
||||
dotFillRange.addEventListener('input', () => {
|
||||
dotFillValue.textContent = `${dotFillRange.value}%`;
|
||||
renderLedDots();
|
||||
});
|
||||
}
|
||||
if (toggleLedDots) {
|
||||
toggleLedDots.addEventListener('change', renderLedDots);
|
||||
}
|
||||
|
||||
// Update stats every 30 seconds
|
||||
setInterval(updateSystemStats, 30000);
|
||||
@@ -1914,6 +1949,7 @@
|
||||
const stage = document.getElementById('previewStage');
|
||||
const img = document.getElementById('displayImage');
|
||||
const canvas = document.getElementById('gridOverlay');
|
||||
const ledCanvas = document.getElementById('ledCanvas');
|
||||
const placeholder = document.getElementById('displayPlaceholder');
|
||||
|
||||
if (data.image) {
|
||||
@@ -1937,9 +1973,12 @@
|
||||
const height = (data.height || 32) * scale;
|
||||
img.style.width = width + 'px';
|
||||
img.style.height = height + 'px';
|
||||
ledCanvas.width = width;
|
||||
ledCanvas.height = height;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
drawGrid(canvas, data.width || 128, data.height || 32, scale);
|
||||
renderLedDots();
|
||||
} else {
|
||||
stage.style.display = 'none';
|
||||
placeholder.style.display = 'block';
|
||||
@@ -1950,6 +1989,54 @@
|
||||
}
|
||||
}
|
||||
|
||||
function renderLedDots(){
|
||||
const ledCanvas = document.getElementById('ledCanvas');
|
||||
const img = document.getElementById('displayImage');
|
||||
const toggle = document.getElementById('toggleLedDots');
|
||||
if (!ledCanvas || !img || !toggle) return;
|
||||
const show = toggle.checked;
|
||||
ledCanvas.style.display = show ? 'block' : 'none';
|
||||
if (!show) return;
|
||||
|
||||
const scale = parseInt(document.getElementById('scaleRange').value || '8');
|
||||
const fillPct = parseInt(document.getElementById('dotFillRange').value || '75');
|
||||
const dotRadius = Math.max(1, Math.floor((scale * fillPct) / 200)); // radius in px
|
||||
const ctx = ledCanvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, ledCanvas.width, ledCanvas.height);
|
||||
|
||||
// Draw black background (gaps between LEDs)
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillRect(0, 0, ledCanvas.width, ledCanvas.height);
|
||||
|
||||
// Create an offscreen canvas to sample pixel colors
|
||||
const off = document.createElement('canvas');
|
||||
const logicalWidth = Math.floor(ledCanvas.width / scale);
|
||||
const logicalHeight = Math.floor(ledCanvas.height / scale);
|
||||
off.width = logicalWidth;
|
||||
off.height = logicalHeight;
|
||||
const offCtx = off.getContext('2d');
|
||||
// Draw the current image scaled down to logical LEDs to sample colors
|
||||
try {
|
||||
offCtx.drawImage(img, 0, 0, logicalWidth, logicalHeight);
|
||||
} catch (_) { /* draw failures ignored */ }
|
||||
|
||||
// Draw circular dots for each LED pixel
|
||||
for (let y = 0; y < logicalHeight; y++) {
|
||||
for (let x = 0; x < logicalWidth; x++) {
|
||||
const pixel = offCtx.getImageData(x, y, 1, 1).data;
|
||||
const r = pixel[0], g = pixel[1], b = pixel[2], a = pixel[3];
|
||||
// Skip fully black to reduce overdraw
|
||||
if (a === 0 || (r|g|b) === 0) continue;
|
||||
ctx.fillStyle = `rgb(${r},${g},${b})`;
|
||||
const cx = Math.floor(x * scale + scale / 2);
|
||||
const cy = Math.floor(y * scale + scale / 2);
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, dotRadius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tab functionality
|
||||
function showTab(tabName) {
|
||||
// Hide all tab contents
|
||||
|
||||
Reference in New Issue
Block a user