From 3b103e993578977fec8a44bc0149a0722506b160 Mon Sep 17 00:00:00 2001 From: peijunlei Date: Tue, 1 Sep 2026 19:30:04 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=96=B0=E5=A2=9E=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E5=8D=A1=E7=89=87=E4=BA=A7=E5=93=81=E5=85=A5=E5=9C=BA?= =?UTF-8?q?=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/themes/dist_static/static/css/category.css | 20 ++++++++++++++++++++ public/themes/dist_static/static/js/category.js | 15 +++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/public/themes/dist_static/static/css/category.css b/public/themes/dist_static/static/css/category.css index c251b10..89a4663 100644 --- a/public/themes/dist_static/static/css/category.css +++ b/public/themes/dist_static/static/css/category.css @@ -162,6 +162,26 @@ display: none; } +@keyframes c-card-enter { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} +.s-product-card.is-entering { + animation: c-card-enter 0.35s ease both; + animation-delay: var(--enter-delay, 0ms); +} +@media (prefers-reduced-motion: reduce) { + .s-product-card.is-entering { + animation: none; + } +} + .c-empty { display: flex; align-items: center; diff --git a/public/themes/dist_static/static/js/category.js b/public/themes/dist_static/static/js/category.js index 39b1644..6f4a23d 100644 --- a/public/themes/dist_static/static/js/category.js +++ b/public/themes/dist_static/static/js/category.js @@ -65,6 +65,20 @@ function updateProductFilter(state) { grid.hidden = visibleCount === 0; empty.hidden = visibleCount > 0; } +function animateProductCards(state) { + const panel = document.querySelector(`[data-category-panel="${state.categoryId}"]`); + if (!panel) return; + const cards = [...panel.querySelectorAll(".s-product-card:not([hidden])")]; + cards.forEach((card) => { + card.classList.remove("is-entering"); + card.style.removeProperty("--enter-delay"); + }); + void panel.offsetWidth; + cards.forEach((card, index) => { + card.style.setProperty("--enter-delay", `${index * 50}ms`); + card.classList.add("is-entering"); + }); +} function updateBreadcrumb(state) { const name = getCategoryName(state.categoryId); const breadcrumb = document.querySelector("[data-breadcrumb-current]"); @@ -75,6 +89,7 @@ function updateView(state) { updateSidebar(state); updatePanels(state); updateProductFilter(state); + animateProductCards(state); updateBreadcrumb(state); } function bindEvents(state) {