Luca Bravo
状态
文章总数
55篇 总字数
6.9万 运营时长
2年6个月分类
标签
AI Arch BF CLI CSS Fuwari Giscus GTK HEO Hyprland JS KDE K签 Linux Lisp LLM Path Pypi Python RSS Vim VTB VUP Whl WP 上海 专业 主题 云朵 享乐 优化 伦理 便利店 俄国 光标 内耗 函数式 分析 创伤 创意 判断 前端 北京 参观 哲学 商业 国庆 壁纸 天津 姐妹 字典 安装 实用 对话 度假 开发环境 开源 归档 微风 心理 意义 技校 抚远 拥抱 推荐 插件 摄影 政策 故障排查 效果 散文 文件 文化 旅游 日本 显卡 样式 模糊 治愈 浏览器 浦东 浦西 热力图 特效 猫猫 玩具 环境 甘城 生态 病毒 直播 社会 社团 视奸 秋游 科幻 移民 窗口 系统 终端 经济 美化 美缝 耳机 脑操 自动驾驶 苏州 茅山 萌系 补档 解释器 设计 评论 话术 谷子 走路 轮子 辍学 迁移 运维 重构 随机 静安 音频 颜文字 颜色 首都 麦金塔 鼠标
909 字
5 分钟
在Fuwari中添加“随机一篇文章”功能
不久前,我上线了一个比较实验性的设计:随机一篇文章,与其他博客不同的是,它始终坐落于文章列表的顶端,较为抢眼,让漫无目的用户一眼看到它,便可以方便快速地随机到任意一篇文章,提升老文章的人流量,缓解文章因为时间原因导致的不平等。
部署前,请先阅读以下事项
- 因为本博客的历史原因,仅随机带标签的文章,忽略没有标签的文章,如果要去除此特性,需要修改以下代码:
if (isHomePage) { const posts = await getCollection('posts'); postUrls = posts .filter(p => !p.data.draft && p.data.tags.length > 0) .map(p => getPostUrlBySlug(p.slug)); }
- 包含此组件的页面在进行过渡动画时可能有轻微的抽搐、卡顿感,原因未知。
第一步:创建组件
在src/components
中,创建文件RandomPostCard.astro
,并且写入以下内容:
---
import { getCollection } from 'astro:content';
import { getPostUrlBySlug } from '../utils/url-utils';
import { Icon } from 'astro-icon/components';
// 1. 先判断当前页面是否为首页(仅主页执行后续逻辑)
const isHomePage = Astro.url.pathname === '/';
let postUrls = [];
// 2. 仅主页获取文章 URL(非主页跳过,减少无效计算)
if (isHomePage) {
const posts = await getCollection('posts');
postUrls = posts
.filter(p => !p.data.draft && p.data.tags.length > 0)
.map(p => getPostUrlBySlug(p.slug));
}
---
{/* 3. 核心条件:仅当是首页时,才渲染整个组件 */}
{isHomePage && (
<>
{postUrls.length > 0 ? (
<!-- 外层容器:添加 ID 对应样式,确保在 Swup 容器内 -->
<div
id="random-post-card"
class="card-base flex flex-col w-full rounded-[var(--radius-large)] overflow-hidden relative onload-animation "
style="animation-delay: calc(var(--content-delay) + 0ms);"
data-post-urls={JSON.stringify(postUrls)}
>
<!-- 标题区域链接 -->
<div class="pl-6 md:pl-9 pr-6 md:pr-2 pt-6 md:pt-7 pb-6 relative w-full">
<a
href="#"
class="random-post-link transition group w-full block font-bold text-2xl text-90
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)] md:before:block"
style="text-align: left;"
aria-label="随机文章"
>
随机一篇文章
<Icon class="inline text-[2rem] text-[var(--primary)] md:hidden translate-y-0.5 absolute bottom-7" name="material-symbols:chevron-right-rounded" />
<Icon class="text-[var(--primary)] text-[2rem] transition hidden md:inline absolute translate-y-0.5 opacity-0 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0 bottom-7" name="material-symbols:chevron-right-rounded" />
</a>
</div>
<!-- 右上角跳转按钮 -->
<a
href="#"
class="random-post-link !hidden md:!flex btn-regular w-[3.25rem] absolute right-3 top-3 bottom-3 rounded-xl bg-[var(--enter-btn-bg)] hover:bg-[var(--enter-btn-bg-hover)] active:bg-[var(--enter-btn-bg-active)] active:scale-95"
style="--coverWidth: 28%;"
aria-label="进入随机文章"
>
<Icon name="material-symbols:chevron-right-rounded" class="transition text-[var(--primary)] text-4xl mx-auto" />
</a>
</div>
<div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 md:hidden"></div>
) : (
<p class="text-center py-6">没有可用的文章</p>
)}
<!-- 4. 脚本逻辑:仅主页加载(随组件一起被条件包裹) -->
<script is:inline>
(function() {
try {
const container = document.querySelector('[data-post-urls]');
if (!container) throw new Error('未找到文章 URL 容器');
// 解析文章 URL 列表
const postUrls = JSON.parse(container.dataset.postUrls || '[]');
if (!Array.isArray(postUrls) || postUrls.length === 0) throw new Error('无有效文章 URL');
// 生成随机 URL(保留原有路径处理逻辑)
const randomIndex = Math.floor(Math.random() * postUrls.length);
let randomUrl = postUrls[randomIndex];
if (randomUrl && typeof randomUrl === 'string') {
if (!randomUrl.startsWith('/') && !randomUrl.startsWith('http')) {
randomUrl = '/' + randomUrl;
}
} else {
throw new Error(`无效 URL 格式: ${randomUrl}`);
}
// 仅设置 href,不阻止默认行为(Swup 会自动拦截链接)
const links = document.querySelectorAll('.random-post-link');
if (links.length === 0) throw new Error('未找到随机文章链接');
links.forEach(link => {
link.href = randomUrl; // 确保 href 正确
link.addEventListener('click', function() {
console.log('跳转到随机文章:', randomUrl);
});
});
console.log('随机文章链接初始化成功,URL:', randomUrl);
} catch (error) {
console.error('随机文章功能异常:', error);
// 异常时确保链接可正常跳转(降级处理)
document.querySelectorAll('.random-post-link').forEach(link => {
link.href = '/'; // 跳转到首页(可自定义降级路径)
});
}
})();
</script>
</>
)}
第二步:使组件生效
打开src/components/PostPage.astro
文件,按照注释说明导入并激活组件,重点分别在第4
和第13
行:
---
import { getPostUrlBySlug } from '@utils/url-utils'
import PostCard from './PostCard.astro'
import RandomPostCard from './RandomPostCard.astro' //导入组件
const { page } = Astro.props
let delay = 0
const interval = 50
---
<div class="transition flex flex-col rounded-[var(--radius-large)] bg-[var(--card-bg)] py-1 md:py-0 md:bg-transparent md:gap-4 mb-4">
<!-- 激活组件 -->
<RandomPostCard />
{page.data.map((entry, i) => (
<PostCard
entry={entry}
title={entry.data.title}
tags={entry.data.tags}
category={entry.data.category}
published={entry.data.published}
updated={entry.data.updated}
url={getPostUrlBySlug(entry.slug)}
image={entry.data.image}
description={entry.data.description}
draft={entry.data.draft}
class:list="onload-animation"
style={`animation-delay: calc(var(--content-delay) + ${(i+1) * interval}ms);`}
/>
))}
</div>