精选模块 · 轮播滑块(carousel-slide)详解
本文档详细说明首页“文章 · 精选模块”的轮播滑块实现与使用方式,涵盖设计理念、目录结构、数据与行为、API、样式、自定义与最佳实践。
设计目标
- 突出高质量内容:基于文章
recommend
值筛选与排序。 - 占用更少空间:采用纵向单卡片轮播(vertical slide)。
- 读取友好:4s 自动切换,支持鼠标悬停暂停。
- 极简美观:只呈现“标题 + 简介”,搭配轻量玻璃拟态与细腻动效。
- 开箱即用:已封装懒加载包装组件,可直接放入页面。
目录结构
app/components/widget/FeaturedArticlesCarousel.vue
:轮播主体组件(本模块核心)。app/components/widget/LazyWidgetFeaturedArticlesCarousel.vue
:懒加载包装组件。app/composables/useFeaturedArticles.ts
:精选文章数据获取与变体函数。
核心代码
1. 轮播主体组件 (FeaturedArticlesCarousel.vue
)
关键逻辑:
<script setup lang="ts"> const { data: featuredArticles } = await useFeaturedArticles(5, 1) const currentIndex = ref(0) const isAutoPlay = ref(true) const isTransitioning = ref(true) const nextSlide = () => { currentIndex.value = currentIndex.value + 1 if (currentIndex.value >= processedArticles.value.length) { setTimeout(() => { isTransitioning.value = false currentIndex.value = 0 setTimeout(() => { isTransitioning.value = true }, 50) }, 2200) } } </script> <template> <div class="carousel-track" :style="{ transform: `translateY(-${currentIndex * 100}%)`, transition: isTransitioning ? 'transform 2.2s cubic-bezier(0.25, 0.46, 0.45, 0.94)' : 'none' }"> <div v-for="article in processedArticles" class="carousel-slide"> <ZRawLink :to="article.path" class="featured-card"> <div class="card-content"> <h3 class="card-title">{{ article.title }}</h3> <p class="card-description">{{ article.description }}</p> </div> </ZRawLink> </div> </div> </template>
2. 懒加载包装组件 (LazyWidgetFeaturedArticlesCarousel.vue
)
<script setup lang="ts"> const FeaturedArticlesCarousel = defineAsyncComponent(() => import('./FeaturedArticlesCarousel.vue')) </script> <template> <Suspense> <FeaturedArticlesCarousel /> <template #fallback>加载中...</template> </Suspense> </template>
3. 数据获取 Composable (useFeaturedArticles.ts
)
export function useFeaturedArticles(limit = 5, minRecommend = 1) { return useAsyncData(`featured-articles-${limit}-${minRecommend}`, () => queryCollection('content') .where('stem', 'LIKE', 'posts/%') .where('recommend', '>=', minRecommend) .select('categories', 'date', 'description', 'image', 'path', 'readingTime', 'recommend', 'title', 'type', 'updated') .all() .then(articles => { const sorted = articles.sort((a, b) => { const recommendDiff = (b.recommend || 0) - (a.recommend || 0) if (recommendDiff !== 0) return recommendDiff return new Date(b.date || 0).getTime() - new Date(a.date || 0).getTime() }) return sorted.slice(0, limit) }), { default: () => [] as ArticleProps[] } ) }
数据来源与规则
- 数据来自
content
集合中路径以posts/
开头的文章。 - 通过
recommend >= minRecommend
筛选;默认minRecommend = 1
。 - 排序规则:先按
recommend
降序;若相等,再按date
降序。 - 默认最多返回 5 篇,可自定义数量与最小推荐值。
可用字段(已在组件内使用):categories
、date
、description
、image
、path
、readingTime
、recommend
、title
、type
、updated
。
轮播行为设计
- 布局:纵向单卡片容器,高度 5rem(移动端 4rem)。
- 自动播放:默认每 4000ms 切换一次。
- 悬停暂停:
mouseenter
暂停、mouseleave
恢复。 - 过渡:
transform: translateY()
,默认 2.2s cubic-bezier 缓动。 - 无缝循环:在轨道末尾复制第一张,动画结束后取消过渡瞬时归位为第 1 张,再恢复过渡,视觉无缝。
- 空态:当无精选文章时显示占位提示。
快速开始
在任意页面模板中引入懒加载包装组件(推荐):
<template> <div class="featured-section"> <LazyWidgetFeaturedArticlesCarousel /> </div> </template>
或直接使用主体组件(同步加载):
<template> <FeaturedArticlesCarousel /> </template>
API 与扩展点
组件
FeaturedArticlesCarousel
- Props:无(当前版本)。
- 插槽:无。
- 事件:无。
- 说明:开箱即用的精选轮播组件,内部完成数据拉取与展示逻辑。
LazyWidgetFeaturedArticlesCarousel
- 用途:用
Suspense + defineAsyncComponent
对主体组件懒加载,并提供加载占位。
- 用途:用
Composables
位于 app/composables/useFeaturedArticles.ts
:
useFeaturedArticles(limit = 5, minRecommend = 1)
:按推荐值筛选与排序,默认 5 篇、recommend >= 1
。usePopularArticles(limit = 3)
:热门(基于推荐值),等价于useFeaturedArticles(limit, 1)
。useLatestFeaturedArticles(limit = 3)
:最新精选(按时间降序)。useCategoryFeaturedArticles(category: string, limit = 3)
:某分类的精选文章。
示例:
const { data: featuredArticles } = await useFeaturedArticles(5, 1)
样式类参考
- 容器:
.featured-carousel-main
、.carousel-container
、.carousel-header
、.featured-title
、.featured-text
、.megaphone-container
。 - 轮播:
.carousel-wrapper
(含玻璃拟态与立体边框)、.carousel-track
、.carousel-slide
。 - 卡片:
.featured-card
、.card-content
、.card-title-row
、.card-title
、.recommend-badge
、.card-description
。 - 空态:
.no-featured
、.no-featured-icon
、.no-featured-text
、.no-featured-hint
。 - 响应式:
@media (max-width: 768px)
下针对高度、圆角、阴影与字号做了收敛。
适配与可访问性
- 响应式:移动端高度缩小至 4rem,间距/阴影同步收敛。
- 交互:悬停暂停可避免阅读被打断。
- 链接:使用
ZRawLink
跳转目标文章路径。
评论区
评论加载中...