feat: add stagger entry animation to showcase on load and shuffle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+14
-13
@@ -4,8 +4,6 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Use shared TagUtils module
|
||||
const { formatTagHtml, createTagChipHtml } = window.TagUtils || {};
|
||||
|
||||
const container = document.getElementById('showcaseGrid');
|
||||
if (!container) return;
|
||||
@@ -59,7 +57,7 @@
|
||||
|
||||
try {
|
||||
const images = JSON.parse(dataScript.textContent);
|
||||
distributeImages(images, true); // Use lazy loading for initial images (some may be below fold)
|
||||
distributeImages(images, true, true); // animate on initial load
|
||||
} catch (e) {
|
||||
console.error('Failed to parse initial images:', e);
|
||||
}
|
||||
@@ -174,20 +172,23 @@
|
||||
return item;
|
||||
}
|
||||
|
||||
function distributeImages(images, useLazyLoading = false) {
|
||||
images.forEach(img => {
|
||||
function distributeImages(images, useLazyLoading = false, animate = false) {
|
||||
images.forEach((img, i) => {
|
||||
const colIndex = getShortestColumnIndex();
|
||||
const item = createImageElement(img, useLazyLoading);
|
||||
|
||||
if (animate) {
|
||||
item.style.setProperty('--stagger-index', i);
|
||||
item.classList.add('animating-in');
|
||||
// Remove class after animation completes so it doesn't block interactions
|
||||
const delay = i * 60;
|
||||
setTimeout(() => item.classList.remove('animating-in'), delay + 300);
|
||||
}
|
||||
|
||||
columns[colIndex].appendChild(item);
|
||||
|
||||
// Update height estimate (will be corrected after image loads)
|
||||
columnHeights[colIndex] += 300;
|
||||
|
||||
// Correct height after image loads
|
||||
const imgEl = item.querySelector('img');
|
||||
imgEl.onload = () => {
|
||||
updateColumnHeights();
|
||||
};
|
||||
imgEl.onload = () => { updateColumnHeights(); };
|
||||
});
|
||||
|
||||
document.dispatchEvent(new CustomEvent('showcaseUpdated'));
|
||||
@@ -236,7 +237,7 @@
|
||||
columns.forEach(col => col.innerHTML = '');
|
||||
columnHeights = columnHeights.map(() => 0);
|
||||
|
||||
distributeImages(data.images);
|
||||
distributeImages(data.images, false, true); // animate on shuffle
|
||||
|
||||
// Scroll to top
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
|
||||
@@ -1394,6 +1394,24 @@ header {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
/* Stagger entry animation for showcase items */
|
||||
@keyframes itemFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(12px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.masonry-item.animating-in {
|
||||
animation: itemFadeIn 0.25s ease forwards;
|
||||
animation-delay: calc(var(--stagger-index, 0) * 60ms);
|
||||
opacity: 0; /* Start hidden until animation fires */
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
Forms
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
Reference in New Issue
Block a user