From c1ec88daf998707222ff56ce9b24be61f2bea201 Mon Sep 17 00:00:00 2001 From: GJ Date: Mon, 21 Sep 2026 10:58:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(short-drama-agent-front):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=9B=BE=E7=89=87=E5=8A=A0=E8=BD=BD=E5=8A=A8=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/components/ui/RevealImage.vue | 146 +++++++++---------------- tests/components/ui/AssetImage.test.ts | 18 ++- 3 files changed, 62 insertions(+), 103 deletions(-) diff --git a/.gitignore b/.gitignore index 9f11a7d..48541ce 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ coverage *.tsbuildinfo .DS_Store .worktree/ +.worktrees/ diff --git a/src/components/ui/RevealImage.vue b/src/components/ui/RevealImage.vue index 89ade59..88360c3 100644 --- a/src/components/ui/RevealImage.vue +++ b/src/components/ui/RevealImage.vue @@ -1,71 +1,60 @@ @@ -95,53 +83,27 @@ onBeforeUnmount(() => clearTimeout(settleTimer)) } .reveal-image > .n-image img { opacity: 0; - filter: blur(9px); - transform: scale(1.025); - transition: - opacity 280ms ease, - filter 620ms cubic-bezier(0.22, 1, 0.36, 1), - transform 620ms cubic-bezier(0.22, 1, 0.36, 1); + transition: opacity 220ms cubic-bezier(0.25, 1, 0.5, 1); } .reveal-image.is-loaded > .n-image img { opacity: 1; - filter: blur(0); - transform: scale(1); } -.reveal-image-grid { - @apply absolute inset-0 grid grid-cols-4 grid-rows-4 pointer-events-none; - gap: 1px; - background: var(--app-body); -} -.reveal-image-cell { +.reveal-image-placeholder { + @apply absolute inset-0 pointer-events-none; background-color: var(--app-control); - background-repeat: no-repeat; - background-size: 400% 400%; - opacity: 1; - transform: scale(1.01); - transition: - opacity 420ms cubic-bezier(0.4, 0, 0.2, 1), - transform 520ms cubic-bezier(0.22, 1, 0.36, 1); - transition-delay: calc(var(--reveal-order) * 18ms); + animation: reveal-image-pulse 1.2s cubic-bezier(0.65, 0, 0.35, 1) infinite alternate; } -.reveal-image:not(.is-loaded) .reveal-image-cell { - animation: reveal-image-wait 1.8s ease-in-out infinite alternate; - animation-delay: calc(var(--reveal-order) * -55ms); -} -.reveal-image.is-loaded .reveal-image-cell { - opacity: 0; - transform: scale(0.88); -} -@keyframes reveal-image-wait { +@keyframes reveal-image-pulse { from { - filter: brightness(0.96); + opacity: 0.72; } to { - filter: brightness(1.08); + opacity: 1; } } @media (prefers-reduced-motion: reduce) { .reveal-image > .n-image img, - .reveal-image-cell { + .reveal-image-placeholder { animation: none; transition: none; } diff --git a/tests/components/ui/AssetImage.test.ts b/tests/components/ui/AssetImage.test.ts index 3cd1be7..457d958 100644 --- a/tests/components/ui/AssetImage.test.ts +++ b/tests/components/ui/AssetImage.test.ts @@ -33,19 +33,15 @@ describe('资源图片展示与原图预览', () => { expect(wrapper.get('img').element.style.objectFit).toBe('contain') }) - it('图片加载完成后按网格逐块显现,并在动画结束后移除覆盖层', async () => { - vi.useFakeTimers() - vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) => { - callback(0) - return 1 - }) - wrapper = mount(AssetImage, { props: { src: '/storage/reveal.png', alt: '加载动效图片' } }) - expect(wrapper.findAll('.reveal-image-cell')).toHaveLength(16) + it('图片加载前显示轻量骨架占位,加载完成后直接淡入图片并移除占位', async () => { + wrapper = mount(AssetImage, { props: { src: '/storage/reveal.png', alt: '加载中的图片' } }) + expect(wrapper.get('.reveal-image').attributes('aria-busy')).toBe('true') + expect(wrapper.find('.reveal-image-placeholder').exists()).toBe(true) + expect(wrapper.find('.reveal-image-grid').exists()).toBe(false) await wrapper.get('img').trigger('load') expect(wrapper.get('.reveal-image').classes()).toContain('is-loaded') - vi.advanceTimersByTime(760) - await wrapper.vm.$nextTick() - expect(wrapper.find('.reveal-image-grid').exists()).toBe(false) + expect(wrapper.get('.reveal-image').attributes('aria-busy')).toBe('false') + expect(wrapper.find('.reveal-image-placeholder').exists()).toBe(false) }) it('cover 仅裁切卡片,点击使用 Naive 原生预览展示同一完整资源,遮罩可关闭', async () => {