1
0
mirror of https://github.com/sendevia/website.git synced 2026-03-06 07:40:50 +08:00

feat(ArticleMasonry): enhance card animations and accessibility

This commit is contained in:
2026-01-13 16:46:56 +08:00
parent 5b13683d2b
commit efea73968a
5 changed files with 62 additions and 21 deletions

View File

@@ -52,13 +52,12 @@ const masonryGroups = computed(() => {
});
/**
* 计算特定卡片在原始逻辑顺序中的位置
* 获取逻辑序号
* @param colIndex 列索引
* @param rowIndex 列内的行索引
* @returns {number} 用于 Tab 导航的顺序索引 (从 1 开始)
* @param rowIndex 行索引
*/
const getTabIndex = (colIndex: number, rowIndex: number): number => {
return rowIndex * columnCount.value + colIndex + 1;
const getLogicIndex = (colIndex: number, rowIndex: number): number => {
return rowIndex * columnCount.value + colIndex;
};
/**
@@ -82,10 +81,11 @@ const hasDownloadableContent = (item: PostData): boolean => {
<template>
<div class="ArticleMasonry">
<ClientOnly>
<div v-for="(column, index) in masonryGroups" :key="index" class="masonry-column">
<div v-for="(column, colIndex) in masonryGroups" :key="colIndex" class="masonry-column">
<MaterialCard
v-for="(item, itemIndex) in column"
v-for="(item, rowIndex) in column"
:key="item.id"
class="entrance"
variant="feed"
size="m"
color="outlined"
@@ -95,7 +95,8 @@ const hasDownloadableContent = (item: PostData): boolean => {
:date="item.date"
:impression="getArticleImage(item)"
:downloadable="hasDownloadableContent(item)"
:tabindex="getTabIndex(index, itemIndex)"
:tabindex="getLogicIndex(colIndex, rowIndex) + 1"
:style="{ '--delay': getLogicIndex(colIndex, rowIndex) }"
/>
</div>
</ClientOnly>

View File

@@ -24,3 +24,14 @@
opacity: 1;
}
}
@keyframes card-entrance {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0px);
}
}

View File

@@ -19,6 +19,3 @@
}
}
}
@media screen and (max-width: 840px) {
}

View File

@@ -8,7 +8,7 @@
text-decoration: none;
overflow: hidden;
outline: none;
.content {
display: flex;
@@ -19,6 +19,8 @@
height: 100%;
width: 100%;
overflow: hidden;
.impression-area {
z-index: 1;
}
@@ -28,6 +30,24 @@
}
}
&.entrance {
animation: card-entrance var(--md-sys-motion-spring-slow-spatial-duration) calc(min(var(--delay), 20) * 0.08s)
var(--md-sys-motion-spring-slow-spatial) forwards;
opacity: 0;
transform: translateY(30px);
will-change: transform, opacity;
}
&:focus-visible {
.content {
@include mixin.focus-ring($thickness: 4);
&::after {
background-color: var(--md-sys-state-focus-state-layer);
}
}
}
&.feed {
border-radius: var(--md-sys-shape-corner-large);
@@ -36,6 +56,8 @@
flex-direction: column;
gap: 12px;
border-radius: var(--md-sys-shape-corner-large);
.impression-area {
display: flex;
flex-direction: column-reverse;
@@ -103,14 +125,6 @@
}
}
&:hover {
&:has(img:nth-child(2)) {
img:nth-child(2) {
opacity: 1;
}
}
}
img {
position: absolute;
top: 0px;
@@ -162,6 +176,16 @@
text-decoration: none;
}
}
&:hover {
.impression-area .image-container {
&:has(img:nth-child(2)) {
img:nth-child(2) {
opacity: 1;
}
}
}
}
}
}
@@ -191,3 +215,11 @@
}
}
}
@media (prefers-reduced-motion: reduce) {
.MaterialCard.entrance {
animation: none;
opacity: 1;
transform: none;
}
}

View File

@@ -156,7 +156,7 @@ span {
}
.MaterialButton,
.MaterialCard,
.MaterialCard .content,
.PrevNext a {
&::after {
content: "";