beamer

\usetheme{Madrid}\usetheme{Warsaw}\usetheme{Singapore}——LaTeX 的演示幻灯片 class beamer 目前自带 27 个 presentation theme,无一例外全都以城市命名。作者在手册里亲口说明了原因:他起名的点子用光了,而这些城市都是他或某位合作者参加过会议或研讨会的地方。这份随意其实也是整个 class 的性格。写 beamer 几乎和写任何别的 LaTeX 文档一样,只有一点不同,而这一点改变了一切:单位是 frame,不是页。本页就从这一点出发,依次讲 frame、overlay、主题,以及为什么一加入 verbatim 环境幻灯片就会崩。

beamer 与普通 document class 的不同之处

从写下 \documentclass{beamer} 那一刻起,那些用来推动分页的工具就失效了。把 \newpage 放进 frame 里,既不报错也不警告,只是被忽略——实际试一下,\newpage 两侧的两段会并排出现在同一张幻灯片上。原因很直接:在 beamer 里,决定分页的是 frame 的边界,而不是作者。同理,\section{...} 在正文中什么也不显示,它只是登记进目录和导航。而写在任何 \begin{frame} 之外的文字也不会报错,它会被排成一张没有主题的裸页。「怎么中间多出一张奇怪的页」几乎都源于此。

另一处与纸面文档不同的是尺寸:beamer 排的是屏幕,不是纸张。默认是 4:3,beamer.cls 里写作 \beamer@paperwidth{12.80cm}\beamer@paperheight{9.60cm},也就是 128 mm × 96 mm。用 class 选项可以改变比例:\documentclass[aspectratio=169]{beamer} 得到 160 mm × 90 mm 的 16:9。TeX Live 2024 所带版本单独列出的值有八个:16101691495443321412013。有意思的是它如何处理表外的值:传入不在列表里的数字,beamer 会把高度固定在 9.60 cm,再按数位拆分计算比例。写 aspectratio=21,就真的得到 2:1、宽 19.2 cm 的幻灯片。正文字号照常指定,如 \documentclass[11pt]{beamer},默认是 11pt。

frame 环境:一张幻灯片不等于一页

beamer 的核心是 frame 环境。从 \begin{frame}\end{frame} 是一个逻辑屏幕,里面放列表、图和公式。标题用 \frametitle{...} 添加,或作为环境参数传入:\begin{frame}{Title}。关键在于:一个 frame 不一定是一页。用上下一节的 overlay,同一个 frame 会生成多页 PDF,翻页时看起来就是逐步显示。frame 是逻辑屏幕,PDF 页是它的一帧——beamer 自己的文档就把这两个词分开,前者叫 frame,后者叫 slide。

frame 可以在方括号里接选项。最常用的是内容纵向溢出时的 [allowframebreaks]。加上它之后,beamer 会把 frame 拆成所需的若干页,而且还会在 frame 标题末尾自动补上罗马数字——灌入 22 项的列表,第一页标题是「Long I」,第二页是「Long II」。此外还有对齐纵向位置的 [t](顶部)、[c](居中)、[b](底部),去掉主题装饰好让图占满整屏的 [plain],供 \againframe 之后重新调用的 [label=...],以及不计入 frame 编号的 [noframenumbering]。下一节的 [fragile] 也写在同一位置。

标题元数据与 article 一样在导言区声明。写 \title{...}\author{...}\institute{...}\date{...},然后在正文中把 \titlepage 放进一个 frame,就会排出标题幻灯片(\maketitle 结果相同)。较长的演示里,用 \section{...} 分隔内容后,这些结构会反映到每张幻灯片顶部的导航中。声明好分段之后,把 \tableofcontents 放进一个 frame 就得到目录幻灯片,而 \tableofcontents[currentsection]只强调当前所在的节。若想在每节开头自动插入这样的 frame,可在导言区用 \AtBeginSection[]{...} 布置。

Overlay:\pause 与 <2-> 的区别

逐步呈现内容靠的就是 overlay,入口有两个。\pause 只要放在 frame 中间,后面的内容就被送到下一张幻灯片——省事,但只能按源码顺序一步步累加。另一个是 overlay specification,也就是紧跟在命令之后的尖括号 <...>\item<1-> 表示「从第 1 帧起一直显示」,\item<2> 表示「只在第 2 帧」,\item<2-> 表示「第 2 帧起」。也可以把范围和跳号并列,如 <-2,4-5,7>。实际编译一下,行为与写法完全一致:带 \item<3> 的项目在第 3 帧出现,\only<2>{...} 只在第 2 帧露面,\uncover<3->{...} 从第 3 帧开始可见。

命令行为隐藏时的占位
\pause把后续内容送到下一张幻灯片内容依次追加,因此不成问题
\uncover<2->{...}在指定帧显现始终保留,因此布局不会跳动
\onslide<2->{...}在指定帧显示,其余隐藏始终保留
\only<2>{...}只在指定帧输出不保留,后面的内容会往上挤
\alert<2>{...}在指定帧切换为强调色始终可见,因此始终保留
[<+->]加在列表环境上,自动逐项推进\uncover 相同

这张表真正有用的是第三列。\uncover\onslide 即使在隐藏时也保留空间,所以项目出现时周围不会动。\only 则会插入或移除输出本身,隐藏的空间被收回,后面的内容会上下移动。要替换公式的一部分时,\only 正是所需;但若只是顺次展示项目列表,它就是个陷阱,用 \uncover[<+->] 更稳妥。同样的 overlay 语法也适用于 \textbf\textcolor,不只是 \alert,所以「只在这一帧加粗」不需要新的语法。

latex
% the blunt instrument: everything after \pause moves to the next slide
\begin{frame}{Results}
  \begin{itemize}
    \item First point
    \pause
    \item Second point
  \end{itemize}
\end{frame}

% overlay specifications: say exactly which slides each piece belongs to
\begin{frame}{Results}
  \begin{itemize}
    \item<1-> Always there
    \item<2-> From slide two
    \item<3>  Slide three only
  \end{itemize}
  \only<2>{Shown on slide 2, and takes no space otherwise}
  \uncover<3->{Space is reserved from the start, ink appears on slide 3}
\end{frame}

% the same stepwise reveal, without numbering each item by hand
\begin{frame}{Results}
  \begin{itemize}[<+->]
    \item Observation
    \item Interpretation
    \item Limitation
  \end{itemize}
\end{frame}

主题:城市、飞禽、花卉与海洋生物

主题共有五类,而且看名字就知道属于哪一类。开头提到的城市名(MadridWarsawBerlinSingapore 等)是 presentation theme,也就是一次性决定全局的那种。配色主题的命名更彻底,手册自己就把谜底揭开了:飞禽albatrossbeetlecrane)是自行指定全部颜色的「完整」配色主题;花卉lilyorchidrose)是内部配色主题,只改变 block 等内部元素的颜色;海洋生物whaledolphinseahorse)是外部配色主题,管的是页眉、页脚和侧边栏的配色。所以一写下 \usecolortheme{whale},名字就已经告诉你它只动边框周围的东西。

命令设置内容示例
\usetheme一次性决定布局、导航和配色\usetheme{Madrid}(城市名)
\usecolortheme只替换配色\usecolortheme{seahorse}(海洋生物=外部)
\usefonttheme标题与正文的字体\usefonttheme{serif}, \usefonttheme{professionalfonts}
\useinnertheme内部元素的样式(列表符号、block 形状)\useinnertheme{rounded}
\useoutertheme外框样式(页眉、页脚、侧边栏)\useoutertheme{infolines}

实务上最快的做法是先选定一个 \usetheme{...},再用它下面的四个命令覆盖不满意的部分。按目的来挑:不想要导航条就用 MadridBoadilla;想让听众看到当前位置就用 FrankfurtWarsaw。若想要更现代的外观,metropolis\usetheme{metropolis})是常见答案——它不属于 beamer 本体,但作为独立宏包收录在 TeX Live 中。一句提醒:反复切换主题最容易白白耗掉一个下午。听众看的是内容而不是配色,请尽早定下一个,回到写作上。

block 与 columns:加框强调与左右分栏

要用带框的盒子突出内容,使用 block 环境\begin{block}{Title}…\end{block} 会生成配色与主题一致、带标题的盒子。提示警告用的 alertblock 和示例用的 exampleblock 写法相同;定理与定义可直接使用 theoremdefinition 环境(beamer 会自动载入 amsthm)。要把幻灯片左右分开,就在 columns 环境 里并列 \column{width}——把图和说明并排放置时的常规做法。

latex
\begin{frame}{Comparison}
  \begin{columns}
    \column{0.5\textwidth}
      \begin{block}{Before}
        The old estimate.
      \end{block}
    \column{0.5\textwidth}
      \begin{alertblock}{After}
        The corrected one.
      \end{alertblock}
  \end{columns}
\end{frame}

为什么加入 verbatim 会让 frame 崩溃,以及 [fragile] 做了什么

verbatim 环境或 listings 宏包的 lstlisting(显示源代码)放进 frame 里,只要没写 \begin{frame}[fragile],编译就会停下。麻烦的是它停下的方式。在 TeX Live 2024 上试验,beamer 指向的不是行号而是整个文件,并且说:Runaway argument?,接着 ! File ended while scanning use of \@xverbatim.,然后 I suspect you have forgotten a }, causing me to read past where you wanted me to stop.,最后是 ! Emergency stop.。你会看到一个自己从没敲过的内部宏名,外加一句言之凿凿的诊断:你漏了一个右括号。可根本没有括号没闭合。照这个建议去数括号,只会在一个括号毫无问题的文件里白花很长时间。

真正的原因不是括号,而是读取的顺序。beamer 会先把 frame 的全部内容作为参数整块读入,再按 overlay 的帧数逐次重排。可是 verbatim 的机制是「在读取时改变字符的处理方式」,对已经被吞下去的内容来说为时已晚——错误以 Runaway argument?(参数失控)开头正是因为如此:事故恰恰发生在内容被当作参数读取的过程中。加上 [fragile],beamer 就走另一条路。用用户指南自己的话说,frame 的内容会先写到外部文件,再读回来。实际编译一次就能看见:工作目录里多出一个扩展名为 .vrb 的文件,内容正是该 frame 的正文。

这套「经由外部文件」的机制还带来第二条规则。正如用户指南明确指出的,在带 [fragile] 的 frame 中,\end{frame} 必须单独占一行(前置空白除外)。因为 beamer 是靠「行首的 \end{frame}」这个字符串来判断写出到哪里为止的,所以像 \end{verbatim} \end{frame} 这样挤在一行,同样会失败,这次报出的是另一个内部宏名:! File ended while scanning use of \next.。还有一点:\alert<2>{...} 之类的 overlay 命令在 verbatim 内部无效——原样输出字符正是 verbatim 的本职。若要强调代码的一部分,请改用 beamer 自带的 semiverbatim 环境:它与 verbatim 几乎相同,只是 \{} 仍作为命令保留,因此可以插入 \alert<2>{...}

latex
\begin{frame}[fragile]{An algorithm}   % [fragile] -> body goes via a .vrb file
\begin{verbatim}
int main(void) { return 0; }
\end{verbatim}
\end{frame}                            % must be alone on its own line

% to highlight part of a listing, use semiverbatim rather than verbatim
\begin{frame}[fragile]{Highlighted}
\begin{semiverbatim}
for (int i = 2; i < 100; i++)
  \alert<2>{if (is_prime[i])}
\end{semiverbatim}
\end{frame}

可编译的最小 beamer 文档

下面是包含主题、标题幻灯片和逐步显示列表的最小配置。导言区声明主题和标题元数据;正文中第一个 frame 放 \titlepage,第二个 frame 放带 [<+->] 的列表。虽然只有两个 frame,但由于 overlay,PDF 会展开成 4 页。若要做日文幻灯片,请在导言区加上与引擎匹配的日文支持,例如在 LuaLaTeX 下载入 luatexja

latex
\documentclass[aspectratio=169,11pt]{beamer}
\usetheme{Madrid}
\usecolortheme{seahorse}

\title{My Talk}
\author{Ada Lovelace}
\institute{Analytical Engine Lab}
\date{\today}

\begin{document}

\begin{frame}
  \titlepage
\end{frame}

\begin{frame}{Overview}
  \begin{itemize}[<+->]
    \item Background
    \item Method
    \item Results
  \end{itemize}
\end{frame}

\end{document}

面向真实演讲的写作流程

beamer 不是把论文切碎的工具。先把演讲流程分成「问题、方法、结果、结论」几段,每个 frame 只放一个主张。不要从正文稿粘贴长段落,而要压缩成图、公式和简短列表,只在逐步呈现真正有用的地方才加 \pause<2->。overlay 是控制讲解顺序的工具,不是装饰。

  • 先定下宽高比 — 如果知道投影环境,在动手设计之前就锁定 aspectratio=16943。之后再改,已经摆好的图会全部错位。
  • 每张的密度 — 列表控制在 3 到 5 项。内容溢出时,把 frame 拆开通常比逃向 [allowframebreaks] 更好读。
  • 代码和日志 — 含 verbatimlstlistingminted 的 frame 一定要加 [fragile],并让 \end{frame} 单独占一行。
  • 要突出图的 frame — 加上 [plain] 可去掉主题的边框装饰,让图占满整屏。
  • 用于分发的 PDF — 演讲用和分发用是两份不同的文档。用 handout 模式折叠 overlay 以及编写讲者备注,请见「讲义」页。