Markdown 生成幻灯片
幻灯片 - markdown¶
使用 Markdown 一键生成精美幻灯片,先看演示。
选中幻灯片,试一试:
全屏模式 | 概览模式 | 演讲视图 | 暂停放映 |
---|---|---|---|
F 进入,ESC 退出 | O 或 ESC 进出 | S 进入 | B 或 . 进出 |
一、渲染框架¶
普通编辑器一般不支持制作幻灯片演示文稿,但可以借助开源项目轻松将 markdown 文件转化为精美幻灯片。
1. Reveal.js¶
reveal.js,一个流行的 HTML 幻灯片展示框架,能让任何人免费制作功能齐全且精美的演示文稿。
- 官方案例 和 第三方使用手册 - 使用案例;
- 丰富的插件生态:Plugins, Tools and Hardware:
- 插件集:reveal.js-plugins,演示 Demo;
- 插件集:Martino's plugins;
- 绘图工具:tldreveal;
- 推荐扩展:
- reveal-md,打了类固醇的 reveal.js,从任何 Markdown 文件获取精美的 reveal.js 演示文稿;
- Slidesdown,一款简洁快速的幻灯片演示框架,案例展示;
- MkSlides,一款专门用于制作幻灯片的静态网站生成器,案例展示;
- 特点区分:
- reveal.js:直接在 HTML 中写代码驱动幻灯片;
- reveal-md:是 reveal.js 的套装强化版,内置了很多插件包和工具包,可以自由扩展,使用JSON和JS文件配置;
- Slidesdown:参考 reveal.js 修改的现成框架,集成了几乎所有幻灯片常用插件,开箱即用,但扩展性不好;
- MkSlides:基于 reveal.js 和参考 reveal-md 和 MkDocs 的 Python封装优化版,仅使用一个 YAML文件配置所有属性,可自由扩展插件;✓
2. Slidev¶
Slidev (slide + dev),为开发者打造的极具灵活性和交互性的幻灯片制作工具。案例展示。
- 官方文档:为什么选 Slidev ?集成了 Vue 组件,还支持 Shiki Magic Move 和 TwoSlash 集成;
- 集成编辑:Slidev 附带了「内嵌编辑器」和 「VS Code 扩展」,也可以使用 在线编辑器;
3. Marp¶
Marp,使用 Markdown 编写演示文稿的生态系统。
- Framework:Marpit,独立于 Marp 的轻量级框架;
- CLI 界面:Marp CLI,将 Markdown 文件转换为静态 HTML / CSS、PDF、PowerPoint 文档和图像;
- 集成编辑:Marp for VS Code,VS Code 扩展;
- 推荐扩展:MarkSlides,基于 Marp 创建的幻灯片工具,支持使用 ChatGPT 等方式生成幻灯片,也可使用 在线编辑器 ;
4. 其他¶
- presenterm,基于终端的幻灯片工具,类似还有 slides;
- presenterm 基于 Rust 语言;
- slides 基于 Go 语言;
- moffee,基于 Python 的幻灯片制作工具;
- remark,一款在Web页面内用 Markdown 驱动的简单幻灯片工具,案例展示;
- 更多工具:
二、幻灯片输出¶
reveal.js 足够了。如果是开发者,不考虑复杂性和时间成本还可选择 Slidev。
- 使用编辑器,方便简单,但功能有限:
- Typora 支持导出幻灯片演示文稿。参考 使用 Pandoc 和 RevealJs 转换漂亮的演示文稿;
- MiaoYan 集成了 reveal.js,不需要导出直接在编辑器编写和预览;
- YankNote 内置插件库,可扩展 reveal.js 后直接使用;
- VS Code 也可以通过插件库扩展 reveal.js;
- 使用原生 reveal.js,通过 CLI 操作,功能最丰富,更好用。
三、使用 Reveal.js¶
推荐 MkSlides,功能介绍还可参考 reveal.js markdown。
1. 安装 MkSlides¶
pip3 install mkslides
mkslides serve demo.md
mkslides serve -o docs/
mkslides build demo.md -d _slide
mkslides build docs/ -d _slide
2. 操作提示¶
- 前后翻页:Space / Shift+Space,还可以使用方向键;
- 全屏模式:F 进入,ESC 退出;
- 概览模式:O 或 ESC 进出;
- 跳转页面:G 进入,输入页码,最后 Enter 确认;还可以用Shift+←/→ 跳转首/尾页;
- 暂停放映:B 或 . 进出;
- 演讲视图:S 进入,可在 md 源文中使用
Note:
标记演讲备注; - 绘图白板:D 进入,ESC 退出,需要先安装插件 tldreveal;
- 全文搜索:CTRL+Shift+F 进出,Enter 匹配下一项;
- 等比缩放:按住 Option (Alt) 同时鼠标单击需要缩放的位置;
- 帮助提示:Shift+? 进出;
- 菜单目录:M 或点击按钮进出,需要先安装插件 reveal.js-menu;
- 滚动视图:可通过URL激活,直接在URL后添加参数
?view=scroll
后回车; - PDF 输出:可通过URL激活,直接在URL后添加参数
?print-pdf
后回车,然后CTRL/CMD+P,利用系统打印功能另存为PDF;
3. 基础语法¶
- 分页:默认用分割线
---
,可自定义,还可配置垂直分页符; - 标记演讲备注:使用
Note:
,默认只在演讲视图下能看到演讲备注; - 设置幻灯片及元素属性:通过 HTML注解的方式。最大程度避免对 Markdown 语法的污染;
<!-- 举例:markdown语法 -->
文本靠右 <!-- .element: style="float: right; width: 40%" -->
文本靠左 <!-- .element: style="width: 40%" -->
用法参考 <!-- .element: class="fragment" -->
<!-- .slide: data-background="#5EAF9E" -->
4. 背景设置¶
用法参考:
<!-- .slide: data-background="#5EAF9E" -->
<!-- .slide: data-background="#F8CB9E" data-background-transition="zoom" -->
<!-- .slide: data-background-gradient="radial-gradient(#283b95, #17b2c3)" -->
<!-- .slide: data-background-image="assets/schoonmeersen.jpg" -->
<!-- .slide: data-background-video="xx.mp4" data-background-video-loop data-background-video-muted -->
<!-- .slide: data-background-iframe="https://revealjs.com/demo" data-background-interactive -->
通过 data-background
配置,支持 4 种不同类型:
类型 | 复合属性 |
---|---|
data-background-color | data-background-gradient |
data-background-image | data-background-size data-background-repeat data-background-opacity |
data-background-video | data-background-video-loop data-background-video-muted |
data-background-iframe | data-background-interactive |
5. 片段控制¶
用法参考:
1. 有序列表项 一 <!-- .element: class="fragment" -->
2. 有序列表项 二 <!-- .element: class="fragment" -->
3. 有序列表项 三 <!-- .element: class="fragment" -->
- 我第一最后出来 <!-- .element: class="fragment fade-down" data-fragment-index="3" -->
- 我第二先出来 <!-- .element: class="fragment highlight-current-yellow" data-fragment-index="1" -->
- 我第三再出来 <!-- .element: class="fragment fade-up" data-fragment-index="2" -->
内联元素¶
举例:Markdown 是一种轻量级的纯文本标记语言,用简单少量的符号对文字进行标注,从而实现 以最小的输入代价生成印刷级排版格式的文档。 它在 流畅的书写和印刷级阅读体验 之间找到了平衡, 让人只需专注内容而不是纠结排版。
常用的标记符号不超过十个,好记好用好效果。
用法参考:
<p>
<span class="fragment" data-fragment-index="0">Markdown 是一种轻量级的纯文本标记语言,用简单少量的符号对文字进行标注,从而实现</span>
<span class="fragment" data-fragment-index="1" style="color:red">以最小的输入代价生成印刷级排版格式的文档。</span>
<span class="fragment" data-fragment-index="2">它在</span>
<span class="fragment" data-fragment-index="3" style="color:red">流畅的书写和印刷级阅读体验</span>
<span class="fragment" data-fragment-index="3">之间找到了平衡,</span>
<span class="fragment" data-fragment-index="4">让人只需专注内容而不是纠结排版。</span>
</p>
<p class="fragment" data-fragment-index="5">常用的标记符号不超过十个,好记好用好效果。</p>
6. 动画补充¶
上一节 片段控制 就是动画设置,但还可以安装插件 reveal.js-appearance,补充更多动画效果,使用参考 demo-markdown。
<!-- .element: class="animate__bounceInLeft" -->
<!-- .element: class="animate__fadeInDown" data-split="words" -->
<!-- .element: class="animate__fadeInDown" data-split="letters" -->
<!-- .element: class="animate__fadeInDown" data-delay="200" -->
<!-- .element: class="animate__fadeInDown animate__slow" -->
<!-- .element: class="animate__fadeInDown animate__faster" data-split="words" data-delay="200" -->
<!-- .element: class="animate__fadeInUp animate__faster baseline" data-split="words" data-delay="80" data-container-delay="600" -->
<!-- .element: class="animate__flipInX demoimg" -->
<!-- .slide: data-autoappear="true" -->
7. 排版布局¶
详细教程,参考另一篇文档:CSS排版布局。
8. 动态代码¶
在代码块中用行号控制,直接在起始标记符 ``` 后面用中括号配行号方式设置。
- 行号高亮:
[3,8-10]
,第3行和第8-10行高亮; - 分步高亮:
[3-5|8-10|13-15]
,开始高亮 3-5 行,下一步 8-10,最后 13-15 行; - 行号偏移:
[30:]
,行号从 30 开始; - 组合使用:
[30: 1|2-4|5]
:
import { withTable, useTable } from 'table-render';
const Page = () => {
const { refresh } = useTable();
}
export default withTable(Page)
四、配置与扩展¶
通过配置与扩展可实现幻灯片的各种自定义功能。有 2 种配置方式:
- 全局配置,使用配置文件;
- 独立配置,在 md 源文中使用 YAML 前言;
如果同时使用,则 YAML 前言会覆盖配置文件的选项。一般用配置文件配好通用选项,个别特例再用 YAML 前言调整。选项参考 reveal.js config。
1. 使用 MkSlides¶
独立配置-YAML前言¶
可在 md源文中使用 YAML Front Matter 来设定单个文档属性,参考顶部:
title: Markdown 幻灯片
slides:
separator: '^\s*<!--h-->\s*$'
separator_vertical: '^\s*<!--v-->\s*$'
theme: solarized # league sky
revealjs:
transition: slide
navigationMode: linear
separator_notes: '^Notes?:'
全局配置-配置文件¶
MkSlides 使用一个 mkslides.yml
配置所有属性、插件、预处理:
- 在文档根目录创建 docs 和 plugin 文件夹,分别用来存放 markdown 文档和外部插件;
- 在文档根目录创建
mkslides.yml
(会被自动拾取),内容参考:
index:
title: Aaron - Slides
slides:
theme: sky
highlight_theme: monokai-sublime
separator: '^\s*<!--h-->\s*$'
separator_vertical: '^\s*<!--v-->\s*$'
separator_notes: '^Notes?:'
preprocess_script: preproc.py # 预处理脚本
revealjs:
width: 1280
height: 800
transition: slide
navigationMode: linear
menu:
titleSelector: 'h1, h2, h3'
hideMissingTitles: true
openButton: true
openSlideNumber: true
plugins:
- name: RevealMermaid
extra_javascript:
- ../plugin/reveal.js-mermaid-plugin/plugin/mermaid/mermaid.js
- name: RevealMenu
extra_javascript:
- ../plugin/reveal.js-menu/menu.js
- name: RevealPlantUML
extra_javascript:
- ../plugin/revealjs-plantuml/dist/plantuml.js
预处理-Python¶
可为 MkSlides 提供一个 Python 脚本,用来批处理文档格式的自定义调整。
- 例如,让标题自动创建新幻灯片,可使用
preproc.py
,将其路径配置在mkslides.yml
中:
def preprocess(markdown_text: str) -> str:
lines = markdown_text.split('\n')
result = []
first_heading_found = False
for index, line in enumerate(lines):
stripped = line.lstrip()
if stripped.startswith('#'):
if first_heading_found:
result.append('\n<!--h-->\n' if len(stripped.split()[0]) <= 2 else '\n<!--v-->\n')
else:
first_heading_found = True
result.append(line)
return '\n'.join(result)
2. 使用 reveal-md¶
独立配置-YAML前言¶
可在 md源文中使用 YAML Front Matter 来设定单个文档属性,内容参考:
---
title: Markdown 幻灯片
separator: '^\s*<!--h-->\s*$'
verticalSeparator: '^\s*<!--v-->\s*$'
theme: sky
revealOptions:
transition: slide
navigationMode: linear
separator_notes: '^Notes?:'
---
全局配置-配置文件¶
reveal-md 使用 JSON 和 JS 文件配置全局文档属性,以及插件:
- 在文档根目录创建 docs 和 plugin 文件夹,分别用来存放 markdown 文档和外部插件;
- 在文档根目录创建
reveal-md.json
和reveal.json
,文件会被自动拾取; - 在文档根目录创建
plugin.js
文件,用来配置插件名称,内容参考:
- 在
reveal-md.json
中配置 reveal-md 扩展的选项、插件路径和plugin.js
路径,内容参考:
{ "separator": "^\s*---\s*$", "verticalSeparator": "^\s*- - -\s*$", "theme": "league", "scripts": [ "plugin/reveal.js-mermaid-plugin/plugin/mermaid/mermaid.js", "plugin/revealjs-plantuml/dist/plantuml.js", "plugin/reveal.js-menu/menu.js", "plugin/chart/chart.min.js", "plugin/chart/plugin.js", "plugin.js" ] }
- 在
reveal.json
中配置 reveal 选项和插件属性,内容参考:
{ "transition": "slide", "navigationMode": "linear", "slideNumber": "c/t", "showSlideNumber": "speaker", "highlight_theme": "monokai-sublime", "separator_notes": "^Notes?:", "controls": false, "menu": { "titleSelector": "h1, h2, h3", "hideMissingTitles": true, "openButton": true, "openSlideNumber": true }, "mermaid": { "theme": "base", "themeVariables": { "primaryColor": "#43464A", "primaryTextColor": "#E3DB82", "lineColor": "#F2AA3B" } }, }
预处理-JS¶
可为 reveal-md 提供一个 JS 脚本,用来批处理文档格式的自定义调整。
- 例如:让标题自动创建新幻灯片,可使用
preproc.js
;
module.exports = (markdown, options) => {
return new Promise((resolve, reject) => {
var firstHeadingFound = false;
return resolve(
markdown
.split('\n')
.map((line, index) => {
const stripped = line.trimStart();
if (stripped.startsWith('#')) {
if (firstHeadingFound) {
return (stripped.split(' ')[0].length <= 2 ? '\n<!--h-->\n\n' + line : '\n<!--v-->\n\n' + line);
} else {
firstHeadingFound = true;
}
}
return line;
})
.join('\n')
);
});
};
终端运行时,把脚本路径配置在参数里:
3. 目录树¶
.
├── docs
│ ├── slide.md
│ └── xxx.md
├── plugin
│ ├── reveal.js-menu
│ │ └──menu.js
│ └── reveal.js-mermaid
│ └──mermaid.js
├── plugin.js
├── preproc.js
├── reveal-md.json
├── reveal.json
├── preproc.py
└── mkslides.yml
五、AI 加持¶
可以用 AI 自动生成代码,比如上面的 预处理 脚本。
可以把需求提前翻译成英文(避免翻译损耗),输出结果更精准。
1. Prompt¶
写一段解析 markdown 文本的代码,要求如下:
1. 在一级标题(#)和二级标题(##)前一行添加分隔符 -h-,对于其他级别的标题,添加 -v-;
2. 忽略第一次的标题的添加动作;
3. 要考虑标题前有空格的情况;
4. 用 JavaScript 或 Python 实现;
Write a piece of code that parses markdown text with the following requirements:
1. Add the separator -h- to the line before the first-level heading (#) and the second-level heading (##), and -v- for the headings at other levels;
2. Ignore the first action of adding titles;
3. Consider the case where there is a space before the title;
4. Implemented in JavaScript or Python;
2. Claude 解析¶
Claude,我只能说:🐮👍。需求理解、 代码质量、代码注释、结果预演等样样到位,甚至连示范输出都考虑了各种情况。
需求理解¶
1. Takes a markdown text as input and processes it line by line
2. Identifies heading lines by checking for '#' symbols at the start (after removing leading spaces)
3. Adds the appropriate separator:
4. '-h-' for level 1 and 2 headings (# and ##)
5. '-v-' for all other heading levels
6. Ignores the first heading encountered (no separator added)
7. Handles cases where there are spaces before the heading markers
8. Preserves the original formatting and spacing
结果预演¶
Python实现¶
Claude 3.5 Sonnet
:
def preprocess(markdown_text: str) -> str:
lines = markdown_text.split('\n')
result = []
first_heading_found = False
for index, line in enumerate(lines):
stripped = line.lstrip()
if stripped.startswith('#'):
if first_heading_found:
result.append('\n<!--h-->\n' if len(stripped.split()[0]) <= 2 else '\n<!--v-->\n')
else:
first_heading_found = True
result.append(line)
return '\n'.join(result)
3. ChatGPT 解析¶
中规中矩,代码都可以使用。
Python 实现¶
ChatGPT 4o
:
def parse_markdown(md_text: str) -> str:
lines = md_text.split("\n")
result = []
first_title_found = False # Flag to ignore the first heading
for line in lines:
stripped_line = line.lstrip() # Remove leading spaces for title detection
if stripped_line.startswith("#"):
if not first_title_found:
first_title_found = True # Skip adding separator for the first title
else:
if stripped_line.startswith("##"):
result.append("\n<!--h-->\n") # Separator for ## headings
elif stripped_line.startswith("#"):
result.append("\n<!--h-->\n") # Separator for # headings
else:
result.append("\n<!--v-->\n") # Separator for other headings
result.append(line) # Add the original line
return "\n".join(result)
六、示范 Demo¶
完整示范见 md-slideshow。