fix: pile uses shared FRAME_PAD_RATIO/FRAME_BOTTOM_RATIO/FRAME_COLOR to match main, stronger sepia wash on pile
This commit is contained in:
+26
-62
@@ -6,6 +6,10 @@
|
||||
var selectedPersonId = null, isRunning = false, refreshTimer = null, urlDriven = false;
|
||||
var currentVideoPlaying = false;
|
||||
var pileCanvas, pileCtx;
|
||||
// Shared polaroid proportions (match main frame CSS: ~3% sides, ~10% bottom)
|
||||
var FRAME_PAD_RATIO = 0.03;
|
||||
var FRAME_BOTTOM_RATIO = 0.10;
|
||||
var FRAME_COLOR = '#ede8df';
|
||||
|
||||
var $setupScreen = document.getElementById('setup-screen'), $slideshowScreen = document.getElementById('slideshow-screen');
|
||||
var $connectionStatus = document.getElementById('connection-status'), $setupContent = document.getElementById('setup-content');
|
||||
@@ -66,73 +70,46 @@
|
||||
}
|
||||
|
||||
function clearPileCanvas() {
|
||||
if (pileCtx) {
|
||||
pileCtx.setTransform(1,0,0,1,0,0);
|
||||
pileCtx.clearRect(0, 0, pileCanvas.width, pileCanvas.height);
|
||||
pileCtx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1);
|
||||
}
|
||||
if (pileCtx) { pileCtx.setTransform(1,0,0,1,0,0); pileCtx.clearRect(0, 0, pileCanvas.width, pileCanvas.height); pileCtx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1); }
|
||||
}
|
||||
|
||||
// Animate a polaroid fading onto the pile canvas
|
||||
function dropPhotoPile(imgSrc) {
|
||||
var img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.onload = function () {
|
||||
var vw = window.innerWidth, vh = window.innerHeight;
|
||||
// Pile polaroid size: 18-25% of screen width
|
||||
var polaroidW = vw * (0.18 + Math.random() * 0.07);
|
||||
var padding = polaroidW * 0.04;
|
||||
var bottomPad = polaroidW * 0.12;
|
||||
var innerW = polaroidW - padding * 2;
|
||||
// Use shared proportions to match main frame
|
||||
var pad = polaroidW * FRAME_PAD_RATIO;
|
||||
var bottomPad = polaroidW * FRAME_BOTTOM_RATIO;
|
||||
var innerW = polaroidW - pad * 2;
|
||||
var innerH = innerW * (img.height / img.width);
|
||||
var totalH = innerH + padding + bottomPad;
|
||||
var cx = Math.random() * vw;
|
||||
var cy = Math.random() * vh;
|
||||
var totalH = innerH + pad + bottomPad;
|
||||
var cx = Math.random() * vw, cy = Math.random() * vh;
|
||||
var rot = (Math.random() - 0.5) * 30;
|
||||
|
||||
// Fade in over ~1 second
|
||||
var startTime = null;
|
||||
var fadeDuration = 1000;
|
||||
|
||||
var startTime = null, fadeDuration = 1200;
|
||||
function drawFrame(timestamp) {
|
||||
if (!startTime) startTime = timestamp;
|
||||
var elapsed = timestamp - startTime;
|
||||
var alpha = Math.min(elapsed / fadeDuration, 1);
|
||||
|
||||
// We need to redraw this specific polaroid with increasing alpha
|
||||
// To avoid clearing the whole canvas, we draw on a temp canvas and composite
|
||||
var alpha = Math.min((timestamp - startTime) / fadeDuration, 1);
|
||||
pileCtx.save();
|
||||
pileCtx.globalAlpha = alpha;
|
||||
pileCtx.translate(cx, cy);
|
||||
pileCtx.rotate(rot * Math.PI / 180);
|
||||
|
||||
// Shadow
|
||||
pileCtx.shadowColor = 'rgba(0,0,0,0.45)';
|
||||
pileCtx.shadowBlur = 18;
|
||||
pileCtx.shadowOffsetX = 3;
|
||||
pileCtx.shadowOffsetY = 6;
|
||||
|
||||
// White polaroid border
|
||||
pileCtx.fillStyle = '#ede8df';
|
||||
pileCtx.fillStyle = FRAME_COLOR;
|
||||
pileCtx.fillRect(-polaroidW/2, -totalH/2, polaroidW, totalH);
|
||||
|
||||
// Clear shadow for image
|
||||
pileCtx.shadowColor = 'transparent';
|
||||
pileCtx.shadowBlur = 0;
|
||||
pileCtx.shadowOffsetX = 0;
|
||||
pileCtx.shadowOffsetY = 0;
|
||||
|
||||
// Photo
|
||||
pileCtx.drawImage(img, -polaroidW/2 + padding, -totalH/2 + padding, innerW, innerH);
|
||||
|
||||
// Sepia/warm wash over the photo
|
||||
pileCtx.fillStyle = 'rgba(160, 130, 80, 0.15)';
|
||||
pileCtx.fillRect(-polaroidW/2 + padding, -totalH/2 + padding, innerW, innerH);
|
||||
|
||||
pileCtx.shadowColor = 'transparent'; pileCtx.shadowBlur = 0; pileCtx.shadowOffsetX = 0; pileCtx.shadowOffsetY = 0;
|
||||
pileCtx.drawImage(img, -polaroidW/2 + pad, -totalH/2 + pad, innerW, innerH);
|
||||
// Sepia wash
|
||||
pileCtx.fillStyle = 'rgba(150, 120, 70, 0.2)';
|
||||
pileCtx.fillRect(-polaroidW/2 + pad, -totalH/2 + pad, innerW, innerH);
|
||||
pileCtx.restore();
|
||||
|
||||
if (alpha < 1) {
|
||||
requestAnimationFrame(drawFrame);
|
||||
}
|
||||
if (alpha < 1) requestAnimationFrame(drawFrame);
|
||||
}
|
||||
requestAnimationFrame(drawFrame);
|
||||
};
|
||||
@@ -169,8 +146,7 @@
|
||||
isRunning=false;clearTimeout(slideshowTimer);if(refreshTimer)clearInterval(refreshTimer);stopVideo();
|
||||
$slideshowScreen.style.display='none';$setupScreen.style.display='flex';document.body.classList.add('setup-mode');
|
||||
$btnStart.textContent='▶ Start Slideshow';$btnStart.disabled=false;
|
||||
$bgBlur.style.backgroundImage='';$bgBlur.classList.remove('visible');$mainFrame.classList.remove('visible');
|
||||
clearPileCanvas();
|
||||
$bgBlur.style.backgroundImage='';$bgBlur.classList.remove('visible');$mainFrame.classList.remove('visible');clearPileCanvas();
|
||||
};
|
||||
|
||||
function showNextAsset() { currentIndex++;if(currentIndex>=assets.length){if(config.shuffle)shuffleArray(assets);currentIndex=0;}showAsset(currentIndex); }
|
||||
@@ -182,42 +158,30 @@
|
||||
var asset = assets[index], isVideo = asset.type === 'VIDEO';
|
||||
var thumbUrl = '/api/assets/' + asset.id + '/thumbnail?size=preview';
|
||||
console.log('[Frambe] Showing ' + (isVideo ? 'VIDEO' : 'PHOTO') + ': ' + (asset.originalFileName || asset.id));
|
||||
|
||||
// Drop previous photo onto pile
|
||||
if (currentIndex > 0) {
|
||||
var pi = currentIndex - 1; if (pi < 0) pi = assets.length - 1;
|
||||
if (assets[pi]) dropPhotoPile('/api/assets/' + assets[pi].id + '/thumbnail?size=thumbnail');
|
||||
}
|
||||
|
||||
// Fade out main
|
||||
if (currentIndex > 0) { var pi = currentIndex - 1; if (pi < 0) pi = assets.length - 1; if (assets[pi]) dropPhotoPile('/api/assets/' + assets[pi].id + '/thumbnail?size=thumbnail'); }
|
||||
$mainFrame.classList.remove('visible');
|
||||
|
||||
var img = new Image();
|
||||
img.onload = function () { setTimeout(function () { displayAsset(asset, thumbUrl, isVideo); }, 500); };
|
||||
img.onerror = function () { setTimeout(showNextAsset, 500); };
|
||||
img.src = thumbUrl;
|
||||
|
||||
var ni = index + 1; if (ni >= assets.length) ni = 0;
|
||||
if (assets[ni]) { var pre = new Image(); pre.src = '/api/assets/' + assets[ni].id + '/thumbnail?size=preview'; }
|
||||
}
|
||||
|
||||
function displayAsset(asset, thumbUrl, isVideo) {
|
||||
if (config.backgroundBlur) { $bgBlur.style.backgroundImage = 'url(' + thumbUrl + ')'; $bgBlur.classList.add('visible'); }
|
||||
|
||||
$mainVideo.style.display = 'none'; $mainPhoto.style.display = 'none';
|
||||
|
||||
if (isVideo) {
|
||||
$mainVideo.style.display = 'block';
|
||||
$mainVideo.src = '/api/assets/' + asset.id + '/video'; $mainVideo.poster = thumbUrl;
|
||||
$mainVideo.load();
|
||||
$mainVideo.play().then(function(){ currentVideoPlaying=true; console.log('[Frambe] Video playing'); }).catch(function(e){ console.warn('[Frambe] Video autoplay failed: '+e.message); });
|
||||
$mainVideo.onended = function () { console.log('[Frambe] Video ended'); currentVideoPlaying=false; showNextAsset(); };
|
||||
slideshowTimer = setTimeout(function(){ if(currentVideoPlaying){console.log('[Frambe] Video timeout');showNextAsset();} }, Math.max((config.slideshowInterval||30)*3, 120)*1000);
|
||||
$mainVideo.play().then(function(){ currentVideoPlaying=true; }).catch(function(e){ console.warn('[Frambe] Video autoplay failed: '+e.message); });
|
||||
$mainVideo.onended = function () { currentVideoPlaying=false; showNextAsset(); };
|
||||
slideshowTimer = setTimeout(function(){ if(currentVideoPlaying){showNextAsset();} }, Math.max((config.slideshowInterval||30)*3, 120)*1000);
|
||||
} else {
|
||||
$mainPhoto.style.display = 'block'; $mainPhoto.src = thumbUrl;
|
||||
slideshowTimer = setTimeout(showNextAsset, (config.slideshowInterval||30)*1000);
|
||||
}
|
||||
|
||||
requestAnimationFrame(function () { $mainFrame.classList.add('visible'); });
|
||||
updateExifInfo(asset); startProgress(isVideo ? null : (config.slideshowInterval||30)*1000);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user