VitePress 配置
更新: 6/26/2025 字数: 0 字 时长: 0 分钟
搜索功能(Docsearch)
VitePress
的内部集成有Algolia
的 Algolia DocSearch
,我们可以直接配置使用。
先看下 vitePress 官方给的配置
export default defineConfig({
themeConfig: {
//Algolia搜索
search: {
provider: 'algolia',
options: {
appId: '<Application ID>',
apiKey: '<Search-Only API Key>',
indexName: '<INDEX_NAME>',
},
},
},
})
配置缺少关键 Key,我们需要先注册 Algolia 账号。
评论(Giscus)
安装 giscus
Giscus 是一个基于 GitHub Discussion 的评论系统,启用简便
进 Giscus App 官网:https://github.com/apps/giscus
点击 Install
安装
选择 Only select repositories
,再指定一个你想开启讨论的仓库
注意
仓库必须是公开的,私有的不行
想单独放评论,新建一个也可
查看
完成后可以在个人头像-设置-应用 Applications
中看到
开启讨论
因为 giscus 会把评论数据都放到讨论 discussions
中
我们进入要开启讨论的仓库,点设置 - 勾选讨论 Settings - discussions
生成数据
输入自己的仓库链接,满足条件会提示可用
下拉到 Discussion 分类推荐选 General
,懒加载评论也可以勾选下
关于讨论的类型,分类如下
查看了一下 Github 的讨论文档
类别 | 中文 | 说明 |
---|---|---|
Announcements | 公告 | 每次评论都会推送所有人 |
General | 常规 | 开放式讨论 |
Ideas | 想法 | 开放式讨论 |
Polls | 投票 | 可投票与讨论 |
Q&A | 问答 | 问答形式 |
Show and tell | 展示和说明 | 开放式讨论 |
下方就自动生成了你的关键数据
其中 data-repo
、 data-repo-id
、 data-category
和 data-category-id
这 4 个是我们的关键数据
<script src="https://giscus.app/client.js"
data-repo="github repository"
data-repo-id="R_******"
data-category="General"
data-category-id="DIC_******"
data-mapping="pathname"
data-strict="0"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="bottom"
data-theme="preferred_color_scheme"
data-lang="zh-CN"
data-loading="lazy"
crossorigin="anonymous"
async>
</script>
安装使用
有能力的可以用官方给的 js 数据封装
我这里用 @T-miracle/vitepress-plugin-comment-with-giscus 的插件
pnpm add -D vitepress-plugin-comment-with-giscus
yarn add -D vitepress-plugin-comment-with-giscus
npm install vitepress-plugin-comment-with-giscus
bun add -D vitepress-plugin-comment-with-giscus
在 .vitepress/theme/index.ts
中填入下面代码
并将我们之前获取的 4 个关键数据填入,其他保持默认保存
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme';
import giscusTalk from 'vitepress-plugin-comment-with-giscus';
import { useData, useRoute } from 'vitepress';
export default {
extends: DefaultTheme,
setup() {
// Get frontmatter and route
const { frontmatter } = useData();
const route = useRoute();
// giscus配置
giscusTalk({
repo: 'your github repository', //仓库
repoId: 'your repository id', //仓库ID
category: 'Announcements', // 讨论分类
categoryId: 'your category id', //讨论分类ID
mapping: 'pathname',
inputPosition: 'bottom',
lang: 'zh-CN',
},
{
frontmatter, route
},
//默认值为true,表示已启用,此参数可以忽略;
//如果为false,则表示未启用
//您可以使用“comment:true”序言在页面上单独启用它
true
);
}
看下底部的效果吧
如果某一页不想启用
我们可以在当前页使用 Frontmatter
关闭
---
comment: false
---
样式美化
主要是针对 vitePress
做的一些样式方面的美化,包括首页彩虹样式动画,以及 markdown
方面的引用、容器、记号笔、代码块和代码组等功能的美化。
彩虹背景动画
在 UnoCSS 首页中,它的标题和图片背景有类似彩虹的渐变色动画
具体效果可以看下面这个 GIF
图:
效果还是挺明显的:左侧 UnoCSS
文字、Getting Started
按钮以及右侧 Logo
都有彩虹渐变背景的动画效果
我们同样可以实现这种效果
在 theme/style
新建 rainbow.css
文件,在 rainbow.css
中 写一个名为 rainbow
关键帧
点我查看代码
/**
* 来源于 unocss
* https://github.com/unocss/unocss/blob/f3bf5218294928f48f5745cd8686261334a7c78d/docs/.vitepress/theme/rainbow.css
*/
// #region rainbow //
@keyframes rainbow {
0% {
--vp-c-brand-1: #00a98e;
--vp-c-brand-light: #4ad1b4;
--vp-c-brand-lighter: #78fadc;
--vp-c-brand-dark: #008269;
--vp-c-brand-darker: #005d47;
--vp-c-brand-next: #009ff7;
}
1.25% {
--vp-c-brand-1: #00a996;
--vp-c-brand-light: #4bd1bd;
--vp-c-brand-lighter: #79fbe5;
--vp-c-brand-dark: #008371;
--vp-c-brand-darker: #005e4f;
--vp-c-brand-next: #009dfa;
}
2.5% {
--vp-c-brand-1: #00a99f;
--vp-c-brand-light: #4cd1c6;
--vp-c-brand-lighter: #7afbee;
--vp-c-brand-dark: #00837a;
--vp-c-brand-darker: #005e56;
--vp-c-brand-next: #009bfc;
}
3.75% {
--vp-c-brand-1: #00a9a7;
--vp-c-brand-light: #4dd1cf;
--vp-c-brand-lighter: #7bfbf8;
--vp-c-brand-dark: #008382;
--vp-c-brand-darker: #005e5e;
--vp-c-brand-next: #0098fd;
}
5% {
--vp-c-brand-1: #00a9b0;
--vp-c-brand-light: #4ed1d7;
--vp-c-brand-lighter: #7dfaff;
--vp-c-brand-dark: #00838a;
--vp-c-brand-darker: #005e65;
--vp-c-brand-next: #0096fd;
}
6.25% {
--vp-c-brand-1: #00a9b8;
--vp-c-brand-light: #4fd1e0;
--vp-c-brand-lighter: #7efaff;
--vp-c-brand-dark: #008391;
--vp-c-brand-darker: #005e6d;
--vp-c-brand-next: #0093fd;
}
7.5% {
--vp-c-brand-1: #00a9c0;
--vp-c-brand-light: #50d0e8;
--vp-c-brand-lighter: #7ffaff;
--vp-c-brand-dark: #008399;
--vp-c-brand-darker: #005e74;
--vp-c-brand-next: #2e90fc;
}
8.75% {
--vp-c-brand-1: #00a8c7;
--vp-c-brand-light: #51d0f0;
--vp-c-brand-lighter: #81f9ff;
--vp-c-brand-dark: #0082a0;
--vp-c-brand-darker: #005e7b;
--vp-c-brand-next: #4d8dfa;
}
10% {
--vp-c-brand-1: #00a8cf;
--vp-c-brand-light: #52cff7;
--vp-c-brand-lighter: #82f8ff;
--vp-c-brand-dark: #0082a7;
--vp-c-brand-darker: #005e81;
--vp-c-brand-next: #638af8;
}
11.25% {
--vp-c-brand-1: #00a7d5;
--vp-c-brand-light: #53cfff;
--vp-c-brand-lighter: #84f8ff;
--vp-c-brand-dark: #0081ae;
--vp-c-brand-darker: #005d87;
--vp-c-brand-next: #7587f5;
}
12.5% {
--vp-c-brand-1: #00a6dc;
--vp-c-brand-light: #55ceff;
--vp-c-brand-lighter: #85f7ff;
--vp-c-brand-dark: #0081b4;
--vp-c-brand-darker: #005d8d;
--vp-c-brand-next: #8583f1;
}
13.75% {
--vp-c-brand-1: #00a6e2;
--vp-c-brand-light: #56cdff;
--vp-c-brand-lighter: #87f6ff;
--vp-c-brand-dark: #0080b9;
--vp-c-brand-darker: #005c93;
--vp-c-brand-next: #9280ed;
}
15% {
--vp-c-brand-1: #00a4e7;
--vp-c-brand-light: #57ccff;
--vp-c-brand-lighter: #88f4ff;
--vp-c-brand-dark: #007fbf;
--vp-c-brand-darker: #005b98;
--vp-c-brand-next: #9f7ce9;
}
16.25% {
--vp-c-brand-1: #00a3ec;
--vp-c-brand-light: #58caff;
--vp-c-brand-lighter: #89f3ff;
--vp-c-brand-dark: #007ec3;
--vp-c-brand-darker: #005b9c;
--vp-c-brand-next: #aa78e3;
}
17.5% {
--vp-c-brand-1: #00a2f1;
--vp-c-brand-light: #58c9ff;
--vp-c-brand-lighter: #8af1ff;
--vp-c-brand-dark: #007dc8;
--vp-c-brand-darker: #0059a0;
--vp-c-brand-next: #b574dd;
}
18.75% {
--vp-c-brand-1: #00a0f4;
--vp-c-brand-light: #59c7ff;
--vp-c-brand-lighter: #8bf0ff;
--vp-c-brand-dark: #007bcb;
--vp-c-brand-darker: #0058a3;
--vp-c-brand-next: #be71d7;
}
20% {
--vp-c-brand-1: #009ff7;
--vp-c-brand-light: #5ac5ff;
--vp-c-brand-lighter: #8ceeff;
--vp-c-brand-dark: #007ace;
--vp-c-brand-darker: #0057a6;
--vp-c-brand-next: #c76dd1;
}
21.25% {
--vp-c-brand-1: #009dfa;
--vp-c-brand-light: #5ac3ff;
--vp-c-brand-lighter: #8decff;
--vp-c-brand-dark: #0078d0;
--vp-c-brand-darker: #0055a8;
--vp-c-brand-next: #cf69c9;
}
22.5% {
--vp-c-brand-1: #009bfc;
--vp-c-brand-light: #5bc1ff;
--vp-c-brand-lighter: #8de9ff;
--vp-c-brand-dark: #0076d2;
--vp-c-brand-darker: #0053aa;
--vp-c-brand-next: #d566c2;
}
23.75% {
--vp-c-brand-1: #0098fd;
--vp-c-brand-light: #5bbfff;
--vp-c-brand-lighter: #8ee7ff;
--vp-c-brand-dark: #0074d3;
--vp-c-brand-darker: #0051ab;
--vp-c-brand-next: #dc63ba;
}
25% {
--vp-c-brand-1: #0096fd;
--vp-c-brand-light: #5bbcff;
--vp-c-brand-lighter: #8ee4ff;
--vp-c-brand-dark: #0071d4;
--vp-c-brand-darker: #004fab;
--vp-c-brand-next: #e160b3;
}
26.25% {
--vp-c-brand-1: #0093fd;
--vp-c-brand-light: #5bb9ff;
--vp-c-brand-lighter: #8ee1ff;
--vp-c-brand-dark: #006fd3;
--vp-c-brand-darker: #004dab;
--vp-c-brand-next: #e65eab;
}
27.5% {
--vp-c-brand-1: #2e90fc;
--vp-c-brand-light: #69b6ff;
--vp-c-brand-lighter: #99deff;
--vp-c-brand-dark: #006cd2;
--vp-c-brand-darker: #004baa;
--vp-c-brand-next: #e95ca2;
}
28.75% {
--vp-c-brand-1: #4d8dfa;
--vp-c-brand-light: #7eb3ff;
--vp-c-brand-lighter: #abdbff;
--vp-c-brand-dark: #0069d1;
--vp-c-brand-darker: #0048a9;
--vp-c-brand-next: #ed5a9a;
}
30% {
--vp-c-brand-1: #638af8;
--vp-c-brand-light: #8fb0ff;
--vp-c-brand-lighter: #bbd7ff;
--vp-c-brand-dark: #3066cf;
--vp-c-brand-darker: #0045a7;
--vp-c-brand-next: #ef5992;
}
31.25% {
--vp-c-brand-1: #7587f5;
--vp-c-brand-light: #9fadff;
--vp-c-brand-lighter: #cad4ff;
--vp-c-brand-dark: #4963cc;
--vp-c-brand-darker: #0941a4;
--vp-c-brand-next: #f15989;
}
32.5% {
--vp-c-brand-1: #8583f1;
--vp-c-brand-light: #aea9ff;
--vp-c-brand-lighter: #d8d1ff;
--vp-c-brand-dark: #5b5fc8;
--vp-c-brand-darker: #2e3ea1;
--vp-c-brand-next: #f25981;
}
33.75% {
--vp-c-brand-1: #9280ed;
--vp-c-brand-light: #bca6ff;
--vp-c-brand-lighter: #e6cdff;
--vp-c-brand-dark: #6a5cc4;
--vp-c-brand-darker: #413a9d;
--vp-c-brand-next: #f25a79;
}
35% {
--vp-c-brand-1: #9f7ce9;
--vp-c-brand-light: #c8a2ff;
--vp-c-brand-lighter: #f2c9ff;
--vp-c-brand-dark: #7758c0;
--vp-c-brand-darker: #503598;
--vp-c-brand-next: #f25c71;
}
36.25% {
--vp-c-brand-1: #aa78e3;
--vp-c-brand-light: #d39eff;
--vp-c-brand-lighter: #fec6ff;
--vp-c-brand-dark: #8354bb;
--vp-c-brand-darker: #5c3193;
--vp-c-brand-next: #f15e69;
}
37.5% {
--vp-c-brand-1: #b574dd;
--vp-c-brand-light: #de9bff;
--vp-c-brand-lighter: #ffc2ff;
--vp-c-brand-dark: #8d50b5;
--vp-c-brand-darker: #662c8e;
--vp-c-brand-next: #ef6061;
}
38.75% {
--vp-c-brand-1: #be71d7;
--vp-c-brand-light: #e897ff;
--vp-c-brand-lighter: #ffbfff;
--vp-c-brand-dark: #964baf;
--vp-c-brand-darker: #6f2688;
--vp-c-brand-next: #ed635a;
}
40% {
--vp-c-brand-1: #c76dd1;
--vp-c-brand-light: #f194fa;
--vp-c-brand-lighter: #ffbcff;
--vp-c-brand-dark: #9e47a9;
--vp-c-brand-darker: #772082;
--vp-c-brand-next: #eb6552;
}
41.25% {
--vp-c-brand-1: #cf69c9;
--vp-c-brand-light: #f991f2;
--vp-c-brand-lighter: #ffb9ff;
--vp-c-brand-dark: #a643a2;
--vp-c-brand-darker: #7e197c;
--vp-c-brand-next: #e8694b;
}
42.5% {
--vp-c-brand-1: #d566c2;
--vp-c-brand-light: #ff8deb;
--vp-c-brand-lighter: #ffb6ff;
--vp-c-brand-dark: #ac3f9b;
--vp-c-brand-darker: #841075;
--vp-c-brand-next: #e46c44;
}
43.75% {
--vp-c-brand-1: #dc63ba;
--vp-c-brand-light: #ff8be3;
--vp-c-brand-lighter: #ffb3ff;
--vp-c-brand-dark: #b23b94;
--vp-c-brand-darker: #89046f;
--vp-c-brand-next: #e06f3d;
}
45% {
--vp-c-brand-1: #e160b3;
--vp-c-brand-light: #ff88db;
--vp-c-brand-lighter: #ffb1ff;
--vp-c-brand-dark: #b7378c;
--vp-c-brand-darker: #8d0068;
--vp-c-brand-next: #db7336;
}
46.25% {
--vp-c-brand-1: #e65eab;
--vp-c-brand-light: #ff86d2;
--vp-c-brand-lighter: #ffaffb;
--vp-c-brand-dark: #bb3485;
--vp-c-brand-darker: #910060;
--vp-c-brand-next: #d77630;
}
47.5% {
--vp-c-brand-1: #e95ca2;
--vp-c-brand-light: #ff84ca;
--vp-c-brand-lighter: #ffadf2;
--vp-c-brand-dark: #be317d;
--vp-c-brand-darker: #940059;
--vp-c-brand-next: #d17a2a;
}
48.75% {
--vp-c-brand-1: #ed5a9a;
--vp-c-brand-light: #ff83c1;
--vp-c-brand-lighter: #fface9;
--vp-c-brand-dark: #c12f75;
--vp-c-brand-darker: #970052;
--vp-c-brand-next: #cc7d24;
}
50% {
--vp-c-brand-1: #ef5992;
--vp-c-brand-light: #ff82b8;
--vp-c-brand-lighter: #ffabe0;
--vp-c-brand-dark: #c32d6d;
--vp-c-brand-darker: #98004b;
--vp-c-brand-next: #c6811e;
}
51.25% {
--vp-c-brand-1: #f15989;
--vp-c-brand-light: #ff82af;
--vp-c-brand-lighter: #ffabd7;
--vp-c-brand-dark: #c52d65;
--vp-c-brand-darker: #9a0043;
--vp-c-brand-next: #bf8418;
}
52.5% {
--vp-c-brand-1: #f25981;
--vp-c-brand-light: #ff82a7;
--vp-c-brand-lighter: #ffabce;
--vp-c-brand-dark: #c52e5e;
--vp-c-brand-darker: #9a003c;
--vp-c-brand-next: #b98713;
}
53.75% {
--vp-c-brand-1: #f25a79;
--vp-c-brand-light: #ff839e;
--vp-c-brand-lighter: #ffacc5;
--vp-c-brand-dark: #c62f56;
--vp-c-brand-darker: #9a0035;
--vp-c-brand-next: #b28a0f;
}
55% {
--vp-c-brand-1: #f25c71;
--vp-c-brand-light: #ff8496;
--vp-c-brand-lighter: #ffadbc;
--vp-c-brand-dark: #c5314e;
--vp-c-brand-darker: #99002e;
--vp-c-brand-next: #ab8d0c;
}
56.25% {
--vp-c-brand-1: #f15e69;
--vp-c-brand-light: #ff868d;
--vp-c-brand-lighter: #ffaeb4;
--vp-c-brand-dark: #c43447;
--vp-c-brand-darker: #980027;
--vp-c-brand-next: #a3900b;
}
57.5% {
--vp-c-brand-1: #ef6061;
--vp-c-brand-light: #ff8885;
--vp-c-brand-lighter: #ffb0ab;
--vp-c-brand-dark: #c3373f;
--vp-c-brand-darker: #970020;
--vp-c-brand-next: #9c920d;
}
58.75% {
--vp-c-brand-1: #ed635a;
--vp-c-brand-light: #ff8a7d;
--vp-c-brand-lighter: #ffb2a3;
--vp-c-brand-dark: #c13b38;
--vp-c-brand-darker: #940619;
--vp-c-brand-next: #949510;
}
60% {
--vp-c-brand-1: #eb6552;
--vp-c-brand-light: #ff8d76;
--vp-c-brand-lighter: #ffb59b;
--vp-c-brand-dark: #be3e31;
--vp-c-brand-darker: #921111;
--vp-c-brand-next: #8b9715;
}
61.25% {
--vp-c-brand-1: #e8694b;
--vp-c-brand-light: #ff8f6e;
--vp-c-brand-lighter: #ffb794;
--vp-c-brand-dark: #bb4229;
--vp-c-brand-darker: #8f1908;
--vp-c-brand-next: #83991b;
}
62.5% {
--vp-c-brand-1: #e46c44;
--vp-c-brand-light: #ff9367;
--vp-c-brand-lighter: #ffba8c;
--vp-c-brand-dark: #b74622;
--vp-c-brand-darker: #8c1f00;
--vp-c-brand-next: #7a9b21;
}
63.75% {
--vp-c-brand-1: #e06f3d;
--vp-c-brand-light: #ff9661;
--vp-c-brand-lighter: #ffbd86;
--vp-c-brand-dark: #b44a1a;
--vp-c-brand-darker: #882500;
--vp-c-brand-next: #719d27;
}
65% {
--vp-c-brand-1: #db7336;
--vp-c-brand-light: #ff995a;
--vp-c-brand-lighter: #ffc17f;
--vp-c-brand-dark: #af4e11;
--vp-c-brand-darker: #842a00;
--vp-c-brand-next: #679e2e;
}
66.25% {
--vp-c-brand-1: #d77630;
--vp-c-brand-light: #ff9c54;
--vp-c-brand-lighter: #ffc47a;
--vp-c-brand-dark: #ab5206;
--vp-c-brand-darker: #802f00;
--vp-c-brand-next: #5da035;
}
67.5% {
--vp-c-brand-1: #d17a2a;
--vp-c-brand-light: #fea04f;
--vp-c-brand-lighter: #ffc774;
--vp-c-brand-dark: #a55600;
--vp-c-brand-darker: #7b3300;
--vp-c-brand-next: #51a13c;
}
68.75% {
--vp-c-brand-1: #cc7d24;
--vp-c-brand-light: #f8a34a;
--vp-c-brand-lighter: #ffca70;
--vp-c-brand-dark: #a05900;
--vp-c-brand-darker: #773700;
--vp-c-brand-next: #44a244;
}
70% {
--vp-c-brand-1: #c6811e;
--vp-c-brand-light: #f2a646;
--vp-c-brand-lighter: #ffce6c;
--vp-c-brand-dark: #9b5d00;
--vp-c-brand-darker: #713b00;
--vp-c-brand-next: #34a44b;
}
71.25% {
--vp-c-brand-1: #bf8418;
--vp-c-brand-light: #ebaa42;
--vp-c-brand-lighter: #ffd168;
--vp-c-brand-dark: #956000;
--vp-c-brand-darker: #6c3e00;
--vp-c-brand-next: #1ba553;
}
72.5% {
--vp-c-brand-1: #b98713;
--vp-c-brand-light: #e4ad3f;
--vp-c-brand-lighter: #ffd466;
--vp-c-brand-dark: #8e6300;
--vp-c-brand-darker: #674100;
--vp-c-brand-next: #00a65b;
}
73.75% {
--vp-c-brand-1: #b28a0f;
--vp-c-brand-light: #ddb03d;
--vp-c-brand-lighter: #ffd764;
--vp-c-brand-dark: #886600;
--vp-c-brand-darker: #614400;
--vp-c-brand-next: #00a663;
}
75% {
--vp-c-brand-1: #ab8d0c;
--vp-c-brand-light: #d5b33c;
--vp-c-brand-lighter: #ffda63;
--vp-c-brand-dark: #816900;
--vp-c-brand-darker: #5b4700;
--vp-c-brand-next: #00a76c;
}
76.25% {
--vp-c-brand-1: #a3900b;
--vp-c-brand-light: #cdb63c;
--vp-c-brand-lighter: #f8dd63;
--vp-c-brand-dark: #7a6b00;
--vp-c-brand-darker: #554900;
--vp-c-brand-next: #00a874;
}
77.5% {
--vp-c-brand-1: #9c920d;
--vp-c-brand-light: #c5b83d;
--vp-c-brand-lighter: #f0e064;
--vp-c-brand-dark: #736e00;
--vp-c-brand-darker: #4e4b00;
--vp-c-brand-next: #00a87d;
}
78.75% {
--vp-c-brand-1: #949510;
--vp-c-brand-light: #bdbb3e;
--vp-c-brand-lighter: #e7e366;
--vp-c-brand-dark: #6c7000;
--vp-c-brand-darker: #474d00;
--vp-c-brand-next: #00a985;
}
80% {
--vp-c-brand-1: #8b9715;
--vp-c-brand-light: #b4bd41;
--vp-c-brand-lighter: #dee668;
--vp-c-brand-dark: #647200;
--vp-c-brand-darker: #404f00;
--vp-c-brand-next: #00a98e;
}
81.25% {
--vp-c-brand-1: #83991b;
--vp-c-brand-light: #abc045;
--vp-c-brand-lighter: #d4e86c;
--vp-c-brand-dark: #5c7400;
--vp-c-brand-darker: #385100;
--vp-c-brand-next: #00a996;
}
82.5% {
--vp-c-brand-1: #7a9b21;
--vp-c-brand-light: #a2c249;
--vp-c-brand-lighter: #cbea70;
--vp-c-brand-dark: #537600;
--vp-c-brand-darker: #2f5200;
--vp-c-brand-next: #00a99f;
}
83.75% {
--vp-c-brand-1: #719d27;
--vp-c-brand-light: #98c44e;
--vp-c-brand-lighter: #c1ec75;
--vp-c-brand-dark: #4a7700;
--vp-c-brand-darker: #255300;
--vp-c-brand-next: #00a9a7;
}
85% {
--vp-c-brand-1: #679e2e;
--vp-c-brand-light: #8ec654;
--vp-c-brand-lighter: #b7ee7a;
--vp-c-brand-dark: #407900;
--vp-c-brand-darker: #185500;
--vp-c-brand-next: #00a9b0;
}
86.25% {
--vp-c-brand-1: #5da035;
--vp-c-brand-light: #84c75a;
--vp-c-brand-lighter: #acf080;
--vp-c-brand-dark: #357a0a;
--vp-c-brand-darker: #015600;
--vp-c-brand-next: #00a9b8;
}
87.5% {
--vp-c-brand-1: #51a13c;
--vp-c-brand-light: #79c961;
--vp-c-brand-lighter: #a1f287;
--vp-c-brand-dark: #277b16;
--vp-c-brand-darker: #005700;
--vp-c-brand-next: #00a9c0;
}
88.75% {
--vp-c-brand-1: #44a244;
--vp-c-brand-light: #6dca68;
--vp-c-brand-lighter: #96f48e;
--vp-c-brand-dark: #117c1f;
--vp-c-brand-darker: #005700;
--vp-c-brand-next: #00a8c7;
}
90% {
--vp-c-brand-1: #34a44b;
--vp-c-brand-light: #60cc70;
--vp-c-brand-lighter: #89f595;
--vp-c-brand-dark: #007d28;
--vp-c-brand-darker: #005801;
--vp-c-brand-next: #00a8cf;
}
91.25% {
--vp-c-brand-1: #1ba553;
--vp-c-brand-light: #51cd77;
--vp-c-brand-lighter: #7cf69d;
--vp-c-brand-dark: #007e30;
--vp-c-brand-darker: #00590d;
--vp-c-brand-next: #00a7d5;
}
92.5% {
--vp-c-brand-1: #00a65b;
--vp-c-brand-light: #48ce80;
--vp-c-brand-lighter: #75f7a6;
--vp-c-brand-dark: #007f38;
--vp-c-brand-darker: #005917;
--vp-c-brand-next: #00a6dc;
}
93.75% {
--vp-c-brand-1: #00a663;
--vp-c-brand-light: #48cf88;
--vp-c-brand-lighter: #75f8ae;
--vp-c-brand-dark: #008040;
--vp-c-brand-darker: #005a20;
--vp-c-brand-next: #00a6e2;
}
95% {
--vp-c-brand-1: #00a76c;
--vp-c-brand-light: #49cf91;
--vp-c-brand-lighter: #76f9b7;
--vp-c-brand-dark: #008049;
--vp-c-brand-darker: #005b28;
--vp-c-brand-next: #00a4e7;
}
96.25% {
--vp-c-brand-1: #00a874;
--vp-c-brand-light: #49d099;
--vp-c-brand-lighter: #76f9c0;
--vp-c-brand-dark: #008151;
--vp-c-brand-darker: #005c30;
--vp-c-brand-next: #00a3ec;
}
97.5% {
--vp-c-brand-1: #00a87d;
--vp-c-brand-light: #49d0a2;
--vp-c-brand-lighter: #77fac9;
--vp-c-brand-dark: #008159;
--vp-c-brand-darker: #005c37;
--vp-c-brand-next: #00a2f1;
}
98.75% {
--vp-c-brand-1: #00a985;
--vp-c-brand-light: #4ad1ab;
--vp-c-brand-lighter: #77fad3;
--vp-c-brand-dark: #008261;
--vp-c-brand-darker: #005d3f;
--vp-c-brand-next: #00a0f4;
}
100% {
--vp-c-brand-1: #00a98e;
--vp-c-brand-light: #4ad1b4;
--vp-c-brand-lighter: #78fadc;
--vp-c-brand-dark: #008269;
--vp-c-brand-darker: #005d47;
--vp-c-brand-next: #009ff7;
}
}
:root {
--vp-c-brand-1: #00a98e;
--vp-c-brand-light: #4ad1b4;
--vp-c-brand-lighter: #78fadc;
--vp-c-brand-dark: #008269;
--vp-c-brand-darker: #005d47;
--vp-c-brand-next: #009ff7;
--vp-c-brand-2: var(--vp-c-brand-darker);
// 一共 82 种颜色,颜色过度时间为 3s
animation: rainbow 246s linear infinite;
}
:root.dark {
--vp-c-brand-2: var(--vp-c-brand-light);
}
@media (prefers-reduced-motion: reduce) {
:root {
animation: none !important;
}
}
// #endregion rainbow //
接着使用这个关键帧
在 theme/index.ts
中写入代码
/* .vitepress/theme/index.ts */
// 彩虹背景动画样式
let homePageStyle: HTMLStyleElement | undefined
export default {
extends: DefaultTheme,
enhanceApp({app , router }) {
// 彩虹背景动画样式
if (typeof window !== 'undefined') {
watch(
() => router.route.data.relativePath,
() => updateHomePageStyle(location.pathname === '/'),
{ immediate: true },
)
}
},
}
// 彩虹背景动画样式
function updateHomePageStyle(value: boolean) {
if (value) {
if (homePageStyle) return
homePageStyle = document.createElement('style')
homePageStyle.innerHTML = `
:root {
animation: rainbow 12s linear infinite;
}`
document.body.appendChild(homePageStyle)
} else {
if (!homePageStyle) return
homePageStyle.remove()
homePageStyle = undefined
}
}
这段代码的逻辑是这样的:
- 先定义一个动画样式变量
homePageStyle
,类型为HTMLStyleElement
- 创建一个名为
updateHomePageStyle
的函数,函数的作用是根据传入的参数value
来判断是否需要添加动画样式。如果value
为真并且homePageStyle
不存在,则创建一个新的样式元素homePageStyle
,并设置样式内容为动画样式,使用我们之前创建的关键帧rainbow
。然后将homePageStyle
添加到body
元素中。如果value
为假,则移除样式元素 - 之后使用
watch
监听 路由是否变化,如果路由变化,则执行updateHomePageStyle 函数
,在当前页面是首页的情况下,给函数传true
,否则传false
为什么不直接在全局样式中写 animation ?
主要是性能方面的考虑
如果直接在全局 CSS
中添加动画:
:root {
animation: rainbow 12s linear infinite;
}
那么这个动画会在所有页面都运行,即使你已经离开首页(/)
进入其他页面。这会导致:
❌ 动画资源浪费(即使不需要该动画时也持续运行)
❌ 可能影响性能(尤其是复杂的动画或低端设备上)
我们当前代码中通过 Vue
的 watch
监听路由变化,并调用 updateHomePageStyle(location.pathname === "/")
实现了以下效果:
✔️ 按需加载动画,提升性能
只在访问首页
/
时才插入带有动画的<style>
标签,当用户浏览其他页面时,动态移除动画样式,这样可以避免不必要的资源消耗,提升性能✔️ 精确控制动画生命周期
通过手动控制动画样式的添加与移除,可以确保动画状态始终与当前页面匹配,避免出现“页面已切换但动画仍在运行”的不一致行为
💡 类似场景举例
这种做法常见于以下场景:
- 首页背景动画
- 页面加载特效
- 某些页面独有的交互动画
A/B
测试中的特定样式注入
引用颜色更改
在 Markdown 中,我们常用的引用符号是 >
,关于引用的样式我们我们可以稍微改动一下
在 theme/style
新建 blockquote.css
文件,并且复制下面代码,粘贴到 blockquote.css
中
/* .vitepress/theme/style/blockquote.css */
.vp-doc blockquote {
border-radius: 10px;
padding: 18px 20px 20px 15px;
position: relative;
background-color: var(--vp-c-gray-soft);
border-left: 6px solid var(--vp-c-green-2);
}
然后在 index.css
中引入生效
/* .vitepress/theme/style/index.css */
@import "./blockquote.css";
输入:
> 更新时间:2024 年
输出:
更新时间:2024 年
容器颜色
vitePress
中 tip
、warning
、danger
等容器的样式不太好看,这里我们参考Vuepress/hope 主题的容器颜色去实现一套我们自己的方案
在 theme/style
新建 custom-block.css
文件,复制下面代码,粘贴到 custom-block.css
中
点我查看代码
/* .vitepress/theme/style/custom-block.css */
/* 深浅色卡 */
// #region custom-block //
:root {
--custom-block-info-left: #cccccc;
--custom-block-info-bg: #fafafa;
--custom-block-tip-left: #009400;
--custom-block-tip-bg: #e6f6e6;
--custom-block-warning-left: #e6a700;
--custom-block-warning-bg: #fff8e6;
--custom-block-danger-left: #e13238;
--custom-block-danger-bg: #ffebec;
--custom-block-note-left: #4cb3d4;
--custom-block-note-bg: #eef9fd;
--custom-block-important-left: #a371f7;
--custom-block-important-bg: #f4eefe;
--custom-block-caution-left: #e0575b;
--custom-block-caution-bg: #fde4e8;
}
.dark {
--custom-block-info-left: #cccccc;
--custom-block-info-bg: #474748;
--custom-block-tip-left: #009400;
--custom-block-tip-bg: #003100;
--custom-block-warning-left: #e6a700;
--custom-block-warning-bg: #4d3800;
--custom-block-danger-left: #e13238;
--custom-block-danger-bg: #4b1113;
--custom-block-note-left: #4cb3d4;
--custom-block-note-bg: #193c47;
--custom-block-important-left: #a371f7;
--custom-block-important-bg: #230555;
--custom-block-caution-left: #e0575b;
--custom-block-caution-bg: #391c22;
}
/* 标题字体大小 */
.custom-block-title {
font-size: 16px;
}
/* info容器:背景色、左侧 */
.custom-block.info {
border-left: 5px solid var(--custom-block-info-left);
background-color: var(--custom-block-info-bg);
}
/* info容器:svg图 */
.custom-block.info [class*="custom-block-title"]::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%23ccc'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}
/* 提示容器:边框色、背景色、左侧 */
.custom-block.tip {
/* border-color: var(--custom-block-tip); */
border-left: 5px solid var(--custom-block-tip-left);
background-color: var(--custom-block-tip-bg);
}
/* 提示容器:svg图 */
.custom-block.tip [class*="custom-block-title"]::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23009400' d='M7.941 18c-.297-1.273-1.637-2.314-2.187-3a8 8 0 1 1 12.49.002c-.55.685-1.888 1.726-2.185 2.998H7.94zM16 20v1a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-1h8zm-3-9.995V6l-4.5 6.005H11v4l4.5-6H13z'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -2px;
}
/* 警告容器:背景色、左侧 */
.custom-block.warning {
border-left: 5px solid var(--custom-block-warning-left);
background-color: var(--custom-block-warning-bg);
}
/* 警告容器:svg图 */
.custom-block.warning [class*="custom-block-title"]::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M576.286 752.57v-95.425q0-7.031-4.771-11.802t-11.3-4.772h-96.43q-6.528 0-11.3 4.772t-4.77 11.802v95.424q0 7.031 4.77 11.803t11.3 4.77h96.43q6.528 0 11.3-4.77t4.77-11.803zm-1.005-187.836 9.04-230.524q0-6.027-5.022-9.543-6.529-5.524-12.053-5.524H456.754q-5.524 0-12.053 5.524-5.022 3.516-5.022 10.547l8.538 229.52q0 5.023 5.022 8.287t12.053 3.265h92.913q7.032 0 11.803-3.265t5.273-8.287zM568.25 95.65l385.714 707.142q17.578 31.641-1.004 63.282-8.538 14.564-23.354 23.102t-31.892 8.538H126.286q-17.076 0-31.892-8.538T71.04 866.074q-18.582-31.641-1.004-63.282L455.75 95.65q8.538-15.57 23.605-24.61T512 62t32.645 9.04 23.605 24.61z' fill='%23e6a700'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
}
/* 危险容器:背景色、左侧 */
.custom-block.danger {
border-left: 5px solid var(--custom-block-danger-left);
background-color: var(--custom-block-danger-bg);
}
/* 危险容器:svg图 */
.custom-block.danger [class*="custom-block-title"]::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}
/* NOTE容器:背景色、左侧 */
.custom-block.note {
border-left: 5px solid var(--custom-block-note-left);
background-color: var(--custom-block-note-bg);
}
/* NOTE容器:svg图 */
.custom-block.note [class*="custom-block-title"]::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%234cb3d4'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}
/* IMPORTANT容器:背景色、左侧 */
.custom-block.important {
border-left: 5px solid var(--custom-block-important-left);
background-color: var(--custom-block-important-bg);
}
/* IMPORTANT容器:svg图 */
.custom-block.important [class*="custom-block-title"]::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M512 981.333a84.992 84.992 0 0 1-84.907-84.906h169.814A84.992 84.992 0 0 1 512 981.333zm384-128H128v-42.666l85.333-85.334v-256A298.325 298.325 0 0 1 448 177.92V128a64 64 0 0 1 128 0v49.92a298.325 298.325 0 0 1 234.667 291.413v256L896 810.667v42.666zm-426.667-256v85.334h85.334v-85.334h-85.334zm0-256V512h85.334V341.333h-85.334z' fill='%23a371f7'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}
/* CAUTION容器:背景色、左侧 */
.custom-block.caution {
border-left: 5px solid var(--custom-block-caution-left);
background-color: var(--custom-block-caution-bg);
}
/* CAUTION容器:svg图 */
.custom-block.caution [class*="custom-block-title"]::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}
// #endregion custom-block //
更改之前效果:
更改之后效果:
更改之后加了左边框、图标,看着好看多了
导航毛玻璃
在 theme/style
文件夹,然后新建 blur.css
并填入如下代码
/* .vitepress\theme\style\blur.css */
:root {
/* 首页下滑后导航透明 */
.VPNavBar:not(.has-sidebar):not(.home.top) {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
/* 搜索框透明 */
.DocSearch-Button {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
/* Feature透明 */
.VPFeature {
border: none;
box-shadow: 0 10px 30px 0 rgb(0 0 0 / 15%);
background-color: #fbfbfc;
}
/* 文档页侧边栏顶部透明 */
.curtain {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
@media (min-width: 960px) {
/* 文档页导航中间透明 */
.VPNavBar:not(.home.top) .content-body {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
}
/* 移动端大纲栏透明 */
.VPLocalNav {
background-color: rgba(255, 255, 255, 0);
backdrop-filter: blur(10px);
}
}
最后引入 index.css
中 即可看到效果
/* style/index.css */
@import "./blur.css";
更改之前效果:
更改之后效果:
相比较更改之前导航栏纯白的背景,更改之后的导航栏有一个毛玻璃的效果,体验感会更好
记号笔
在某些整段的文字中,我们可以用记号笔,划出重点。这里的记号笔效果参考了尤大的个人主页
在 theme/style
新建 marker.css
文件,将下面代码,复制粘贴到 marker.css
中
/* .vitepress/theme/style/marker.css */
/* 尤雨溪主页记号笔效果 不喜欢可自行调整 */
.marker {
white-space: nowrap;
position: relative;
}
.marker:after {
content: "";
position: absolute;
z-index: -1;
top: 66%;
left: 0em;
right: 0em;
bottom: 0;
transition: top 200ms cubic-bezier(0, 0.8, 0.13, 1);
background-color: rgba(79, 192, 141, 0.5);
}
.marker:hover:after {
top: 0%;
}
然后在 index.css
中引入生效
/* .vitepress/theme/style/index.css */
@import "./marker.css";
输入:
<sapn class="marker">这里是尤雨溪的主页样式,鼠标放在我上面看效果</sapn>
输出:
代码块
将代码块改成 Mac 风格,三个小圆点
在 .vitepress/theme/style
目录新建一个 vp-code.css
文件,复制下面代码,粘贴到 vp-code.css
保存
/* .vitepress/theme/style/vp-code.css */
/* 代码块:增加留空边距 增加阴影 */
.vp-doc div[class*="language-"] {
box-shadow: 0 10px 30px 0 rgb(0 0 0 / 40%);
padding-top: 20px;
}
/* 代码块:添加macOS风格的小圆点 */
.vp-doc div[class*="language-"]::before {
content: "";
display: block;
position: absolute;
top: 12px;
left: 12px;
width: 12px;
height: 12px;
background-color: #ff5f56;
border-radius: 50%;
box-shadow: 20px 0 0 #ffbd2e, 40px 0 0 #27c93f;
z-index: 1;
}
/* 代码块:下移行号 隐藏右侧竖线 */
.vp-doc .line-numbers-wrapper {
padding-top: 40px;
border-right: none;
}
/* 代码块:重建行号右侧竖线 */
.vp-doc .line-numbers-wrapper::after {
content: "";
position: absolute;
top: 40px;
right: 0;
border-right: 1px solid var(--vp-code-block-divider-color);
height: calc(100% - 60px);
}
.vp-doc div[class*="language-"].line-numbers-mode {
margin-bottom: 20px;
}
然后在 index.css
中引入生效
/* .vitepress/theme/style/index.css */
@import "./vp-code.css";
更改之前效果:
更改之后效果:
更改之后加了边框阴影和顶部左侧的小圆点,更好看了
代码组
在更改代码块的基础上,更改代码组样式
在 .vitepress/theme/style
目录新建一个 vp-code-group.css
文件,复制下面代码,粘贴到 vp-code-group.css
保存
/* .vitepress/theme/style/vp-code-group.css */
/* 代码组:tab间距 */
.vp-code-group .tabs {
padding-top: 20px;
}
/* 代码组:添加样式及阴影 */
.vp-code-group {
color: var(--vp-c-black-soft);
border-radius: 8px;
box-shadow: 0 10px 30px 0 rgb(0 0 0 / 40%);
}
/* 代码组:添加macOS风格的小圆点 */
.vp-code-group .tabs::before {
content: " ";
position: absolute;
top: 12px;
left: 12px;
height: 12px;
width: 12px;
background: #fc625d;
border-radius: 50%;
box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
}
/* 代码组:修正倒角、阴影、边距 */
.vp-code-group div[class*="language-"].vp-adaptive-theme.line-numbers-mode {
border-radius: 8px;
box-shadow: none;
padding-top: 0px;
}
/* 代码组:隐藏小圆点 */
.vp-code-group div[class*="language-"].vp-adaptive-theme.line-numbers-mode::before {
display: none;
}
/* 代码组:修正行号位置 */
.vp-code-group .line-numbers-mode .line-numbers-wrapper {
padding-top: 20px;
}
/* 代码组:修正行号右侧竖线位置 */
.vp-code-group .line-numbers-mode .line-numbers-wrapper::after {
top: 24px;
height: calc(100% - 45px);
}
/* 代码组(无行号):修正倒角、阴影、边距 */
.vp-code-group div[class*="language-"].vp-adaptive-theme {
border-radius: 8px;
box-shadow: none;
padding-top: 0px;
}
/* 代码组(无行号):隐藏小圆点 */
.vp-code-group div[class*="language-"].vp-adaptive-theme::before {
display: none;
}
然后在 index.css
中引入生效
/* .vitepress/theme/style/index.css */
@import "./vp-code-group.css";
插件
使用插件实现一些其他效果。比如使用 medium-zoom
实现缩放图片,使用 vitepress-plugin-group-icons
增加代码组图标,使用 nprogress-v2
实现切换进度条效果 。
图片缩放
主要是使用 medium-zoom ,参考了Allow images to be zoomed in on click
pnpm add -D medium-zoom
yarn add -D medium-zoom
npm install medium-zoom
bun add -D medium-zoom
在 .vitepress/theme/index.ts
添加如下代码,并保存
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import mediumZoom from 'medium-zoom';
import { onMounted, watch, nextTick } from 'vue';
import { useRoute } from 'vitepress';
export default {
extends: DefaultTheme,
setup() {
const route = useRoute();
const initZoom = () => {
// mediumZoom('[data-zoomable]', { background: 'var(--vp-c-bg)' }); // 默认
mediumZoom('.main img', { background: 'var(--vp-c-bg)' }); // 不显式添加{data-zoomable}的情况下为所有图像启用此功能
};
onMounted(() => {
initZoom();
});
watch(
() => route.path,
() => nextTick(() => initZoom())
);
},
}
点击图片后,还是能看到导航栏,加一个遮挡样式
在 theme/style
新建 medium-zoom.scss
加入如下代码:
/* .vitepress/theme/style/medium-zoom.scss */
:root {
--medium-zoom-z-index: 100;
--medium-zoom-c-bg: var(--vp-c-bg);
}
.medium-zoom-overlay {
/* override element style set by medium-zoom script */
z-index: var(--medium-zoom-z-index);
background-color: var(--medium-zoom-c-bg) !important;
}
.medium-zoom-overlay ~ img {
z-index: calc(var(--medium-zoom-z-index) + 1);
}
更改之后效果
代码组图标
使用的插件是 @yuyinws/vitepress-plugin-group-icons
参照教程安装:https://vpgi.vercel.app/
pnpm add -D vitepress-plugin-group-icons
yarn add -D vitepress-plugin-group-icons
npm install vitepress-plugin-group-icons
bun add -D vitepress-plugin-group-icons
然后在 config.mts
中配置
// .vitepress/config.mts
import { defineConfig } from 'vitepress'
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
export default defineConfig({
markdown: {
config(md) {
md.use(groupIconMdPlugin) //代码组图标
},
},
vite: {
plugins: [
groupIconVitePlugin() //代码组图标
],
},
})
最后还需要再 index.ts
中引入样式
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import 'virtual:group-icons.css' //代码组样式
export default {
extends: DefaultTheme,
}
使用时,请确保代码后有对应的文字触发,如 sh [pnpm]
表示这一段代码块是 pnpm
的代码块
::: code-group
```sh [pnpm]
pnpm -v
```
```sh [yarn]
yarn -v
```
```sh [bun]
bun -v
```
:::
pnpm -v
yarn -v
bun -v
此插件已经涵盖了所有的常用图标,但是有一个问题:如果我们想给代码为 js
的块添加图标,必须写文件名,如:
::: code-group
```ts [a.ts]
console.log("I'm TypeScript");
```
```js [b.js]
console.log("I'm JavaScript");
```
```md [c.md]
Markdown 图标演示
```
```css [d.css]
h1 {
background: red;
}
```
:::
这是因为在插件的内部逻辑中,ts
、js
等图标是根据文件类型去判断添加的。我理想的效果是只需要写文件类型就行了,就比如js [js]
那么该如何自定义呢, 我们需要在config.mts
中配置:
// .vitepress/config.mts
import { defineConfig } from 'vitepress'
import { groupIconMdPlugin, groupIconVitePlugin, localIconLoader } from 'vitepress-plugin-group-icons'
export default defineConfig({
markdown: {
config(md) {
md.use(groupIconMdPlugin) //代码组图标
},
},
vite: {
plugins: [
groupIconVitePlugin({
customIcon: {
mts: "vscode-icons:file-type-typescript",
cts: "vscode-icons:file-type-typescript",
ts: "vscode-icons:file-type-typescript",
tsx: "vscode-icons:file-type-typescript",
mjs: "vscode-icons:file-type-js",
cjs: "vscode-icons:file-type-js",
json: "vscode-icons:file-type-json",
js: "vscode-icons:file-type-js",
jsx: "vscode-icons:file-type-js",
md: "vscode-icons:file-type-markdown",
py: "vscode-icons:file-type-python",
ico: "vscode-icons:file-type-favicon",
html: "vscode-icons:file-type-html",
css: "vscode-icons:file-type-css",
scss: "vscode-icons:file-type-scss",
yml: "vscode-icons:file-type-light-yaml",
yaml: "vscode-icons:file-type-light-yaml",
php: "vscode-icons:file-type-php",
},
})
],
},
})
最后总体看下使用插件增加代码组图标,更改前后的效果
更改前
更改后
切换路由进度条
当切换页面,顶部会显示进度条,使用的是 @Skyleen77/nprogress-v2,使用方法还是挺简单的
先安装 nprogress-v2
pnpm add -D nprogress-v2
yarn add -D nprogress-v2
npm install nprogress-v2
bun add -D nprogress-v2
然后再 .vitepress/theme/index.ts
中配置,即可生效
// .vitepress/theme/index.ts
import { NProgress } from 'nprogress-v2/dist/index.js' // 进度条组件
import 'nprogress-v2/dist/index.css' // 进度条样式
if (inBrowser) {
NProgress.configure({ showSpinner: false })
router.onBeforeRouteChange = () => {
NProgress.start() // 开始进度条
}
router.onAfterRouteChanged = () => {
busuanzi.fetch()
NProgress.done() // 停止进度条
}
}
更改后效果
组件
一些应用到vitepress
中的组件的自定义组件,比如首页文字下滑线,五彩纸屑,鼠标粒子效果,字数以及阅读时间
首页文字下划线
首页文字 的下划线,是利用了 @Theo-Messi 的组件
在 theme/components
文件夹,创建 HomeUnderline.vue
,写入入如下代码:
<script setup lang="ts">
import { useData } from "vitepress";
import { onMounted, onUnmounted } from "vue";
let sourceElement: Element | null = null;
let placeholder: Comment | null = null;
onMounted(() => {
const target = document.querySelector(".VPHero .text");
sourceElement = document.querySelector("#hero-text");
if (target && sourceElement) {
placeholder = document.createComment("hero-text-placeholder");
sourceElement.before(placeholder);
target.innerHTML = "";
target.appendChild(sourceElement);
}
});
onUnmounted(() => {
if (placeholder && sourceElement) {
placeholder.parentNode?.replaceChild(sourceElement, placeholder);
}
});
const { frontmatter: fm } = useData();
</script>
<template>
<span id="hero-text">
<span class="hero-text"
>{{ fm.hero?.text }}
<svg
class="hero-svg"
xmlns="http://www.w3.org/2000/svg"
width="240"
height="11"
viewBox="0 0 240 11"
fill="currentColor"
aria-hidden="true"
focusable="false"
>
<path
d="M20.766 10.187c.939-.024.386-.885.552-1.401 1.105-.301.553.626.962 1.061.685-.263 1.171-1.1 1.696-1.085.044.144.15.191.044.378.697-.736 2.21-.134 2.995-1.052a.55.55 0 0 1 .127.215 3.35 3.35 0 0 1-.204-1.204c.42-.034.751-.593.94 0-.255 0-.266.23-.377.416l.426-.273c.448.813-.586.316-.553.927.84-.306 1.802-1.037 2.476-.831.182.803-1.525.339-.608 1.023l-1.033-.268c.85 1.248-.625-.057.171 1.276 1.348.177 1.47-.478 2.818-.3.276-.479-.132-.66.144-1.124 1.857-.885 1.602 1.984 2.94.846.337-.555.42-1.582 1.442-1.08l-.276.889c1.298.038.668-1.348 2.06-.784-.226.368-1.005.344-.8.444.917.689.59-.545 1.27-.569l.16.827c1.371-.181 2.863-.827 4.388-1.037-.072.249-.326.512.044.746 1.912-.478 4.123-.058 6.007.368l.68-.727c.05.015.095.04.132.074a.275.275 0 0 1 .077.118c.014.044.015.091.004.136a.27.27 0 0 1-.07.122c.74.243 0-.445.354-.732.414-.062.552.383.315.603 1.248-.636 3.586-1.401 4.973-.694l-.254.22c1.06.249 1.105-1.477 2.127-.855l-.182.129c2.293.23 4.785-.478 6.564.52.293-1.017 2.272.393 2.365-1.022 1.327.664.967.927 2.813 1.348.492.052.702-.899 1.299-1.061l.044.731.79-.794.47.87.552-.205a.66.66 0 0 1-.332-.2.517.517 0 0 1-.132-.33c.873-.354 2.177.477 2.21.831l2.078-.679c-.039.301-.387.411-.657.607 1.105-.779.226.77 1.232.053-.144-.163.06-.44.077-.588.553.435 1.691.416 2.547.205l-.149.512c1.558.1 3.271-.31 5.018-.335-.636-.224-.514-1.109 0-1.204l.226.774c.32-.478-.552-.282.122-.884.652.076.464.875.094 1.138l.784-.287c.056.23-.127.358-.165.655.309-.478 1.387.75 1.834-.096l.05.23c1.746-.03 2.53-.316 3.95-.383 0-.674.553-.535.984-1.085 1.05.196 2.21.707 3.482.63.878-.343.243-.568.635-.955.077.612 1.332.535.69.985a15.353 15.353 0 0 0 3.83-.68c-.21-.243-.447-.353-.331-.563a.738.738 0 0 1 .275.01c.09.02.173.058.245.11a.58.58 0 0 1 .169.188c.04.072.061.151.062.232l-.088.067c2.127-.956 4.973 1.706 6.669.41l-.099.068 1.763-.684c.817.1-.481.478.127.842 1.9-1.043 3.022.12 4.586-.574 1.243 1.793 4.327-.167 5.979.956l-.1-.42c.426-.421.52.234.835.33-.05-.33-.464-.378-.205-.613 3.598-.545 7.438.598 11.129.956 1.348.11.757-2.203 2.465-1.195l-.481.794c2.719-.956 5.564 0 8.233-.77-.154.182-.16.416-.425.416.552.574 2.083.034 2.094-.435.42.053.1.425.354.665.552.339 1.42-.732 1.718-.158.05.09-.16.186-.265.23.37-.278 1.719.076 1.365-.589 1 .32 1.917-.287 2.713.105.553-.736 1.713.364 1.884-.683-.077 1.08 1.752.875 2.387.377-.215.326.553.345.299.794.718 0 1.381-.206 1.265-.76 1.315 1.305 2.686-1.018 3.415.645a45.888 45.888 0 0 1 6.078-1.17c-.082 1.075-2.138.09-2.066 1.218 1.834-.425 2.906-1.343 4.719-1.066.47.153-.276.478-.437.65 1.835-.43 3.537.148 5.172-.42 0 .1-.182.21-.348.291.321-.033.741.167.713-.325l-.315.13c-.497-.718 1.304-1.468 1.365-1.841-.553 1.396 1.602.377.707 2.137a.73.73 0 0 0 .337-.263.58.58 0 0 0 .1-.383c.315.1.409.297.083.665 1.155-.254.757-.78 1.801-.75 0 .233-.221.324-.337.601.553-.478 1.078-.908 1.951-.697-.056.143.044.33-.216.325 1.509-.048 2.603-1.195 4.249-.722-.513 1.023.553.349.625 1.243l.895-.254-.348-.44c.785.034 1.492-.602 2.155-.296l-.591.354 1.47-.139-.824-.354c.807-.444-.055-1.132.978-.86-.21.086.785.029 1.177.56.398-.278.801-.57 1.376-.335.138.291-.149.984-.055 1.176.398-.736 1.834-.168 2.337-.956-.143.227-.192.49-.138.745l.337-.597c.359.2.409.296.337.669 1.105.134-.309-1.138.967-.626-.105.048-.055.138-.27.23 1.287.277 2.519-.335 3.702 0 .326.903-1.05.195-.669.955 1.724-.129 3.592-.999 5.25-.74l-.31-.106c.277-1.262 1.221.66 2.083.086-.21.086-.298.693-.237.555 1.105.234 2.343-.249 4.083-.603l-.226.32c.657.311 1.763.216 2.481.383.226-.315.641-.253.403-.731 2.166 1.912 4.305-.89 6.228.726-.238 0-.553.268-.387.273l1.702-.244c-.111-.554-.21-.34-.553-.784.124-.163.292-.298.489-.392.198-.094.419-.145.644-.148-.774.34-.028.884.287 1.205-.049-.173.072-.354.05-.526.846 1.008.199-1.11 1.376-.407l-.077.287c.458-.134.889-.478 1.37-.401.177.645-.492.282-.552.803.685 0 1.403-1.162 1.994-.507-.298.167-.718.158-1.016.325.641.77.729.583 1.221.717h-.044l1.138.378-.282-.21c.928-1.635 1.752-.25 2.951-1.3-1.166.994-.21.592-.332 1.309.288.21.724.454.586.65.553-.564.89.478 1.696-.34 0 .235.581.044.431.627.713-.163-.149-.411-.077-.703 1.133-.76 2.514 1.061 4.139.029 1.376-.397 1.658-1.171 2.94-1.515.403.392-.393.836-.393.836.267.161.581.255.906.27a1.97 1.97 0 0 0 .934-.184c-.138.196 0 .373.172.64.519-.038.386-.831 1.05-.477a3.24 3.24 0 0 1-.553.918c.619-.192 1.243-.603 1.884-.79.149.412-.409.603-.646.856.718-.153 1.851-.296 2.105-.927l-.442-.248c.26 0 .105.559-.094.669-.63.478-.862-.258-.884-.478l.459-.134c-.387-1.382-1.818.148-2.719.033l.431-.956-.973.784c-.182-.263-.287-.822.166-.956-.624-.516-.591.33-1.105-.239-.055-.086-.028-.134.033-.172l-.646.273c.132-.201-.072-.703.309-.545-1.105-.617-1.873.674-2.26-.096l.099-.057c-1.596.272-.193.721-1.414 1.534l-.713-1.83-.188.721c-.16-.033-.481-.1-.409-.387-.63.478.089.32-.287.78-.752-.699-2.172.229-2.293-.957-.31.545.729.478-.127.813-.183-1.258-.978.181-1.658-.416.254-.636.917-.273.226-.875-.486 1.076-1.386-.282-2-.096-.066.87-1.332.32-2.354.579.078-.292-1.89-.54-2.818-.885l.033-.148c-.221.87-1.182.674-1.901.832a.906.906 0 0 1 .132-.55c.102-.169.258-.31.449-.406h-.669a.979.979 0 0 1-.34.327 1.167 1.167 0 0 1-.478.151l.194-.65c-.885 0-1.813.712-2.94.244-.083.607.84 1.725-.381 2.103-.034-.335-.056-.899.27-1.028-.105.043-.381.263-.585.12l.502-.545c-.508-.258-.287.478-.701.397 0-.478-.293-.35-.221-.722.11-.038.359.205.525 0a1.931 1.931 0 0 1-.691-.264 1.649 1.649 0 0 1-.503-.487c.028.268-.028.636-.37.684-.89 0-.282-.574-.79-.832-.227.325-.78-.033-.824.674-.259 0-.293-.34-.387-.535-.469.3-2.149.033-1.657.793l.116.053s-.05 0-.078.033c-1.525.66-3.105-.478-4.608-.224V3.34c-.895.244-1.984.106-2.636.593a.711.711 0 0 1-.402-.28.553.553 0 0 1-.084-.442c-.691.158-.774.416-1.746 0 .701-.396-.221-.373.713-.287-.879-.224-1.067-.607-2.039 0 .342-.597-.641-.774-1.067-.602l.608.445c-.436.053-.88.039-1.31-.043l.254-.794c-1.784-1.004-3.315 1.578-4.647-.067-.497.545.973.411.553 1.052-.829-.124-1.658-1.286-1.929-1.29-1.132-.479-1.105 1.137-2.282.812a.818.818 0 0 1 .031.774.938.938 0 0 1-.264.323 1.11 1.11 0 0 1-.397.198c-.829-.124-.994-1.214-.464-1.434.205 0 .299.072.288.168.27-.096.629-.21.303-.526l-.116.282c-.403-.297-1.552-.292-1.271-.75-.635.257-.281.477.183.616-1.061-.435-1.658-.053-2.763-.344.171.162.326.478.155.478-1.608-.378-.724.526-1.824.636-.608-.445.249-1.033-.862-.684-.668-.306-.127-.755.149-.985-1.016.536-1.867-.387-2.442-.478l.553-.22a1.892 1.892 0 0 1-.846.12l.293.573c-.309-.105-.553-.11-.553-.348-.326.368.227.956-.42 1.434-.403-.297-1.265.286-1.392-.478 1.298.272-.127-.76.978-.866a1.102 1.102 0 0 1-.851.024c-.044-.086.044-.157.133-.2-1.233-.689-.592.846-1.879.807.171-.42-.287-.808-.497-.721.519 0 .237.712-.249 1.027-.823-.34-.906.235-1.337.187l.491.162c-.176.426-.585.364-1.165.478-.045-.33.524-.22.326-.368-.652.736-1.437-.793-2.338-.306-.409-.291-.027-.798-.387-.999-1.011.54-1.077-.588-2.133-.148.293.574.349.435-.403.985l1.735-.387-1.105.822c.525 0 1.105-.35 1.42-.249-.553.478-.481.316-.238.794-.701-.86-1.425.478-2.21-.1l.044-1.41c-1.232-.641-2.21.702-3.823.334l.513.248c-.221.56-.994.072-1.519.292.055-.478-.271-.645-.492-.956.028.349-1.177-.043-1.337.899l-.707-.627c-1.305-.267-1.503 1.33-2.763 1.157.381-.507-.183-.846.657-1.21-.414 0-.79-.095-.801.23-.276-.263-1.199.646-1.575.215-.182.206-.243.698-.713.655a.337.337 0 0 1 0-.234c0 .234-.735.31-.331.837-1.271-1.478-3.592.095-4.708-1.172-.936.165-1.883.277-2.835.335.05-.139 0-.234.16-.186-1.143-.44-.707 1.352-2.005.86-.664-.765.69-.411.276-.703-.171-1.553-1.564.21-2.437-.702l.21-.091c-.663-.555-1.608.564-2.713.454a.326.326 0 0 0 0-.234c-.746.784-2.155 1.051-3.205 1.271.326-.607.475-.32.276-.956-.47.091.138.99-.801 1.167-.304-.33-.984-.622-1.078-1.282l.89-.019c-.459-.85-1.149.034-1.613-.114l.055-.368c-1.36.124-1.376 1.06-2.835.999l.155.282c-.796.956-.674-.521-1.465.172l-.248-.956c-.871.453-1.797.82-2.763 1.094.552-.698 1.658-1.06 2.315-1.477-.519 0-1.774.072-2.044.54.21-.09.475-.325.685-.181a2.832 2.832 0 0 1-1.094.83 3.298 3.298 0 0 1-1.42.27c.171-1.832-2.713-.455-3.482-1.865-1.834.693-3.652-.258-5.796-.13.774 1.435-.625.049-.481 1.507-.497.1-.685.076-.729 0l-1.525-.86c-.365-.421.469-.326.42-.65-1.106-.106-.465-.618-1.194-1 .155.521-.37.75-1 .56l.901.659c-1.52.793-1.338-1.214-2.868-.43l.48-.478c-.79.277-2.917 0-3.674 1.204-.144-.167-.332-.564 0-.674-1.89-.148-4.183 1.31-5.664.612l.138-.358c-.348.105-.602.678-1.05.325 0-.148.138-.359 0-.378-.182.124-.923.64-1.392.44l.386-.411c-1.85-.44-2.807 1.023-4.343 1.29 0-1.051-1.475-1.376-2.21-1.53V.685c-2.15-.086-3.625.956-5.598 1.4-1.265-1.118-4.188-.392-6.194-.99.31.182 0 .818-.37.957-.475-.206-1.266.755-1.221-.21h.165c-.375-.957-1.326-.67-2.072-.675l-.083 1.267c-2.006-1.778-5.106.813-6.227-.803-.459.33-1.045.34-1.498.67v-.68a12.396 12.396 0 0 0-3.575 0l.31-.478c-.912 0-1.072 1.98-1.912 2.042l-.288-1c-1.591.053-3.232-.774-4.763.192 0-.148.055-.445.31-.478-.746 0-2.918-.588-2.587.788-.06-.903-1.657-.038-2.48.388l.104-.689c-.685.875-.701 1.11-1.696 1.377-.243-.076-.238-.526.088-.368-.812-.32-.59.655-1.574.33l.342-.435c-.823-.029-.746.2-1.177.707-.503.287-1.564-.114-1.713-.712-.094.368-.52.875-1.011.717a.38.38 0 0 1 .013-.245.442.442 0 0 1 .164-.2c-1.393-.406-2 .851-2.973.235a.553.553 0 0 0-.182-.392 9.431 9.431 0 0 1 1.89.028c0-.616-.912-.688-.255-1.563-.685.478-1.845 1.54-2.713 1.286a.84.84 0 0 1-.1-.215l.061-.072a.668.668 0 0 0-.295 0 .61.61 0 0 0-.257.125 1.992 1.992 0 0 0-.718-.158c-.128-.507-1.023-.234-1.465-.244.072.67-.508.583.06 1.119-.07-.048.078-.086.366-.125a.528.528 0 0 0 .188-.076l-.028.062c.287-.033.663-.062 1.105-.09-.332.358-.68.654-1.183.3-.204.445-.43.894-.552 1.11-.647-.914-1.83-1.377-2.022-1.946-1.321.43-3.145.368-3.918 1.663-.376.177-.459-.344-.614-.535.216-.139.476-.13.586-.316-.74.354-2.249.216-2.381 1.105-.984-.364.491-.837-.818-.636l.166-.277c-2.675-1.291-4.09 2.433-7.068.755.204.105.304.148.354.296-3.316-.645-6.709 1.038-10.018-.062-.94-.205-1 .359-1.531.818l-.249-.713-.906.88c-1.315.679-2.47-1.65-4.117-.411l.254.478c-.624-.058-1.939.387-1.873-.177-.055.09-.166.516-.425.272l-.044-.372-1.487.712c-1.199-.215.078-1.506-1.658-1.492C.895 5.105-.22 6.114.04 6.362c.178.01.347.073.478.179a.645.645 0 0 1 .24.4l-.558.225C.17 8.279-.194 9.44 1.304 10.144l.917-.732.36.521-.818.1c.513.479.784 0 1.105-.305.07.225.233.42.458.55l.907-1.114c.149.43-.376.884.292 1.094.426-.516-.502-.956.233-1.314.513.478.403.898.933.44a.447.447 0 0 1 .012.336.525.525 0 0 1-.233.27c.476-.367 1.304-.214 1.525-.817.553.598 1.658-.248 1.691.808.29-.433.74-.77 1.277-.956-.752 1.3 1.724 0 1.591 1.348.553-1.162 2.21-.617 3.255-1.3-.055.095-.16.282-.265.23.624.061.823.391 1.237.592 0-.956.967-1.195 1.448-1.797.812.87-.392 1.118-.1 1.974-.082-.755 1.272-.813.973-1.434.614.53.514.248.862 1.008.028-1.17.553-.22.962-.956.873.54.282 1.086 1.182.689.453.354-.342.808-.342.808Zm21.793-2.93-.447.057.447-.058Zm1.818-.091a7.552 7.552 0 0 0-.801 0c-.072-.23 0-.478.171-.478-.083.186.348.305.63.478Zm-4.128-4.49c.288-.109.393 0 .442.159-.172.02-.343.053-.508.1v.081a.973.973 0 0 1 .066-.34Z"
/>
</svg>
</span>
{{ fm.hero?.textsuffix }}
</span>
</template>
<style scoped>
.hero-text {
display: inline-block;
position: relative;
}
.hero-svg {
position: absolute;
top: 1em;
left: 0.2em;
z-index: -1;
width: calc(100% - 0.7em);
height: auto;
color: var(--vp-c-brand);
}
</style>
然后,在 theme/index.ts
中注册全局组件
/* .vitepress/theme/index.ts */
import DefaultTheme from 'vitepress/theme'
import HomeUnderline from "./components/HomeUnderline.vue"
export default {
extends: DefaultTheme,
enhanceApp({app}) {
// 注册全局组件
app.component('HomeUnderline' , HomeUnderline)
}
}
最后回到docs/index.md
,插入组件
<!-- index.md -->
<HomeUnderline />
看下插入后的效果
五彩纸屑
首页的一个纸屑动画效果,使用@catdad/canvas-confetti,参考官网的写法,我们再用组件封装下
在 theme/components
文件夹中创建 confetti.vue
,写入以下代码
<script setup lang="ts">
import confetti from "canvas-confetti";
import { inBrowser } from "vitepress";
if (inBrowser) {
/* 纸屑 */
confetti({
particleCount: 100,
spread: 170,
origin: { y: 0.6 },
});
}
</script>
npm 打包报错,请使用如下方式
<script setup lang="ts">
import { onMounted } from "vue";
import confetti from "canvas-confetti";
onMounted(() =>
/* 纸屑 */
confetti({
particleCount: 100,
spread: 170,
origin: { y: 0.6 },
})
);
</script>
然后,在 theme/index.ts
中注册全局组件
/* .vitepress/theme/index.ts */
import DefaultTheme from 'vitepress/theme'
import confetti from "./components/confetti.vue"
export default {
extends: DefaultTheme,
enhanceApp({app}) {
// 注册全局组件
app.component('confetti' , confetti)
}
}
最后回到docs/index.md
,插入组件
<!-- index.md -->
<confetti />
在首页刷新下,就可以看到效果:
鼠标粒子效果
给鼠标点击以及移动添加动画效果
配置组件,在 .vitepress/theme/components
中分别新建MouseClick.vue
以及MouseFollower.vue
,写入以下代码
<template>
<canvas
ref="canvas"
style="position: fixed; left: 0; top: 0; pointer-events: none; z-index: 999999"
></canvas>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
const canvas = ref(null);
let animationFrameId = null;
let particles = [];
let circles = [];
const colors = ["#FF1461", "#18FF92", "#5A87FF", "#FBF38C"];
// 设置画布大小
function setCanvasSize() {
const canvasEl = canvas.value;
canvasEl.width = window.innerWidth * 2;
canvasEl.height = window.innerHeight * 2;
canvasEl.style.width = window.innerWidth + "px";
canvasEl.style.height = window.innerHeight + "px";
canvasEl.getContext("2d").scale(2, 2);
}
// 创建粒子
function createParticle(x, y) {
const angle = Math.random() * Math.PI * 2;
const speed = 2 + Math.random() * 3;
const radius = 4 + Math.random() * 8;
const color = colors[Math.floor(Math.random() * colors.length)];
return {
x,
y,
radius,
color,
speedX: Math.cos(angle) * speed,
speedY: Math.sin(angle) * speed,
life: 100 + Math.random() * 100, // 生命周期
currentLife: 0,
draw(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
},
update() {
this.x += this.speedX;
this.y += this.speedY;
this.currentLife++;
this.radius *= 0.98; // 逐渐缩小
// 根据生命周期调整透明度
const progress = this.currentLife / this.life;
if (progress > 0.5) {
this.radius *= 0.95;
}
return this.currentLife < this.life;
},
};
}
// 创建圆形扩散效果
function createCircle(x, y) {
const radius = 5 + Math.random() * 10;
const color = "#FFF";
return {
x,
y,
radius,
color,
maxRadius: 80 + Math.random() * 80,
lineWidth: 6,
alpha: 0.5,
speed: 1 + Math.random(),
draw(ctx) {
ctx.globalAlpha = this.alpha;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.color;
ctx.stroke();
ctx.globalAlpha = 1;
},
update() {
this.radius += this.speed * 2;
this.alpha *= 0.97;
this.lineWidth *= 0.98;
return this.radius < this.maxRadius && this.alpha > 0.01;
},
};
}
// 创建随机圆形
function createRandomCircle(x, y) {
const radius = 1;
const color = colors[Math.floor(Math.random() * colors.length)];
const maxRadius = 50 + Math.random() * 40;
return {
x,
y,
radius,
color,
maxRadius,
alpha: 1,
speed: 1 + Math.random(),
draw(ctx) {
ctx.globalAlpha = this.alpha;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
ctx.globalAlpha = 1;
},
update() {
this.radius += this.speed * 3;
this.alpha *= 0.96;
return this.radius < this.maxRadius && this.alpha > 0.01;
},
};
}
// 动画循环
function animate() {
const ctx = canvas.value.getContext("2d");
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
// 更新并绘制粒子
particles = particles.filter((particle) => {
particle.update();
particle.draw(ctx);
return particle.currentLife < particle.life;
});
// 更新并绘制圆形
circles = circles.filter((circle) => {
const shouldKeep = circle.update();
circle.draw(ctx);
return shouldKeep;
});
animationFrameId = requestAnimationFrame(animate);
}
// 处理点击事件
function handleClick(e) {
const x = e.clientX || e.touches[0].clientX;
const y = e.clientY || e.touches[0].clientY;
// 创建粒子
for (let i = 0; i < 20; i++) {
particles.push(createParticle(x, y));
}
// 创建圆形扩散效果
circles.push(createCircle(x, y));
// 创建随机圆形
circles.push(createRandomCircle(x, y));
}
onMounted(() => {
setCanvasSize();
const tapEvent = "ontouchstart" in window ? "touchstart" : "mousedown";
window.addEventListener(tapEvent, handleClick);
window.addEventListener("resize", setCanvasSize);
animate();
});
onUnmounted(() => {
const tapEvent = "ontouchstart" in window ? "touchstart" : "mousedown";
window.removeEventListener(tapEvent, handleClick);
window.removeEventListener("resize", setCanvasSize);
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
});
</script>
<template>
<canvas
ref="canvas"
style="position: fixed; left: 0; top: 0; pointer-events: none; z-index: 999999"
></canvas>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
const canvas = ref(null);
let ctx = null;
let particles = [];
let mouse = { x: globalThis?.innerWidth / 2, y: globalThis?.innerHeight / 2 };
let targetMouse = { x: globalThis?.innerWidth / 2, y: globalThis?.innerHeight / 2 };
let lastMouse = { x: globalThis?.innerWidth / 2, y: globalThis?.innerHeight / 2 };
let animationFrameId = null;
class Particle {
constructor() {
this.reset();
}
reset() {
// 随机角度
this.angle = Math.random() * Math.PI * 2;
// 更小的随机半径 (15-25)
this.radius = Math.random() * 40 + 25;
// 随机旋转速度
this.speed = (Math.random() * 2 + 2) * 0.01;
// 更小的粒子大小 (1-2)
this.size = Math.random() * 3 + 1;
// 随机颜色
this.hue = Math.random() * 360;
// 随机方向
this.clockwise = Math.random() > 0.5;
// 更小的随机偏移
this.offsetX = (Math.random() - 0.5) * 10;
this.offsetY = (Math.random() - 0.5) * 10;
// 生命周期
this.life = Math.random() * 0.5 + 0.5;
this.maxLife = this.life;
// 拖尾效果
this.trail = [];
this.trailLength = Math.floor(Math.random() * 3) + 2; // 2-4个拖尾点
}
update() {
// 更新角度
this.angle += this.speed * (this.clockwise ? 1 : -1);
// 计算目标位置
const targetX = mouse.x + Math.cos(this.angle) * this.radius + this.offsetX;
const targetY = mouse.y + Math.sin(this.angle) * this.radius + this.offsetY;
// 添加当前位置到拖尾数组
if (!this.x) {
this.x = targetX;
this.y = targetY;
}
// 计算实际移动(添加弹性移动)
const dx = targetX - this.x;
const dy = targetY - this.y;
this.x += dx * 0.15;
this.y += dy * 0.15;
// 更新拖尾
this.trail.unshift({ x: this.x, y: this.y });
if (this.trail.length > this.trailLength) {
this.trail.pop();
}
// 更新生命周期
this.life -= 0.002;
if (this.life <= 0) {
this.reset();
}
}
draw() {
if (!ctx) return;
const alpha = this.life / this.maxLife;
// 绘制拖尾
if (this.trail.length > 1) {
ctx.beginPath();
ctx.moveTo(this.trail[0].x, this.trail[0].y);
for (let i = 1; i < this.trail.length; i++) {
const point = this.trail[i];
ctx.lineTo(point.x, point.y);
}
ctx.strokeStyle = `hsla(${this.hue}, 70%, 60%, ${alpha * 0.5})`;
ctx.lineWidth = this.size;
ctx.lineCap = "round";
ctx.stroke();
}
// 绘制主粒子
ctx.fillStyle = `hsla(${this.hue}, 70%, 60%, ${alpha})`;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size * 0.5, 0, Math.PI * 2);
ctx.fill();
}
}
// 平滑跟随鼠标
function updateMousePosition() {
const dx = targetMouse.x - mouse.x;
const dy = targetMouse.y - mouse.y;
// 计算鼠标移动速度
const mouseSpeed = Math.sqrt(
Math.pow(targetMouse.x - lastMouse.x, 2) + Math.pow(targetMouse.y - lastMouse.y, 2)
);
// 根据鼠标速度调整跟随速度
const followSpeed = Math.min(0.15, 0.15 / (1 + mouseSpeed * 0.005));
mouse.x += dx * followSpeed;
mouse.y += dy * followSpeed;
lastMouse.x = mouse.x;
lastMouse.y = mouse.y;
}
function handleMouseMove(e) {
const rect = canvas.value.getBoundingClientRect();
targetMouse.x = e.clientX - rect.left;
targetMouse.y = e.clientY - rect.top;
}
function animate() {
if (!ctx || !canvas.value) return;
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
updateMousePosition();
// 随机添加新粒子
if (particles.length < 25 && Math.random() < 0.1) {
particles.push(new Particle());
}
// 更新和绘制所有粒子
particles.forEach((particle) => {
particle.update();
particle.draw();
});
animationFrameId = requestAnimationFrame(animate);
}
function handleResize() {
if (!canvas.value) return;
canvas.value.width = globalThis.innerWidth;
canvas.value.height = globalThis.innerHeight;
}
function initParticles() {
particles = [];
// 初始创建12-15个粒子
const initialCount = Math.floor(Math.random() * 4) + 12;
for (let i = 0; i < initialCount; i++) {
particles.push(new Particle());
}
}
onMounted(() => {
if (typeof globalThis !== "undefined") {
ctx = canvas.value.getContext("2d");
handleResize();
initParticles();
globalThis.addEventListener("resize", handleResize);
globalThis.addEventListener("mousemove", handleMouseMove);
animate();
}
});
onUnmounted(() => {
if (typeof globalThis !== "undefined") {
globalThis.removeEventListener("resize", handleResize);
globalThis.removeEventListener("mousemove", handleMouseMove);
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
}
});
</script>
<style scoped>
canvas {
pointer-events: none;
position: fixed;
left: 0;
top: 0;
z-index: 999999;
}
</style>
然后,在 index.ts
中注册全局组件
/* .vitepress/theme/index.ts */
import DefaultTheme from "vitepress/theme";
import MouseClick from "./components/MouseClick.vue";
import MouseFollower from "./components/MouseFollower.vue";
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// 注册全局组件
app.component("MouseClick", MouseClick);
app.component("MouseFollower", MouseFollower);
},
};
使用上建议使用 Always 插槽:layout-top
由于我们一个插槽使用了多个组件,我们将其放在 MLayout.vue
组件中
<script lang="ts" setup>
import DefaultTheme from "vitepress/theme";
import MouseClick from "./MouseClick.vue";
import MouseFollower from "./MouseFollower.vue";
</script>
<template>
<DefaultTheme.Layout>
<template #layout-top>
<MouseFollower />
<MouseClick />
</template>
</DefaultTheme.Layout>
</template>
最后看下效果
字数及阅读时间
这里使用了 @shiheme/appbeebee 博客组件
在 theme/components
文件夹,新建 ArticleMetadata.vue
,在 theme/untils
文件夹,新建 functions.ts
再这两个文件中分别写入以下代码
<script lang="ts" setup>
import { useData } from "vitepress";
import { computed, ref, onMounted } from "vue";
import { countWord } from "../untils/functions";
const { page } = useData();
const date = computed(() => new Date(page.value.lastUpdated!));
const wordCount = ref(0);
const imageCount = ref(0);
const wordTime = computed(() => {
return (wordCount.value / 275) * 60;
});
const imageTime = computed(() => {
const n = imageCount.value;
if (imageCount.value <= 10) {
// 等差数列求和
return n * 13 + (n * (n - 1)) / 2;
}
return 175 + (n - 10) * 3;
});
// 阅读时间
const readTime = computed(() => {
return Math.ceil((wordTime.value + imageTime.value) / 60);
});
function analyze() {
document.querySelectorAll(".meta-des").forEach((v) => v.remove());
const docDomContainer = window.document.querySelector("#VPContent");
const imgs = docDomContainer?.querySelectorAll<HTMLImageElement>(".content-container .main img");
imageCount.value = imgs?.length || 0;
const words = docDomContainer?.querySelector(".content-container .main")?.textContent || "";
wordCount.value = countWord(words);
}
onMounted(() => {
// 初始化时执行一次
analyze();
});
</script>
<template>
<div class="word">
<p>
<svg
t="1724572866572"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="18131"
width="16"
height="16"
>
<path
d="M168.021333 504.192A343.253333 343.253333 0 0 1 268.629333 268.8a342.229333 342.229333 0 0 1 243.285334-100.778667A341.504 341.504 0 0 1 755.029333 268.8c9.856 9.898667 19.2 20.394667 27.733334 31.402667l-60.16 46.976a8.021333 8.021333 0 0 0 2.986666 14.122666l175.701334 43.008a8.021333 8.021333 0 0 0 9.898666-7.68l0.810667-180.906666a7.936 7.936 0 0 0-12.885333-6.314667L842.666667 253.44a418.858667 418.858667 0 0 0-330.922667-161.493333c-229.12 0-415.488 183.594667-419.797333 411.818666a8.021333 8.021333 0 0 0 8.021333 8.192H160a7.978667 7.978667 0 0 0 8.021333-7.808zM923.946667 512H864a7.978667 7.978667 0 0 0-8.021333 7.808 341.632 341.632 0 0 1-26.88 125.994667 342.186667 342.186667 0 0 1-73.685334 109.397333 342.442667 342.442667 0 0 1-243.328 100.821333 342.229333 342.229333 0 0 1-270.976-132.224l60.16-46.976a8.021333 8.021333 0 0 0-2.986666-14.122666l-175.701334-43.008a8.021333 8.021333 0 0 0-9.898666 7.68l-0.682667 181.034666c0 6.698667 7.68 10.496 12.885333 6.314667L181.333333 770.56a419.072 419.072 0 0 0 330.922667 161.408c229.205333 0 415.488-183.722667 419.797333-411.818667a8.021333 8.021333 0 0 0-8.021333-8.192z"
fill="#8a8a8a"
p-id="18132"
></path>
</svg>
更新: {{ date.toLocaleDateString() }}
<svg
t="1724571760788"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="6125"
width="16"
height="16"
>
<path
d="M204.8 0h477.866667l273.066666 273.066667v614.4c0 75.093333-61.44 136.533333-136.533333 136.533333H204.8c-75.093333 0-136.533333-61.44-136.533333-136.533333V136.533333C68.266667 61.44 129.706667 0 204.8 0z m307.2 607.573333l68.266667 191.146667c13.653333 27.306667 54.613333 27.306667 61.44 0l102.4-273.066667c6.826667-20.48 0-34.133333-20.48-40.96s-34.133333 0-40.96 13.653334l-68.266667 191.146666-68.266667-191.146666c-13.653333-27.306667-54.613333-27.306667-68.266666 0l-68.266667 191.146666-68.266667-191.146666c-6.826667-13.653333-27.306667-27.306667-47.786666-20.48s-27.306667 27.306667-20.48 47.786666l102.4 273.066667c13.653333 27.306667 54.613333 27.306667 61.44 0l75.093333-191.146667z"
fill="#777777"
p-id="6126"
></path>
<path
d="M682.666667 0l273.066666 273.066667h-204.8c-40.96 0-68.266667-27.306667-68.266666-68.266667V0z"
fill="#E0E0E0"
opacity=".619"
p-id="6127"
></path>
</svg>
字数: {{ wordCount }} 字
<svg
t="1724572797268"
class="icon"
viewBox="0 0 1060 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="15031"
width="16"
height="16"
>
<path
d="M556.726857 0.256A493.933714 493.933714 0 0 0 121.929143 258.998857L0 135.021714v350.390857h344.649143L196.205714 334.482286a406.820571 406.820571 0 1 1-15.908571 312.649143H68.937143A505.819429 505.819429 0 1 0 556.726857 0.256z m-79.542857 269.531429v274.907428l249.197714 150.966857 42.422857-70.070857-212.114285-129.389714V269.787429h-79.542857z"
fill="#8a8a8a"
p-id="15032"
></path>
</svg>
时长: {{ readTime }} 分钟
</p>
</div>
</template>
<style>
.word {
color: var(--vp-c-text-2);
font-size: 15px;
}
.icon {
display: inline-block;
transform: translate(0px, 2px);
}
</style>
const pattern =
/[a-zA-Z0-9_\u0392-\u03C9\u00C0-\u00FF\u0600-\u06FF\u0400-\u04FF]+|[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u3040-\u309F\uAC00-\uD7AF]+/g;
export function countWord(data: string) {
const m = data.match(pattern);
let count = 0;
if (!m) {
return 0;
}
for (let i = 0; i < m.length; i += 1) {
if (m[i].charCodeAt(0) >= 0x4e00) {
count += m[i].length;
} else {
count += 1;
}
}
return count;
}
然后,在 index.ts
中注册全局组件
/* .vitepress/theme/index.ts */
import DefaultTheme from 'vitepress/theme'
import ArticleMetadata from "./components/ArticleMetadata.vue"
export default {
extends: DefaultTheme,
enhanceApp({app}) {
// 注册全局组件
app.component('ArticleMetadata' , ArticleMetadata)
}
}
在使用上面,有些特殊,需要参照官网 Markdown 的高级配置 在 config.mts
中配置
/* .vitepress/config.mts */
import { defineConfig } from 'vitepress'
export default defineConfig({
//markdown配置
markdown: {
// 组件插入h1标题下
config: (md) => {
md.renderer.rules.heading_close = (tokens, idx, options, env, slf) => {
let htmlResult = slf.renderToken(tokens, idx, options);
if (tokens[idx].tag === 'h1') htmlResult += `<ArticleMetadata />`;
return htmlResult;
}
}
}
})