LaTeX技巧906:插入动画(animate 宏包教程) 您所在的位置:网站首页 ppt如何加入gif动画 LaTeX技巧906:插入动画(animate 宏包教程)

LaTeX技巧906:插入动画(animate 宏包教程)

2024-05-08 04:51| 来源: 网络整理| 查看: 265

在最初学习 LaTeX 插图的时候,我就发现,基本的 LaTeX 手段不支持 GIF 格式的动图。虽然一直保持对此的好奇,但是因为没有实际需要,再加上「论文等文稿不适合插入动画」的论调,所以一直没有去探究可行性和解决办法。 前段时间,因为制作一个幻灯片(离散卷积和卷积神经网络)的需要,不得不插入动画以演示「卷积」的过程和效果。于是就借此机会,摸索了如何在 LaTeX 中插入动画。此文是对上述过程的归纳总结。 本文主要介绍两部分内容

如何在 LaTeX 中插入 GIF 格式的动图; 如何在 LaTeX 中插入 TikZ 代码绘制的动画。 以及介绍一些运用动画效果实现的黑科技效果。   主角登场——animate 宏包 要在 LaTeX 中插入动画,首先要考虑输出文件类型是否支持这样的需求。否则,插入动画就变成了无根之木、无源之水。 目前来说,主流的 LaTeX 输出格式是 PDF。PDF 的全称是 Portable Document Format。它是最早由 Adobe 公司提出的文档格式标准;因其优良的特性,现已逐渐发展成为固定格式文本交换的事实标准。 自 2000 年始,1.3 版本的 PDF 开始支持 JavaScript。而后,相关特性在后续版本中不断完善。因此,若是 PDF 浏览器支持相关 API,则可以利用 JavaScript 在 PDF 文稿中做到很多事情——当然,包括了动画。因此,在 PDF 中插入动画是可能的。 Alexander Grahn 根据上述 API,开发了 animate 宏包。该宏包利用 JavaScript,允许用户在 LaTeX 文稿中插入动画,并在支持 JavaScript 的 PDF 阅读器中查看。特别喜人的是,animate 宏包支持目前最流行的几种编译方式;因此,你无须像使用 media9 之类的宏包那样,被编译方式绊住脚。目前 animate 支持的编译方式有 pdfLaTeX / LuaLaTeX LaTeX -> dvips -> ps2pdf / LaTeX -> dvipdfmx XeLaTeX -> xdvipdfmx 当然,支持这些特性的 PDF 阅读器则比较少。目前已知的有 Adobe Acrobat / Reader PDF-XChange Foxit Reader 使用 animate 宏包 此处不表如何安装 animate 宏包,我们来看如何使用 animate 宏包。 和其它宏包一样,在 LaTeX 中使用 \usepackage[]{animate} 即可引入 animate 宏包。唯独需要注意的有三点 必须在引入 animate 宏包之前,显式地引入 graphicx 宏包; 若希望使用 LaTeX -> dvipdfmx 这一工具链,则需要给 graphicx 和 animate 宏包都加上 dvipdfmx 选项(原因); 和交叉引用中遇到的问题一样,使用 animate 宏包创建动画,也需要两次编译(第一次创建 JavaScript 内容,第二次在具体位置插入内容)。 animate 宏包支持不少参数。不过,仅有 dvipdfmx 和 xetex 两个驱动选项只能在载入宏包时使用——而其中仅有 dvipdfmx 是必须的。宏包支持的其他参数,具体的命令、环境也都支持。若是在载入宏包是提供这些参数,相当于给命令、环境设置了「默认值」。因此,这部分参数放在之后具体介绍。 使用 animate 宏包插入 GIF 动图——\animategraphics animate 提供了 \animategraphics 命令,用于插入「一系列」的图片,而后将他们组成动画——相当于插入了动图。 具体来说,其命令是 \animategraphics[]{}{}{}{} 此处 options 是命令的参数,主要用于控制动画的各种效果,具体参数将在下一节中介绍。先前我们讲过这部分参数大都也可用于宏包选项。frame rate 的单位是 Hz,表示 1 秒钟内,「放映」多少帧。 前面说该命令用于插入一系列图片,animate 宏包要求这一系列图片有共同的文件名前缀,而后以数字编号表述其顺序。file basename 选项用于记录该前缀;first 和 last 则是这一系列图片编号的起止。 关于文件名后缀,animate 也做了具体要求。首先,animate 要求插入的图片,后缀名必须是小写。其次,animate 对文件搜索顺序做了规定(实际上是 graphicx 的规定): pdfLaTeX / LuaLaTeX:pdf, mps, png, jpg, jpeg, jbig2, jb2, jp2, j2k, jpx XeLaTeX / LaTeX -> dvipdfmx:pdf, mps, eps, ps, png, jpg, jpeg, bmp LaTeX -> dvips -> ps2pdf:eps, mps, ps 注意到两件事情:一,上述后缀名中没有 gif,这意味着不能直接插入 GIF 格式的动图;二,前面提到,使用 animate 宏包插入动图实际是插入一系列的图片,这意味着我们需要将 GIF 格式的动图,预先转换成一系列符合要求的格式之图片。 转换格式需要用到 ImageMagick 这一开源的工具。安装它,Mac 用户可以使用 brew install ImageMagick,Linux 用户可以使用各自的包管理器,Windows 用户则需要下载安装。 安装好 ImageMagick 之后,我们就可以用它提供的 convert 命令将 GIF 格式的动图逐帧地切分成一系列图片了。假设你的目标图片是 foo.gif,那么使用如下命令可以得到一系列图片:foo-0.png, foo-1.png, foo-2.png, … convert foo.gif -coalesce foo.png 而后,我们就可以用 animate 提供的 \animategraphics 命令插入动图了。 \documentclass{article} \usepackage{graphicx} \usepackage{animate} \begin{document} \animategraphics{24}{foo-}{0}{300} \end{document}   图片引用自 Wikipedia。 animate 选项 animate 宏包提供的选项,按照适用范围可以分为三类:只能用于宏包的选项、只能用于接口的选项(命令和环境)、二者皆适用的选项。现分别介绍。 这里仅介绍其中重要的部分,未尽之详细,请参考 animate 的宏包文档。 只能用于宏包的选项 dvipdfmx:驱动选项,表示用户希望使用 LaTeX -> dvipdfmx 进行编译。 xetex:驱动选项,表示用户希望使用 XeLaTeX -> xdvipdfmx 进行编译。 只能用于接口的选项 这里的接口指的是 animate 宏包提供的用户接口。例如我们已经见过的 \animategraphics 命令,以及下一节会介绍的 animateinline 环境。 label=:为 animate 对象指定唯一的标签,可用于之后的 JavaScript 控制。 every=:只为每个第 帧构建动画,而忽略剩余的帧。 二者皆适用的选项 type=:使用指定的图片类型(而不按照前面提到的顺序搜索)。 poster[= first | | last | none]:指定用于打印和默认展示的动画帧,默认是第一帧。 autopause:当动画所在页不再呈现时,自动暂停动画。 autoresume:当被暂停的动画重新呈现时,自动恢复播放。 autoplay:当动画所在页在 PDF 阅读器中呈现时,自动播放动画。 loop:播放到最后一帧时,从第一帧开始继续播放;如此往复。 palindrome:播放到最后一帧时,逐帧倒退;如此往复。 step:忽略 frame rate,只在每次点击鼠标时播放一帧。 width=, height=, totalheight=, keepaspectratio:按绝对长度缩放动画的大小。 scale=:按比例缩放动画的大小。 controls:展示用于控制动画的按钮。 begin=, end=:仅用于 animateinline 环境,在每一帧的内容前后添加相应内容。 使用 animate 插入用户绘制的动画——animateinline 环境 之前我们介绍了如何使用 ImageMagick 拆分 GIF 动图,而后用 \animategraphics 将拆分得到的一系列图片在 LaTeX 中插入 PDF 文档,变成动画。然而,这可能存在几个问题 从它处获取的 GIF 动图可能侵犯他人版权; 自行制作 GIF 动图成本较高——不如直接用 TikZ 等工具绘制。 因此,这就引出了更高级的主题:使用 animate 宏包,插入用户自行绘制的动画。这需要引入一个新的用户接口——animateinline 环境。它的语法是这样的: \begin{animateinline}[]{} ... typeset material ... \newframe[] ... typeset material ... \newframe*[] ... typeset material ... \newframe \multiframe{}{[]}{ ... repeated (parameterized) material ... } \end{animateinline} 显而易见,animateinline 环境的语法比 \animategraphics 要复杂得多。不过,仔细看的话,其实是有想通之处的。 首先,和 \animategraphics 命令一样,animateinline 环境允许用户通过 options 和 frame rate 控制动画的基本行为。不同之处在于,\animategraphics 的动画内容是固定死的——由图片提供,而 animateinline 的动画内容则需要用户在 LaTeX 代码中逐帧绘制。因此,animateinline 环境提供了三个命令来辅助和控制这些内容。 \newframe 和 \newframe* 的作用正如其名:结束上一帧并开始下一帧。唯一的不同在于,\newframe 会立即开始下一帧,而 \newframe* 则会暂停并等待用户的点击再开始下一帧。它们接受一个可选参数,以便在动画的中途改变 frame rate。 \multiframe 则更为强大,它能提供类似循环的功能,并将「循环变量」传递到 \multiframe 内部供内部绘图命令使用。具体来说,首先我们需要给出循环的次数 number of frames,而后指定循环变量 variables。循环变量的书写格式如下 =+ 这里,variable name 由若干个字母组成,不含 LaTeX 命令的反斜线。需要注意的是,variable name 的首字母是有意义的,它决定了变量的类型。 整数:i, I 浮点数:n, N, r, R 长度:d, D initial value 表示循环变量的初始值,而 increment 表示循环变量在每次循环末尾自增的值。 因此,我们可以写出如下代码 \multiframe{10}{iAngle=0+10, dLineWidth=3pt+-0.1pt}{ ... repeated (parameterized) material ... } 这样,循环会执行 10 次,同时带有两个循环变量:整型变量 iAngle 和 dLineWidth。前者从 0 开始,每次增加 10;后者从 3pt 开始,每次减少 0.1pt。而后,在循环体中(\multiframe 命令的内部),我们就可以使用 \iAngle 和 \dLineWidth 获得循环变量的值了。 \documentclass{article} \usepackage{graphicx} \usepackage{animate} \usepackage{tikz} \usetikzlibrary{positioning} \tikzset{global scale/.style={ scale=#1, every node/.append style={scale=#1} } } \tikzset{global xscale/.style={ xscale=#1, every node/.append style={xscale=#1} } } \tikzset{global yslant/.style={ yslant=#1, every node/.append style={yslant=#1} } } \newcommand{\twodimdrawcontent}[2]{% \useasboundingbox (0, -6) rectangle (16.1, 3); \begin{scope}[global yslant = -0.6, xshift = 1cm, yshift = -1cm] \node (A) at (0, 0) {}; \node (B) at (3, 0) {}; \node (C) at (3, 3) {}; \node (D) at (0, 3) {}; \draw (0,0) grid (3,3); \end{scope} \node[anchor = north, xscale = 2] at (2, -5) {Kernel}; \begin{scope}[xshift = 6cm, global yslant = -0.6, yshift = -2cm] \node (E) at (0cm + #1cm, 2cm - #2cm) {}; \node (F) at (3cm + #1cm, 2cm - #2cm) {}; \node (G) at (3cm + #1cm, 5cm - #2cm) {}; \node (H) at (0cm + #1cm, 5cm - #2cm) {}; \draw[fill, blue!20] (0cm + #1cm, 2cm - #2cm) rectangle (3cm + #1cm, 5cm - #2cm); \draw[fill, blue!50] (1cm + #1cm, 3cm - #2cm) rectangle (2cm + #1cm, 4cm - #2cm); \draw (0,0) grid (5,5); \end{scope} \node[anchor = north, xshift = 6cm, xscale = 2] at (2, -5) {Input}; \begin{scope}[xshift = 12cm, global yslant = -0.6, xshift = 1cm, yshift = -1cm] \node (I) at (0.5cm + #1cm, 2.5cm - #2cm) {}; \draw[fill, red!20] (0cm + #1cm, 2cm - #2cm) rectangle (1cm + #1cm, 3cm - #2cm); \draw (0,0) grid (3, 3); \end{scope} \node[anchor = north, xshift = 12cm, xscale = 2] at (2, -5) {Output}; \draw[dashed, blue] (A.center) -- (E.center); \draw[dashed, blue] (B.center) -- (F.center); \draw[dashed, blue] (C.center) -- (G.center); \draw[dashed, blue] (D.center) -- (H.center); \draw[dashed, red] (E.center) -- (I.center); \draw[dashed, red] (F.center) -- (I.center); \draw[dashed, red] (G.center) -- (I.center); \draw[dashed, red] (H.center) -- (I.center); } \begin{document} \begin{animateinline}[ loop,autopause,controls, buttonsize=1.2em, buttonbg=0.6:0.6:1,buttonfg=0.2:0.2:1, begin={\begin{tikzpicture}[global scale = 0.7, global xscale = 0.5, on grid]}, end={\end{tikzpicture}}]{1.8} \multiframe{3}{icol=0+1}{% \xdef\icol{\icol} \xdef\irow{0} \whiledo{\lengthtest{\irow sp < 2sp}}{ \twodimdrawcontent{\irow}{\icol} \newframe \pgfmathsetmacro{\irow}{\irow + 1} \xdef\irow{\irow} } \twodimdrawcontent{\irow}{\icol} } \end{animateinline} \end{document} 绘图的核心代码定义在 \twodimdrawcontent 这一命令当中。该命令的内容完全是 TikZ 的语法,不在此篇的涵盖范畴中。因此不表。唯一需要注意的是,我们使用了 \useasboundingbox (0, -6) rectangle (16.1, 3); 限定每一个动画帧的大小。这是因为 animateinline 会根据第一帧的大小来确定动画的大小;若是每一帧大小不同,则可能出现某些帧显示不全的现象。 我们仔细看 animateinline 环境中的参数。loop, autopause, controls 我们很熟悉了;buttomsize, buttonbg, buttonfg 顾名思义,是用来调整按钮的样式的;begin 和 end 则在每一帧的内容前后加上了 tikzpicture 环境。这样,我们可以直接使用 \twodimdrawcontent 来绘制动画帧。 animateinline 环境的帧率(frame rate)是 1.8,这意味着每秒会播放 1.8 个动画帧(每 5 秒播放 9 帧)。具体的动画帧内容,则由 \multiframe 给出。 \multiframe 循环 3 次,对应循环变量为 icol。这是一个整型变量,从 0 开始每次自增 1。在循环体内,我们首先定义 \xdef\icol{\icol};这是因为,\multiframe 给出的循环变量,若直接传给 \twodimdrawcontent 则无法正确展开。随后我们将 \irow 定义为 0。接下来,我们使用 \whiledo 的循环。此处不使用 \multiframe 的原因是它无法嵌套。这一循环的变量是 \irow。它从 0 开始,每次循环末尾由 \pgfmathsetmacro{\irow}{\irow + 1}, \xdef\irow{\irow} 自增 1。\whiledo 循环两次,内部用 \twodimdrawcontent{\irow}{\icol}, \newframe 制作出一帧动画。最后在 \whiledo 循环的外部,画出第三帧。 如此,就能得到我们在前文中的动画效果了。 偏好使用 PSTricks 的用户,可以参考 animate 宏包文档里的示例。 一点彩蛋 \documentclass{article} \usepackage{graphicx} \usepackage{animate} \newcommand{\myemph}[1]{% \begin{animateinline}[autoplay, loop]{.5} \emph{#1} \newframe[1.5] \relax \end{animateinline}} \begin{document} This is \myemph{important}! \end{document} 选自:https://liam0205.me/2017/08/10/importing-animate-in-LaTeX/


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有