谢轮眼

谢益辉 2017-02-10

2011 年的时候我曾经提到我第一次用 HTML5 幻灯片。从那以后我就转向了 HTML5 幻灯片阵营,几乎再也没用过 LaTeX 写片子。然而这五六年来,要说真正让我心动的幻灯片样式还是我第一次尝试的那个,剩下的那些看起来有那么点帅气,但离我心里对幻灯片的预期总是差那么点儿,都不能算真爱,不足以让我半夜三点还码不停蹄。这一路我用过 Pandoc 的 dzslides,reveal.js,及后来 R Markdown 里的 Slidy 和 ioslides。这些样式第一次用的时候感觉还不错,用两三次之后画风就成这样了:

欲说还休

直到去年 11 月我看见了一个叫 remark.js 的 JS 库。那感觉,各位客官可以脑补《囚鸟》第 3 分 39 秒的雷声隆隆1。呐,这货,奏是朕一直追寻的。

厉害啊

我解释一下我眼中的 remark.js 厉害在哪些地方:

  1. 首先它就做对了一件简单但至关重要的事情:它的默认例子用了 Google 网络字体,当我一眼看到 Yanone Kaffeesatz、Droid Serif 和 Ubuntu Mono 字体我就迈不动腿了。相比之下,其它幻灯片框架默认都是所有人已经看过五百遍的系统自带字体(reveal.js 除外)。作为字体党,一个不常见的字体是我最重要的开胃菜。比如上周我把我的个人网站迁移到了 Hugo 上,借此机会我也大刀阔斧调整了我的 CSS,用了大号字,英文用 Google 网络字体,对于中文,不得不说苹果系统下的中文字体实在太精致,就连宋体都是那么养眼,看了苹果的宋体再看 Windows 的宋体真的感觉辣眼睛。我的网站字体问题下次再详谈(这是我第一次找到楷体的用武之地)。

  2. remark 是基于 Markdown 的幻灯片框架,这听起来没什么新奇的,这世界上已经处处 Markdown 了,但它又做对了几件至关重要的事情:

    • 它有元素对齐的语法!这特么实在太重要了。无数个日夜我都想居中一些元素,可是只能写丑陋的 HTML 语法 <div style="text-align: center;"> </div>。在 Markdown 中混入这些东西会严重触发我的代码洁癖,导致我时时想掀桌。

    • 不仅是对齐,它支持自定义任何元素的 CSS 类,这样让一些 Markdown 本来不支持的元素也可以通过 CSS 魔法实现了,比如脚注。它敞开了 CSS 大门,也就让幻灯片的任意元素有无穷的可定制性,前提是你要懂 CSS。通过 .foo[] 的 Markdown 语法对应 CSS 类名 foo 这件事打通了 Markdown 的任督二脉。Markdown 最大的问题就是没有语法定制单个元素的特殊格式,它只能通过全局 CSS 规则定义所有同类元素的样式。虽然我反对花里胡哨的格式(我尤其反对对单个词句着色),但有时候实在是希望能自定义一下。传统 Markdown 关死了这扇门,remark 以很简单的方式把它打开了。

  3. 除了对齐单个的元素,它还支持每一张片子的全局对齐方式,比如片子所有内容居中、底端对齐。更重要的是,每一张片子都可以有自己的 CSS 类,换句话说,只要懂 CSS,你想上天就可以上天。

  4. 一张片子里的内容可以以任意方式分割(语法是两短横线),分割过的片子在播放的时候会按次序一段段显示在屏幕上。比如你的爆笑 GIF 动图或者什么梗可以先藏在下面,等你讲完前两点之后再显示出来。这个“任意分割”又是一大亮点,因为传统 HTML5 框架往往只能做到对一个列表里的列表项一项一项依次出来,而 remark 可以非常放肆,你可以先一口气显示前两点,再显示第三点,再显示第四五六点。你想怎么安排你的片子内容怎么出场就怎么安排。

  5. 每张幻灯片底下可以写注释。要解释这个注释有什么用,先得说一下 remark 的最强忍术的结印手势:播放幻灯片时,可以按一些快捷键,比如 ?h 显示帮助、b 让屏幕变黑、m 把一张片子上下左右颠倒(这特么简直太好玩了)、c 克隆一份当前窗口的片子到新窗口、p 进入演讲者模式。所谓演讲者模式,就是左右分栏格式,左栏上下分别显示当前片子和下一页片子(即:你讲当前页的时候可以预览下一页),右栏显示当前页上的注释,这些注释也可以用 Markdown 语法写,用来提醒自己一些幻灯片上没有展示给观众看的内容(比如冷笑话),这个模式只是给演讲者自己看的;前面我们说了克隆,实际演讲中,你可以克隆一份片子,以正常模式拖到投影仪上给观众看,而自己的电脑上则用演讲者模式。这样麻麻再也不怕我作报告的时候忘了讲冷笑话啦。这么说有些抽象,自己戳开一个例子体验一下就知道了。

  6. 一张片子可以设置背景图片!我为什么喜欢我的第一份 HTML5 幻灯片?一个最重要的原因就是图片可以占满全屏。这个功能在我用过的其它框架中几乎都没有,而我又是坚定的图片党(没有师姐那样的气场,只好靠大图来硬撑了)。

    • 之前有好事者在推特上给我提了一个功能请求,就是添加一个参数以便在随机的一页片子上显示 Karl Broman 的胡子照。这张照片背后的故事略有点长,这里就不展开了。总之呢,我觉得这个很好玩,某种程度上 Karl 就是我眼中的蒙娜丽莎,谜の微笑。
  7. 其它功能如模板和代码高亮就不说了。

总之它几乎做对了每一件事情,极大解放了忍者的生产力,让我感到非常满意。别的幻灯片框架通常有天花板,你有能力也翻不过去,这是很窝心的。

那还有我什么事呢?当然有。它支持 Markdown,我自然得需要它支持 R Markdown 啊。当时我正在忙 blogdown 的事情,也很有趣,于是等等等,等到感恩节放假,在去另外一个城市的路上我用手机一边看 remark.js 的文档一边琢磨如何把它引进到 R 里面来,关键问题就是 remark 不支持 Pandoc,而是有自己的基于 JS 的 Markdown 渲染工具,所以问题就是如何绕开 Pandoc。思来想去想到一个绝妙的办法,回来集中三整天时间把它码出来。在现有的 R Markdown 扩展中,估计还没有这么黑的手段,而在我写过的 R 包中,也很少有一个我能指出最关键一行代码的包。这个包的代码量不大,包括注释和空行才 350 行代码,但包含了一些惊人的查克拉,我要是不说估计也很少有人能意识到。R Markdown 自身那些功能没什么惊人的,想必各位客官已经了如指掌了。先说一个普通的,就是数学公式支持,这是费了我一点脑子的事情,主要问题是 remark 不支持 Pandoc 的 Markdown 语法,所以数学公式得作特殊处理,至于怎么处理,看源代码应该是最好的理解方式,总之你可以在幻灯片中自由使用数学公式。

最厉害的其实是无限月读。

还是先说一下包的名字为啥叫 xaringan 吧。它是写轮眼 sharingan 的变体,把 sh 换成了 x,好让歪果仁知道我的姓 Xie 大概怎么念(而不要念“鸡”),当然更重要的是搜起来好搜,虽然读起来不那么好读。写轮眼是火影中的血继限界,有催眠眼和洞察眼的功能。演讲无非也就是催眠观众以及提供洞见,所以我提供这么个写轮眼的工具。xaringan 包中的 R Markdown 输出格式名称为月读(moon_reader),月读是写轮眼发动的幻术,对对手进行灵魂拷问。无限月读是一种大规模幻术,让施术者的轮回写轮眼借助月亮的反射使整个世界都陷入无限的幻觉中。“无限”在 xaringan 包中的意思其实是 基于 servr 包的一个本地网页服务器,这个服务器可以无限预览幻灯片:一旦启动这个术,你只需要编辑、保存你的 R Markdown 源文档,其输出可以持续更新,所以你一直都可以在旁边看到重新编译过的新片子页面,而不需要在 RStudio 中重复点击 Knit 按钮。刷新过的页面会保持你刚刚浏览的位置,比如刚刚你在第 10 页,自动刷新后还会留在那一页,而不会变成从头开始。

其实无限月读的功能并不局限在这个包支持的幻灯片上,它可以支持任意 Rmd 文档的持续编译、刷新、预览,条件是输出格式是 HTML(否则没法在浏览器中打开)。这就是 xaringan 的最大秘密:少年,你可以不必点击 Knit 按钮。

servr 包我几年前就写了,我之所以没有在公开场合正式宣传,只是因为我一直在等一个脚底板有三颗痣的人2,就是 R Markdown Notebooks,而它终于来了,现在我说出 servr 包,也不会有压力了,因为多数人都不会用它,他们应该会用 Rmd 笔记本的预览功能。这样我肩上就不必背负太多维护 servr 包的压力,我早知道很多用户迫切需要更方便的预览功能,要是我早早宣传,恐怕这两年得鸡飞狗跳了。

好了,我估计我能在这个框架下老实两年了。等什么时候出去开会发现遍地写轮眼片子的时候,可能又会心生搬家念头。

搬

希望有朝一日岸本齐史能用这个 R 包写幻灯片吧。


  1. 在我的软件世界还有一些雷声隆隆的时刻,希望将来有机会一一写出来。 ↩︎

  2. 我比较耐等,等一样东西经常能等上两三年。 ↩︎