feat: enhance photo loading with color placeholders
- Use dominant color as placeholder background while images load - Add aspect ratio support for proper image dimensions - Improve loading state with smoother transitions - Remove shimmer animation in favor of solid color placeholders 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
90b450324b
commit
aa0677090b
1 changed files with 27 additions and 33 deletions
|
|
@ -37,6 +37,16 @@
|
||||||
|
|
||||||
const photo = $derived(isAlbum(item) ? item.coverPhoto : item)
|
const photo = $derived(isAlbum(item) ? item.coverPhoto : item)
|
||||||
const isAlbumItem = $derived(isAlbum(item))
|
const isAlbumItem = $derived(isAlbum(item))
|
||||||
|
const placeholderStyle = $derived(
|
||||||
|
photo.dominantColor
|
||||||
|
? `background: ${photo.dominantColor}`
|
||||||
|
: ''
|
||||||
|
)
|
||||||
|
const aspectRatioStyle = $derived(
|
||||||
|
photo.aspectRatio
|
||||||
|
? `aspect-ratio: ${photo.aspectRatio}`
|
||||||
|
: ''
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="photo-item" class:is-album={isAlbumItem}>
|
<div class="photo-item" class:is-album={isAlbumItem}>
|
||||||
|
|
@ -46,7 +56,7 @@
|
||||||
<div class="album-stack">
|
<div class="album-stack">
|
||||||
<div class="stack-photo stack-back"></div>
|
<div class="stack-photo stack-back"></div>
|
||||||
<div class="stack-photo stack-middle"></div>
|
<div class="stack-photo stack-middle"></div>
|
||||||
<div class="stack-photo stack-front">
|
<div class="stack-photo stack-front" style={aspectRatioStyle}>
|
||||||
<img
|
<img
|
||||||
src={photo.src}
|
src={photo.src}
|
||||||
alt={photo.alt}
|
alt={photo.alt}
|
||||||
|
|
@ -55,9 +65,7 @@
|
||||||
onload={handleImageLoad}
|
onload={handleImageLoad}
|
||||||
class:loaded={imageLoaded}
|
class:loaded={imageLoaded}
|
||||||
/>
|
/>
|
||||||
{#if !imageLoaded}
|
<div class="image-placeholder" style={placeholderStyle} class:loaded={imageLoaded}></div>
|
||||||
<div class="image-placeholder"></div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="album-overlay">
|
<div class="album-overlay">
|
||||||
<div class="album-info">
|
<div class="album-info">
|
||||||
|
|
@ -68,7 +76,7 @@
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Single photo -->
|
<!-- Single photo -->
|
||||||
<div class="single-photo">
|
<div class="single-photo" style={aspectRatioStyle}>
|
||||||
<img
|
<img
|
||||||
src={photo.src}
|
src={photo.src}
|
||||||
alt={photo.alt}
|
alt={photo.alt}
|
||||||
|
|
@ -77,9 +85,7 @@
|
||||||
onload={handleImageLoad}
|
onload={handleImageLoad}
|
||||||
class:loaded={imageLoaded}
|
class:loaded={imageLoaded}
|
||||||
/>
|
/>
|
||||||
{#if !imageLoaded}
|
<div class="image-placeholder" style={placeholderStyle} class:loaded={imageLoaded}></div>
|
||||||
<div class="image-placeholder"></div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -129,6 +135,8 @@
|
||||||
border-radius: $corner-radius;
|
border-radius: $corner-radius;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.4s ease;
|
transition: opacity 0.4s ease;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
&.loaded {
|
&.loaded {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
@ -177,6 +185,8 @@
|
||||||
border-radius: $corner-radius;
|
border-radius: $corner-radius;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.4s ease;
|
transition: opacity 0.4s ease;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
&.loaded {
|
&.loaded {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
@ -232,34 +242,18 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: linear-gradient(135deg, #f0f0f0 0%, #e0e0e0 50%, #f0f0f0 100%);
|
background: #f0f0f0; // Lighter default grey
|
||||||
background-size: 200% 200%;
|
|
||||||
animation: shimmer 1.5s ease-in-out infinite;
|
|
||||||
border-radius: $corner-radius;
|
border-radius: $corner-radius;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.4s ease;
|
||||||
|
z-index: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
&.loaded {
|
||||||
position: absolute;
|
opacity: 0;
|
||||||
top: 50%;
|
pointer-events: none;
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
border-radius: 50%;
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='1.5'%3E%3Crect x='3' y='3' width='18' height='18' rx='2' ry='2'/%3E%3Ccircle cx='8.5' cy='8.5' r='1.5'/%3E%3Cpolyline points='21,15 16,10 5,21'/%3E%3C/svg%3E");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
background-size: 24px 24px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes shimmer {
|
|
||||||
0% {
|
|
||||||
background-position: 200% 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
background-position: -200% 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue