修复前端瀑布流动画
All checks were successful
Dart CI / build (push) Successful in 49s

This commit is contained in:
2026-04-04 21:53:11 +08:00
parent 02a4ea3f4e
commit 7ff63d023e
2 changed files with 29 additions and 11 deletions

View File

@@ -7,10 +7,11 @@
* @property {(photoId: number, previewSrc: string) => void} [onUpgradeQuality]
* @property {boolean} [borderLess]
* @property {boolean} [waterfall]
* @property {number} [index]
*/
/** @type {PhotoCardProps} */
let { photo, onUpgradeQuality, borderLess = false, waterfall = false } = $props();
let { photo, onUpgradeQuality, borderLess = false, waterfall = false, index = 0 } = $props();
// 使用 $derived 确保响应式更新
let previewSrc = $derived(`/api/v1/photo/${photo.id}/preview`);
@@ -18,10 +19,13 @@
// 根据原始宽高比计算瀑布流显示比例,防止图片加载前容器塌陷
let aspectRatioStyle = $derived(
waterfall && photo.width && photo.height
? `aspect-ratio: ${photo.width} / ${photo.height};`
? `${photo.width} / ${photo.height}`
: ''
);
// 砖块模式:交错延迟(按网格行顺序);瀑布模式:同时入场(避免列填充顺序与延迟不匹配)
let animationDelay = $derived(waterfall ? '0ms' : `${Math.min(index, 20) * 35}ms`);
function handleMouseEnter() {
if (onUpgradeQuality) {
onUpgradeQuality(photo.id, previewSrc);
@@ -35,8 +39,8 @@
}
</script>
<a href={resolve(`/photo/${photo.id}`)} class="photo-card" class:photo-card-waterfall={waterfall} class:photo-card-compact-waterfall={borderLess && waterfall}>
<div class="photo-wrapper" class:photo-borderless={borderLess} class:photo-waterfall={waterfall} style={aspectRatioStyle}>
<a href={resolve(`/photo/${photo.id}`)} class="photo-card" class:photo-card-waterfall={waterfall} class:photo-card-compact-waterfall={borderLess && waterfall} style={`--wf-aspect: ${aspectRatioStyle}; --wf-delay: ${animationDelay};`}>
<div class="photo-wrapper" class:photo-borderless={borderLess} class:photo-waterfall={waterfall}>
{#if photo.mimeType?.startsWith('video/')}
<div class="video-indicator">🎬</div>
<div class="photo-placeholder">
@@ -65,6 +69,19 @@
display: block;
text-decoration: none;
color: inherit;
animation: card-enter 0.35s cubic-bezier(0.4, 0, 0.2, 1) both;
animation-delay: var(--wf-delay, 0ms);
}
@keyframes card-enter {
from {
opacity: 0;
transform: translateY(12px) scale(0.96);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* 瀑布流模式:防止卡片被列截断,并添加间距 */
@@ -97,7 +114,7 @@
/* 瀑布流模式:保持原始宽高比 */
.photo-waterfall {
aspect-ratio: auto;
aspect-ratio: var(--wf-aspect, auto);
height: auto;
}
@@ -136,7 +153,6 @@
/* 瀑布流模式下 placeholder 默认 4:3 */
.photo-waterfall .photo-placeholder {
aspect-ratio: 4/3;
height: 0;
}
.video-indicator {

View File

@@ -40,11 +40,13 @@
<Empty message={m.no_photos()} icon="📷" />
{:else}
<div class="photo-scroll-container" bind:this={scrollContainer}>
{#key waterfall + '_' + borderLess}
<div class="photo-grid" class:photo-grid-borderless={borderLess} class:photo-grid-waterfall={waterfall}>
{#each getVisiblePhotos() as photo (photo.id)}
<PhotoCard {photo} {onUpgradeQuality} {borderLess} {waterfall} />
{#each getVisiblePhotos() as photo, i (photo.id)}
<PhotoCard {photo} {onUpgradeQuality} {borderLess} {waterfall} index={i} />
{/each}
</div>
{/key}
{#if isLoading}
<div class="loading-trigger">