fix: pile photos fade in with sepia wash, bg transitions smoothly, drift is single-direction
This commit is contained in:
+52
-34
@@ -5,7 +5,7 @@
|
|||||||
var overlayVisible = true, overlayTimeout = null, selectedSource = null, selectedAlbumId = null;
|
var overlayVisible = true, overlayTimeout = null, selectedSource = null, selectedAlbumId = null;
|
||||||
var selectedPersonId = null, isRunning = false, refreshTimer = null, urlDriven = false;
|
var selectedPersonId = null, isRunning = false, refreshTimer = null, urlDriven = false;
|
||||||
var currentVideoPlaying = false;
|
var currentVideoPlaying = false;
|
||||||
var pileCanvas, pileCtx; // canvas for accumulated polaroid pile
|
var pileCanvas, pileCtx;
|
||||||
|
|
||||||
var $setupScreen = document.getElementById('setup-screen'), $slideshowScreen = document.getElementById('slideshow-screen');
|
var $setupScreen = document.getElementById('setup-screen'), $slideshowScreen = document.getElementById('slideshow-screen');
|
||||||
var $connectionStatus = document.getElementById('connection-status'), $setupContent = document.getElementById('setup-content');
|
var $connectionStatus = document.getElementById('connection-status'), $setupContent = document.getElementById('setup-content');
|
||||||
@@ -56,12 +56,13 @@
|
|||||||
// =========================
|
// =========================
|
||||||
function initPileCanvas() {
|
function initPileCanvas() {
|
||||||
pileCanvas = document.getElementById('pile-canvas');
|
pileCanvas = document.getElementById('pile-canvas');
|
||||||
pileCanvas.width = window.innerWidth * (window.devicePixelRatio || 1);
|
var dpr = window.devicePixelRatio || 1;
|
||||||
pileCanvas.height = window.innerHeight * (window.devicePixelRatio || 1);
|
pileCanvas.width = window.innerWidth * dpr;
|
||||||
|
pileCanvas.height = window.innerHeight * dpr;
|
||||||
pileCanvas.style.width = window.innerWidth + 'px';
|
pileCanvas.style.width = window.innerWidth + 'px';
|
||||||
pileCanvas.style.height = window.innerHeight + 'px';
|
pileCanvas.style.height = window.innerHeight + 'px';
|
||||||
pileCtx = pileCanvas.getContext('2d');
|
pileCtx = pileCanvas.getContext('2d');
|
||||||
pileCtx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1);
|
pileCtx.scale(dpr, dpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearPileCanvas() {
|
function clearPileCanvas() {
|
||||||
@@ -72,49 +73,68 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animate a polaroid fading onto the pile canvas
|
||||||
function dropPhotoPile(imgSrc) {
|
function dropPhotoPile(imgSrc) {
|
||||||
var img = new Image();
|
var img = new Image();
|
||||||
img.crossOrigin = 'anonymous';
|
img.crossOrigin = 'anonymous';
|
||||||
img.onload = function () {
|
img.onload = function () {
|
||||||
var vw = window.innerWidth, vh = window.innerHeight;
|
var vw = window.innerWidth, vh = window.innerHeight;
|
||||||
// Polaroid size: ~18-25% of screen width
|
|
||||||
var polaroidW = vw * (0.18 + Math.random() * 0.07);
|
var polaroidW = vw * (0.18 + Math.random() * 0.07);
|
||||||
var padding = polaroidW * 0.04;
|
var padding = polaroidW * 0.04;
|
||||||
var bottomPad = polaroidW * 0.12;
|
var bottomPad = polaroidW * 0.12;
|
||||||
var innerW = polaroidW - padding * 2;
|
var innerW = polaroidW - padding * 2;
|
||||||
var innerH = innerW * (img.height / img.width);
|
var innerH = innerW * (img.height / img.width);
|
||||||
var totalH = innerH + padding + bottomPad;
|
var totalH = innerH + padding + bottomPad;
|
||||||
|
|
||||||
// Random position across the screen
|
|
||||||
var cx = Math.random() * vw;
|
var cx = Math.random() * vw;
|
||||||
var cy = Math.random() * vh;
|
var cy = Math.random() * vh;
|
||||||
var rot = (Math.random() - 0.5) * 30; // -15 to +15 degrees
|
var rot = (Math.random() - 0.5) * 30;
|
||||||
|
|
||||||
pileCtx.save();
|
// Fade in over ~1 second
|
||||||
pileCtx.translate(cx, cy);
|
var startTime = null;
|
||||||
pileCtx.rotate(rot * Math.PI / 180);
|
var fadeDuration = 1000;
|
||||||
|
|
||||||
// Shadow
|
function drawFrame(timestamp) {
|
||||||
pileCtx.shadowColor = 'rgba(0,0,0,0.5)';
|
if (!startTime) startTime = timestamp;
|
||||||
pileCtx.shadowBlur = 15;
|
var elapsed = timestamp - startTime;
|
||||||
pileCtx.shadowOffsetX = 3;
|
var alpha = Math.min(elapsed / fadeDuration, 1);
|
||||||
pileCtx.shadowOffsetY = 5;
|
|
||||||
|
|
||||||
// White polaroid border
|
// We need to redraw this specific polaroid with increasing alpha
|
||||||
pileCtx.fillStyle = '#f0ebe3';
|
// To avoid clearing the whole canvas, we draw on a temp canvas and composite
|
||||||
pileCtx.fillRect(-polaroidW/2, -totalH/2, polaroidW, totalH);
|
pileCtx.save();
|
||||||
|
pileCtx.globalAlpha = alpha;
|
||||||
|
pileCtx.translate(cx, cy);
|
||||||
|
pileCtx.rotate(rot * Math.PI / 180);
|
||||||
|
|
||||||
// Clear shadow for the image
|
// Shadow
|
||||||
pileCtx.shadowColor = 'transparent';
|
pileCtx.shadowColor = 'rgba(0,0,0,0.45)';
|
||||||
pileCtx.shadowBlur = 0;
|
pileCtx.shadowBlur = 18;
|
||||||
pileCtx.shadowOffsetX = 0;
|
pileCtx.shadowOffsetX = 3;
|
||||||
pileCtx.shadowOffsetY = 0;
|
pileCtx.shadowOffsetY = 6;
|
||||||
|
|
||||||
// Photo inside
|
// White polaroid border
|
||||||
pileCtx.drawImage(img, -polaroidW/2 + padding, -totalH/2 + padding, innerW, innerH);
|
pileCtx.fillStyle = '#ede8df';
|
||||||
|
pileCtx.fillRect(-polaroidW/2, -totalH/2, polaroidW, totalH);
|
||||||
|
|
||||||
pileCtx.restore();
|
// Clear shadow for image
|
||||||
console.log('[Frambe] Dropped polaroid onto pile');
|
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.restore();
|
||||||
|
|
||||||
|
if (alpha < 1) {
|
||||||
|
requestAnimationFrame(drawFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestAnimationFrame(drawFrame);
|
||||||
};
|
};
|
||||||
img.onerror = function () { console.warn('[Frambe] Pile image failed to load'); };
|
img.onerror = function () { console.warn('[Frambe] Pile image failed to load'); };
|
||||||
img.src = imgSrc;
|
img.src = imgSrc;
|
||||||
@@ -163,21 +183,20 @@
|
|||||||
var thumbUrl = '/api/assets/' + asset.id + '/thumbnail?size=preview';
|
var thumbUrl = '/api/assets/' + asset.id + '/thumbnail?size=preview';
|
||||||
console.log('[Frambe] Showing ' + (isVideo ? 'VIDEO' : 'PHOTO') + ': ' + (asset.originalFileName || asset.id));
|
console.log('[Frambe] Showing ' + (isVideo ? 'VIDEO' : 'PHOTO') + ': ' + (asset.originalFileName || asset.id));
|
||||||
|
|
||||||
// Drop previous photo onto the pile canvas
|
// Drop previous photo onto pile
|
||||||
if (currentIndex > 0 || pileCanvas) {
|
if (currentIndex > 0) {
|
||||||
var pi = currentIndex - 1; if (pi < 0) pi = assets.length - 1;
|
var pi = currentIndex - 1; if (pi < 0) pi = assets.length - 1;
|
||||||
if (assets[pi]) { dropPhotoPile('/api/assets/' + assets[pi].id + '/thumbnail?size=thumbnail'); }
|
if (assets[pi]) dropPhotoPile('/api/assets/' + assets[pi].id + '/thumbnail?size=thumbnail');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fade out main
|
// Fade out main
|
||||||
$mainFrame.classList.remove('visible');
|
$mainFrame.classList.remove('visible');
|
||||||
|
|
||||||
var img = new Image();
|
var img = new Image();
|
||||||
img.onload = function () { setTimeout(function () { displayAsset(asset, thumbUrl, isVideo); }, 400); };
|
img.onload = function () { setTimeout(function () { displayAsset(asset, thumbUrl, isVideo); }, 500); };
|
||||||
img.onerror = function () { setTimeout(showNextAsset, 500); };
|
img.onerror = function () { setTimeout(showNextAsset, 500); };
|
||||||
img.src = thumbUrl;
|
img.src = thumbUrl;
|
||||||
|
|
||||||
// Preload next
|
|
||||||
var ni = index + 1; if (ni >= assets.length) ni = 0;
|
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'; }
|
if (assets[ni]) { var pre = new Image(); pre.src = '/api/assets/' + assets[ni].id + '/thumbnail?size=preview'; }
|
||||||
}
|
}
|
||||||
@@ -185,7 +204,6 @@
|
|||||||
function displayAsset(asset, thumbUrl, isVideo) {
|
function displayAsset(asset, thumbUrl, isVideo) {
|
||||||
if (config.backgroundBlur) { $bgBlur.style.backgroundImage = 'url(' + thumbUrl + ')'; $bgBlur.classList.add('visible'); }
|
if (config.backgroundBlur) { $bgBlur.style.backgroundImage = 'url(' + thumbUrl + ')'; $bgBlur.classList.add('visible'); }
|
||||||
|
|
||||||
// Always polaroid style
|
|
||||||
$mainVideo.style.display = 'none'; $mainPhoto.style.display = 'none';
|
$mainVideo.style.display = 'none'; $mainPhoto.style.display = 'none';
|
||||||
|
|
||||||
if (isVideo) {
|
if (isVideo) {
|
||||||
|
|||||||
Reference in New Issue
Block a user