slidev

Slidev 是一个基于 Markdown 的演示文稿工具,它允许用户使用简单的语法创建漂亮的幻灯片演示。Slidev 提供了丰富的主题和布局选项,使用户能够定制他们的演示文稿。此外,Slidev 还支持代码高亮、动画效果、演讲者模式等功能,使演示更加生动和吸引人

想要更进阶的效果,需要使用Reveal.js:一个功能丰富的 HTML 演示框架,支持丰富的 JavaScript API 和事件监听

配置与启动

1
2
npm init slidev@latest
slidev #启动项目

项目结构

Slidev 对项目结构进行了一些约定,以尽量减少配置项,使功能扩展更加灵活直观。

基本结构如下所示:

1
2
3
4
5
6
7
8
9
your-slidev/
├── components/ # 自定义组件
├── layouts/ # 自定义布局
├── public/ # 静态资源
├── setup/ # 自定义 setup / hooks
├── styles/ # 自定义样式
├── index.html # 注入的 index.html
├── slides.md # 幻灯片主入口
└── vite.config.ts # 扩展 vite 配置

以上所有均为可选。

组件

约定:./components/*.{vue,js,ts,jsx,tsx,md}

此目录中的组件可以在幻灯片的 Markdown 中直接使用,其组件名与文件名相同。

1
2
3
4
5
your-slidev/
├── ...
└── components/
├── MyComponent.vue
└── HelloWorld.ts

布局

约定:./layouts/*.{vue,js,ts,jsx,tsx}

1
2
3
4
5
your-slidev/
├── ...
└── layouts/
├── cover.vue
└── my-cool-theme.vue

你可以为布局文件使用任何文件名。然后只需在你的 YAML 头部使用文件名引用你的布局。

1
2
3
---
layout: my-cool-theme
---

如果你提供的布局与内置布局或主题布局重名的话,你的自定义布局将优先于内置/主题布局。优先级为 本地 > 主题 > 内置

在布局组件中,你可以使用 <slot/> 展示幻灯片内容。比如:

1
2
3
4
5
6
<!-- default.vue -->
<template>
<div class="slidev-layout default">
<slot />
</div>
</template>

静态资源

约定:./public/*

开发过程中,此目录中的资源文件将在 / 下提供,并会按原样复制到 dist 目录的根目录中

样式

约定:./style.css | ./styles/index.{css,js,ts}

遵循上述约定的文件将被注入到 App 的根目录中。如果你需要引入多个 css 入口,你可以按如下方式创建结构并自行管理引入顺序。

1
2
3
4
5
6
7
your-slidev/
├── ...
└── styles/
├── index.ts
├── base.css
├── code.css
└── layouts.css
1
2
3
4
5
// styles/index.ts

import './base.css'
import './code.css'
import './layouts.css'

样式得益于 UnoCSSPostCSS,你拥有开箱即用的 css 嵌套和 at-directives。示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.slidev-layout {
--uno: px-14 py-10 text-[1.1rem];

h1, h2, h3, h4, p, div {
--uno: select-none;
}

pre, code {
--uno: select-text;
}

a {
color: theme('colors.primary');
}
}

全局图层

约定:global-top.vue | global-bottom.vue

拓展Markdown语法

除了基本的Markdown语法外,slidev拓展了markdown语法,支持内联的HTML和Vue组件,也支持使用UnoCSS来编写样式

分隔幻灯片使用---

扉页及布局

1
2
3
4
5
---
layout: center
background: './images/background-1.png'
class: 'text-white'
---

代码高亮

1
2
3
4
5
6
7
8
9
````md magic-move {at:4}
```ts {2-3|5|all}{lines:true,startLine:5}{maxHeight:'100px'}
function add(
a: Ref<number> | number,
b: Ref<number> | number
) {
return computed(() => unref(a) + unref(b))
}
```

第一个步骤为2-3行高亮,下一个步骤第5行高亮,再下一个步骤全部高亮

lines:true为当前代码块显示行号,startLine为设置起始行突出显示

展示不下的情况使用maxHeight设置固定高度(同时会启用滚动)

如果想把代码块变成编辑器,跟在代码块````ts后加上{monaco},需要直接运行js或ts代码的话使用{monaco-run},并通过{autorun:false}`设置是否自动运行

展示代码间差异:使用~~~来分隔代码的原始版本和修改后的版本

1
2
3
4
5
```ts {monaco-diff}
console.log('Original text')
~~~
console.log('Modified text')
```

内联样式

在 Markdown 中直接使用 <style> 标签来覆盖当前幻灯片的样式

1
2
3
4
5
<style>
h1 {
color: red
}
</style>

静态资源

可以使用本地或远程的 URL 的图片。

如果是远程资源,内置的 vite-plugin-remote-assets 将在第一次运行时把它们缓存到磁盘中,即便是大图也能实现立即加载。

如果是本地资源,请将资源放置到 public 文件夹 中并使用 / 开头的 URL 来引用它们。

可以使用markdown的格式感叹号[](图片路径)的格式

自定义尺寸和样式用下面<img>的格式

<img src="/pic.png" class="m-40 h-40 rounded shadow" />

备注

在 Markdown 中,每张幻灯片中的最后一个注释块将被视为备注.供你在演示时参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---
layout: cover
---

# 第 1 页

This is the cover page.

<!-- 这是一条备注 -->

---

# 第 2 页

<!-- 这不是一条备注,因为它在幻灯片内容前 -->

The second page

<!--
这是另一条备注
-->

图标

Slidev 允许你在 Markdown 中在安装对应包后访问几乎所有的开源的图标集。这得益于 unplugin-iconsIconify

图标 ID 遵循 Iconify 的命名规则 {collection-name}-{icon-name}。例如:

  • 使用 Material Design Icons,其规则为 <mdi-account-circle /> -
  • 使用 Carbon,其规则为 <carbon-badge /> -
  • 使用 Unicons Monochrome,其规则为 <uim-rocket /> -
  • 使用 Twemoji,其规则为 <twemoji-cat-with-tears-of-joy /> -
  • 使用 SVG Logos,其规则为 <logos-vue /> -
  • 还有更多…

你可以通过 Icônes 来浏览访问所有可用的图标。

可以像其他 HTML 元素一样对图标的样式进行修改。例如:

1
2
3
<uim-rocket />
<uim-rocket class="text-3xl text-red-400 mx-2" />
<uim-rocket class="text-3xl text-orange-400 animate-ping" />

布局

vue具名插槽

two-cols 布局 中,你可以采用左(default 插槽)右(right 插槽)两列的布局方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
layout: two-cols
---

<template v-slot:default>
<!--可使用slidev提供的语法糖::default::代替-->

# Left

This shows on the left

</template>
<template v-slot:right>

# Right

This shows on the right

</template>

代码片段导入

通过以下语法从现有文件中导入代码片段

@的值对应于您的软件包根目录。建议将代码片段放在@/snippets中,以便与Monaco编辑器兼容。或者,您也可以从相对路径导入

也支持代码高亮等功能

1
<<< @/snippets/snippet.js ts {2,3|5}{lines:true}

LaTex行高亮

1
2
3
4
5
6
7
8
$$ {1|3|all}
\begin{array}{c}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} &
= \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
\nabla \cdot \vec{\mathbf{B}} & = 0
\end{array}
$$

图表缩放和主题

1
2
3
4
5
6
```mermaid {theme: 'neutral', scale: 0.8}
graph TD
B[Text] --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
```

markdown嵌套

意味着可以将 slides.md 分割成多个文件,并可以按照你的需求组织它们。

1
2
3
4
5
6
7
8
9
10
# Page 1

This is a normal page

---
src: ./subpage2.md
---

<!-- this page will be loaded from './subpage2.md' -->
Inline content will be ignored

有了markdown嵌套的加持,对页面进行重用变得很容易

MDC 语法

参考

快捷键

快捷键 按钮 说明
f 切换全屏
right / space 下一动画或幻灯片
left 上一动画或幻灯片
up - 上一张幻灯片
down - 下一张幻灯片
o 切换 幻灯片总览
d 切换暗黑模式
- 切换 摄像头视图
- 演讲录制
- 进入 演讲者模式
- 切换 集成编辑器
- 下载幻灯片 (仅在 单页(SPA)构建 中支持)
- 显示该演示文稿的信息
- 显示设置菜单
g - 显示“前往…”

动画

点击动画

v-click

要为元素应用 “点击动画”,可以使用 v-click 指令或 <v-click> 组件

1
2
3
4
5
6
7
<!-- 组件用法:
此行将在你点击“下一页”前不可见 -->
<v-click> Hello World! </v-click>

<!-- 指令用法:
此行将在你第二次点击“下一页”时不可见 -->
<div v-click class="text-xl"> Hey! </div>

v-after

v-after 仅作为指令提供。它将在触发前一个 v-click 时使元素变为可见。

1
2
<div v-click> Hello </div>
<div v-after> World </div>

当你点击“下一步”时,“Hello”和“World”将同时显示。

点击后隐藏

你可以为 v-clickv-after 添加 .hide 修饰符,使元素在点击后不可见,而不是显示出来。

1
2
3
4
5
6
<div v-click> Visible after 1 click </div>
<div v-click.hide> Hidden after 2 click </div>
<div v-after.hide> Hidden after 2 click </div>

<v-click> Visible after 1 click </v-click>
<v-click hide> Hidden after 2 click </v-click>

v-clicks

v-clicks 仅作为一个组件提供。它是将 v-click 指令应用于所有子元素的简写。它在处理列表和表格时尤其出色。

它同时接受用于嵌套列表的 depth 属性

此外还可以使用 every 属性来指定每次点击后要显示的项目数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<v-clicks depth="2">

- Item 1
- Item 1.1
- Item 1.2
- Item 2
- Item 2.1
- Item 2.2

</v-clicks>

<v-clicks every="2">

- Item 1 (part 1)
- Item 1 (part 2)
- Item 2 (part 1)
- Item 2 (part 2)

</v-clicks>

次数定位

默认情况下,点击动画是逐个进行的。你可以使用 at prop 或 v-click 指令自定义元素的动画位置。

与 CSS 布局系统一样,点击动画元素可以是“相对”或“绝对”的

相对定位

相对元素的实际位置是根据之前的相对元素计算出来的:

1
2
3
4
5
6
7
8
<div v-click> visible after 1 click </div>
<v-click at="+2"><div> visible after 3 clicks </div></v-click>
<div v-click.hide="'-1'"> hidden after 2 clicks </div>

```js {none|1|2}{at:'+5'}
1 // highlighted after 7 clicks
2 // highlighted after 8 clicks
```
  1. <div v-click>: 这段代码表示这个元素在第1次点击后显示。
  2. <v-click at="+2"><div> visible after 3 clicks </div></v-click>: 这段代码表示这个元素在第3次点击后显示。
  3. <div v-click.hide="'-1'": 这段代码表示这个元素在相对于前一个v-click指令(即第3次点击)提前1次点击时隐藏,即在第2次点击时隐藏。

只有以 '+''-'(如 '+1')开头的字符串值才会被视为相对位置:

属性值 类型
'-1', '+1' 相对
+1 = = =1 绝对
'1' 绝对

因此,不要忘记为相对值加上单引号。

绝对定位

传递的值为需要操作的精确点击次数

1
2
3
4
5
6
7
8
<div v-click="3"> visible after 3 clicks </div>
<v-click at="2"><div> visible after 2 clicks </div></v-click>
<div v-click.hide="1"> hidden after 1 click </div>

```js {none|1|2}{at:3}
1 // highlighted after 3 clicks
2 // highlighted after 4 clicks
```

下面通过{at:1}同步了两行代码的高亮

1
2
3
4
5
6
7
8
9
```js {1|2}{at:1}
1 + 1
'a' + 'b'
```

```js {1|2}{at:1}
2
'ab'
```

进入和离开

你也可以通过传递一个数组为 v-click 指令指定进入和离开的时间。显示/隐藏周期是不包含结束索引在内的。

1
<div v-click="[2, 4]">This will be shown on the 2nd and 3rd clicks, and hide again after the 4th.</div>

元素过渡

当你将 v-click 指令应用于元素时,它将附加 slidev-vclick-target 类名。相反,当元素被隐藏时,类名 slidev-vclick-hidden 会被附加。例如:

1
<div class="slidev-vclick-target slidev-vclick-hidden">Text</div>

点击后,它将变成

1
<div class="slidev-vclick-target">Text</div>

默认情况下,这些类别会使用透明度过渡:

1
2
3
4
5
6
7
8
9
10
/* below shows the default style */

.slidev-vclick-target {
transition: opacity 100ms ease;
}

.slidev-vclick-hidden {
opacity: 0;
pointer-events: none;
}

你可以覆盖它们,在自定义样式表中定制过渡效果。

例如,你可以通过以下方法实现放大转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* styles.css */

.slidev-vclick-target {
transition: all 500ms ease;
}

.slidev-vclick-hidden {
transform: scale(0);
}

/*
仅为某些幻灯片或布局指定动画
*/
.slidev-page-7,
.slidev-layout.my-custom-layout {
.slidev-vclick-target {
transition: all 500ms ease;
}

.slidev-vclick-hidden {
transform: scale(0);
}
}

特定方向动画

在某些情况下,你可能希望在前进和后退时使用不同的动画。在切换幻灯片时,Slidev 将对 slide 容器应用 .slidev-nav-go-forward.slidev-nav-go-backward 类。

因此,你可以利用这一点为不同的方向应用不同的动画:

1
2
3
4
5
6
7
/* example: delay on only forward but not backward */
.slidev-nav-go-forward .slidev-vclick-target {
transition-delay: 500ms;
}
.slidev-nav-go-backward .slidev-vclick-target {
transition-delay: 0;
}

幻灯片过渡

1
2
3
---
transition: slide-left
---

内置过渡:

  • fade - 交叉淡入/淡出
  • fade-out - 淡出后再淡入
  • slide-left - 向左滑动(后退时向右滑动)
  • slide-right - 向右滑动(后退时向左滑动)
  • slide-up - 向上方滑动(向后时向下方滑动)
  • slide-down - 向下滑动(向后时滑动到顶部)
  • view-transition - 使用视图转换 API (View Transitions API) 的幻灯片

可以使用vue的动画

[[前端#过渡动画|参考vue过渡动画]]

v-motion

此组件使用的其实是motion库,一个轻量级的动画库

motion库的官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div v-motion 
:initial="{ opacity: 0 }"
:visible="{ opacity: 1, transition: { delay: 500, duration: 600 } }"
style="{
backdrop-filter: blur(100px) brightness(90%);
background-color: rgba(255, 255, 255, 0.8);
border-radius: 10px;
padding: 20px;
border: 1px solid #ccc;
border-radius: 15px;
width: 270px;
position: absolute;
top: 10px;
left: 10px;
z-index: 9999;
text-align: center;
font-size: 24px;
box-shadow: 20px 26px 14px -6px #ccc;
line-height: 1;
}"
>
演示效果
</div>

v-motion目前已知有:

  • initial:初始状态。在元素加载时应用的动画。
  • enter:进入状态。当元素进入视图时应用的动画。
  • leave:离开状态。当元素离开视图时应用的动画。
  • click-3: 点击第3次的时候触发
  • **visible**:元素每次在视野内时将装备的属性。一旦离开视野,initial将应用这些属性。
  • **visible-once**:元素进入视图后将具备的属性。
  • **hovered**:当指针进入元素区域时,该元素将具备的属性。
  • **focused**:当元素获得焦点时,元素将具备的属性。
  • **tapped**:单击(鼠标)或点击(触摸设备)时元素将具备的属性。

v-mark

1
v-mark="{ at: 2, color: '#234', type: 'circle' }"

type:

  • circle: 圆圈
  • underline下划线

在 Slidev 中更换主题非常简单。在 frontmatter 中添加 theme: 配置即可。

1
2
3
---
theme: seriph
---

提示 若要从作用域包中安装主题,你需要提供完整的命名空间,例如 @organization/slidev-theme-name

在服务启动后,它会自动提示你是否安装该主题

注意:主题也可以提供组件,参考他们各自的文档

拓展插件

扩展插件是你可以在演示文稿中使用的附加组件、布局、样式、配置等集。

  • 它们不影响幻灯片的全局样式
  • 你可以在同一演示文稿中使用多个插件

为了使用扩展插件,你必须通过以下方式手动安装它们:

1
npm install [slidev-addon-package1] [slidev-addon-package2]

然后,你需要在 frontmatter 中声明你的扩展插件:

1
2
3
4
5
---
addons:
- slidev-addon-package1
- slidev-addon-package2
---

或在package.json中

1
2
3
4
5
6
7
8
9
// package.json
{
"slidev": {
"addons": [
"slidev-addon-package1",
"slidev-addon-package2"
]
}
}

内置

组件

  • Arrow 绘制一个箭头

    1
    <Arrow x1="10" y1="20" x2="100" y2="200" />
  • AutoFitText 字体大小自适应文本框

    1
    <AutoFitText :max="200" :min="100" modelValue="Some text"/>
  • Link 链接

    1
    2
    3
    <Link to="42">Go to slide 42</Link>
    <Link to="42" title="Go to slide 42"/>
    <Link to="solutions" title="Go to solutions"/>
  • Toc 目录

    1
    <Toc />
  • Transform 转换

    1
    2
    3
    <Transform :scale="0.5">
    <YourElements />
    </Transform>
  • 自定义组件,[[前端#组件的注册和使用|参考vue如何定义自定义组件]]

布局

由于主题可能会覆盖布局的行为,因此精确理解主题的使用、参数和例子最好的方式是查阅主题文档。

可用内置布局盘点如下:

1
2
3
4
5
6
7
404              center           cover            
default end error
fact full iframe-left
iframe-right iframe image-left
image-right image intro
none quote section
statement two-cols-header two-cols
  • center: 在屏幕中间展示内容。

  • cover: 用来展示演讲稿的封面页,可以包含演讲的标题、演讲者、时间等。

  • default: 最基础的布局,用于展示任意类型的内容。

  • end: 演讲的最后一页。

  • fact: 用来在屏幕上突出展示很多事实或数据。

  • full: 使用屏幕全部空间来展示内容。

  • image-left: 在屏幕左侧展示图片,屏幕右侧展示内容。
    用法:

    1
    2
    3
    4
    5
    ---
    layout: image-left
    image: ./path/to/the/image
    class: my-cool-content-on-the-right
    ---
  • image-right: 在屏幕右侧展示图片,屏幕左侧展示内容。
    用法:

    1
    2
    3
    4
    5
    ---
    layout: image-right
    image: ./path/to/the/image
    class: my-cool-content-on-the-left
    ---
  • image: 将图片作为页面的主要内容进行展示。
    用法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    ---
    layout: image
    image: ./path/to/the/image
    ---
    你可以使用 backgroundSize 选项来控制背景图片的大小:
    ```yaml
    ---
    layout: image
    image: ./path/to/the/image
    backgroundSize: contain
    ---
    ```yaml
    ---
    layout: image-left
    image: ./path/to/the/image
    backgroundSize: 20em 70%
    ---
  • iframe-left: 在屏幕左侧展示网页,内容将放置在右侧。
    用法:

    1
    2
    3
    4
    5
    ---
    layout: iframe-left
    url: https://github.com/slidevjs/slidev
    class: my-cool-content-on-the-right
    ---
  • iframe-right: 在屏幕右侧展示网页,内容将放置在左侧。
    用法:

    1
    2
    3
    4
    5
    ---
    layout: iframe-right
    url: https://github.com/slidevjs/slidev
    class: my-cool-content-on-the-left
    ---
  • iframe: 将网页作为页面的主要内容进行展示。
    用法:

    1
    2
    3
    4
    ---
    layout: iframe
    url: https://github.com/slidevjs/slidev
    ---
  • intro: 介绍演讲稿,通常包含演讲稿标题、简述、作者等信息。

  • none: 没有任何样式的布局。

  • quote: 突出显示引文。

  • section: 用于标记演讲稿的新部分的开始。

  • statement: 将主张/声明作为主要页面内容。

  • two-cols: 将页面内容分为两列。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ---
    layout: two-cols
    ---

    # Left

    显示在左侧

    ::right::

    # Right

    显示在右侧
  • two-cols-header: 将页面内容分为两列,上方和下方的内容分开,第二行将左右两列分开。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    ---
    layout: two-cols-header
    ---

    显示在上方

    ::left::

    # Left

    显示在左侧

    ::right::

    # Right

    显示在右侧