# ox-spectacle **Repository Path**: lorniu/ox-spectacle ## Basic Information - **Project Name**: ox-spectacle - **Description**: Slideshow tool on Emacs, that build HTML presentation with Spectacle.js from Org Mode. - **Primary Language**: Emacs Lisp - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-02-17 - **Last Updated**: 2025-07-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Org-Mode + Spectacle.js = (简单 + 漂亮 + 强大) 的幻灯片 基于 html5 的幻灯片,有 [reveal.js](https://revealjs.com/)、[impress.js](https://impress.js.org/) 等。但在我看来,`reveal.js` 过于朴素且语法复杂,`impress.js` 看起来很好实际上功能缺失太多。也曾使用过其他幻灯片方案,比如 `beamer` 和 `org-present` 等,要么太复杂,要么太简陋,都不能令人满意。 横竖左右比较下来,最实用、也最好看的是 [Spectacle.js](https://formidable.com/open-source/spectacle/)。使用 [React](https://reactjs.org/) 写幻灯片?很疯狂,但确实非常好用。 但是,由于基于 `React`,它的上手难度有些高。即便熟练掌握 `React` 的人,为了幻灯片写大量的 `React.js` 代码也是一件很折磨人的事。 鱼与熊掌不可兼得吗?错了,借助 Org Mode 强大的 Export 引擎,便可以轻轻松松利用 `Spectacle.js` 创造出漂亮的幻灯片了。这就是 `ox-spectacle` 产生的原因。 基本特点 (设计目标): 1. 简单。不要有太多的语法,尽量减少心智负担,一切保持简单 2. 全面。包含 `Spectacle.js` 中的的所有功能,可以自由创作和扩展 2. 自包含。允许将脚本、图片内嵌到一个单独的 html 中,从而便于分发或离线使用 ## 基本使用 首先,通过 Melpa 或其他方式安装本 `ox-spectacle` 包。 然后,加载之: ```elisp (require 'ox-spectacle) ``` 随后,创建 org 文件。比如,下面是包含两个页面的的幻灯片: ```org #+TITLE: 示例幻灯片 * 第一个页面 这是第一个页面。 #+ATTR_HTML: :width 200 :style {{ margin: "2em" }} [[https://formidable.com/open-source/spectacle/svg/formidable-logo-white.svg]] #+ATTR_HTML: :margin {2} :width {900} :showLineNumbers {true} #+begin_src html 我是第一个页面 #+end_src * 第二个页面 #+ATTR_HTML: :type Heading :fontFamily Kaiti 列表和动画演示 - 从前有座山,山中有座庙 - 庙里住个老和尚,在那讲故事 1. 从前有座山,山中有座庙 2. 庙里住个老和尚,在那讲故事…… + 从前有座山…… - 故事讲完。谁赞成,谁反对? ``` 通过 `C-c C-e s o` 导出为 html 并在浏览器中打开。完工。 ## 示例幻灯片 相应文件在 examples 文件夹中,可自行下载查看。 | 介绍 | Org 文件 | 效果展示,可点击查看 | |:-----:|:------:|:-------:| | 【基本使用】中的幻灯片,仅包含两个页面 | [first_zh.org](https://raw.githubusercontent.com/lorniu/ox-spectacle/master/examples/first_zh.org) | [first_zh.html](https://lorniu.github.io/spectacle/first_zh.html) | | [Spectacle 官方示例](https://github.com/FormidableLabs/spectacle/tree/main/examples/js) 中的幻灯片,使用 Org/Ox-Spectacle 进行重写 | [official.org](https://raw.githubusercontent.com/lorniu/ox-spectacle/master/examples/official.org) | [official.html](https://lorniu.github.io/spectacle/official.html)| | 一个会持续更新的示例 (**推荐**) | [scratch.org](https://raw.githubusercontent.com/lorniu/ox-spectacle/master/examples/scratch.org) | [scratch.html](https://lorniu.github.io/spectacle/scratch.html)| ## 预备知识 在正式开始之前,你需要懂一点 html、[React](https://reactjs.org/) 和 [Spectacle.js](https://formidable.com/open-source/spectacle/docs/)。 一个典型的 Spectacle.js 的幻灯片是这样的结构: ```jsx 内容或参数 ``` 几句话总结一下: - 最顶层的节点为 `Deck`,表示整个幻灯片。`Deck` 下面有若干 `Slide` 或 `SlideLayout.Xxx`,每个表示一个幻灯片页面 - 在页面中使用各种组件构建内容。Spectacle 提供了 `Heading/Text/Link/Image/Table/CodePane/Appear` 等多种组件,可以非常方便表达并渲染标题、文本、列表、表格、图片、动画等。Spectacle 也提供了 `Box/FlexBox/Grid` 等组件用作容器,对应 html 中的 `display=block|flex|grid` - 每个组件都可以传入若干属性 (props),它们可能是 css 样式,也可能是组件自定义的其他属性。组件具备的属性详情,需要通过文档了解。传入属性需遵循 `ReactJS` 语法规则,否则会报错。比如可以写成 `backgroundColor="red"` 或 `fontSize={22}` 或 `style={{ color: "red", fontSize: 22 }}` 这样的形式。`{}` 包括起来的是一段合法的 JavaScript 代码或数据 - 比如,可以向 `Deck` 传入 `theme`、`template`、`transition`、`backgroundImage` 等属性,用来设定幻灯片的主题颜色、页面模板、转场动画、背景图片等。向 `Slide` 或其他组件传入类似的属性,将只会影响当前组件 - 背后的一切都是 `html/css/js`,可以按照规则任意创建并使用自己的组件 ## 语法规则 > 现在开始简单扼要介绍 ox-spectacle 的语法规则。更多的细节,请查看 examples 文件或阅读源码。 ### 基本规则 顶层的 headline (level 1) 默认渲染为 `` 组件,可使用 `PROPERTY DRAWER` 进行设定: - 通过 `:layout:` 可将渲染组件替换为 SlideLayout。比如 `:layout: Center` 会将当前 headline 渲染为 `` - 当一个 headline 当设定了 `:layout: top` 那么其子 headline 才会被渲染为 `Slide`! - 通过 `:props:` 为组件传入属性。比如 `:props: backgroundImage="url(...)" backgroundOpacity={0.5}` 将为当前页面设定半透明的背景图片 非顶层的 headline 默认会被渲染成 `` 组件,同样使用 `PROPERTY DRAWER` 进行设定: - 通过 `:type:` 设定渲染的目标组件。比如 `:type: FlexBox` 会将当前 headline 下面的内容包装在一个 `` 中 - 通过 `:props:` 为组件传入属性 - 如果 headline 的标题是 `` 这样的组件声明格式,那么将会使用 headline 上的声明作为当前的容器组件 页面中的普通文本段落 (paragraph) 默认会被渲染为 `xxx` 组件格式: - 可以在上面添加 `#+ATTR_HTML:` 进行设定: + 语法为 `#+ATTR_HTML: :aaa 111 :bbb {222}`,其值需要遵循 ReactJS 语法规则 + 通过 `:type` 设定目标组件,比如 `:type Heading` 会将当前内容渲染为 `xxx` 组件格式 + 通过这种方式可向组件传递更多参数,比如 `#+ATTR_HTML: :type p :color "red" :style {{ marginTop: 2 }}` - 如果当前文本段落符合 `` 这样的组件语法,将不会进行任意转换。因此,可在 org 中自由地写原生 Spectacle 代码 - 为了方便原生代码的编写,可以通过 `M-x ox-spectacle-minor-mode` 激活一些额外的代码高亮和补全 (`capf`) 综上所述,对于同样的页面,存在多种不同的实现方式。 ```org * Slide1, 原生写法 Hello World -------------------------------- * Slide2, 纯粹 Org 写法 ** 这是一个弹性盒子 :PROPERTIES: :type: FlexBox :props: margin={2} :END: #+ATTR_HTML: :type Heading :color blue Hello *** 这是一个分步显示的内容 :PROPERTIES: :type: Appear :END: #+ATTR_HTML: :color yellow World -------------------------------- * Slide3, 混搭模式 ** #+ATTR_HTML: :type Heading :color blue Hello *** #+ATTR_HTML: :color yellow World -------------------------------- * Slide4,居中显示并设定背景 :PROPERTIES: :layout: Center :props: backgroundImage="url(xxx.png)" backgroundOpacity={0.5} :END: ** #+ATTR_HTML: :type Heading :color blue Hello *** #+ATTR_HTML: :color yellow World ``` 不同场景各种写法各有优劣。不要有心智包袱,想怎么写怎么写。了解其规则,合理使用即可。 如果想向页面中添加图片、表格、列表、代码块等,可以使用原生的 Spectacle 写法,但是更推荐使用 Org 语法,这样可以大幅度提高代码的简洁性。同样使用 `#+ATTR_HTML:` 向背后的组件传递属性。 ```org 图片 (): [[file:xxx.png]] #+ATTR_HTML: :width 200px [[file:xxx.png]] 表格 (): | name | price | |------+-------| | aaa | 222 | | bbb | 333 | 列表 (): - aaa 1. 111 2. 222 - bbb - ccc 代码 (): #+ATTR_HTML: :marginBottom {2} #+begin_src js const aaa = (e) => { console.log(e); } #+end_src ``` ### 容器组件 最主要的容器组件为 ``、``、``,它们分别对应 html 中的 block/flexbox/grid,尤其 flexbox 和 grid 是 html 标准中较新的东西,它们非常灵活非常强大。掌握了它们,就能对页面中各部分的布局做到随心所欲了: - https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox - https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout 容器组件的属性跟 html 中对应的 css 属性基本一致。这里不赘述。 ### 简化的分步显示 Spectacle 中使用 `` 和 `` 组件来控制分步显示: ```html 第一个 第二个 {(value, step, isActive) => isActive ? 'The first stepper is not active. Step:' + step + ' Value:' + value : 'The first stepper is active. Step:' + step + ' Value:' + value } ``` Appear 使用相对频繁,因此这里提供了若干简化语法。 可以通过 `#+ATTR_HTML` 向 Image/Code/Table 组件传递 `:type A`,从而为其修饰 Appear: ```org 使用 :type A 表示图片用 Appear 包围: #+ATTR_HTML: :type A [[file:ssss.png]] 等同于: [[file:ssss.png]] A 后面可以跟更多参数,比如下面 priority={3} 将传递给 Appear。注意语法细节: #+ATTR_HTML: :type A priority={3} :margin {2} :border "1px solid red" [[file:ssss.png]] 可以使用 :type NUM 直接表示 priority 为 NUM 的 Appear: #+ATTR_HTML: :type 2 activeStyle={color: "red"} [[file:ssss.png]] 代码块和表格使用同样的机制。不赘述: #+ATTR_HTML: :type A #+begin_src js const aaa = 3 #+end_src ``` 列表条目的分步显示,简化语法类似,只不过是通过 `` 的方式传递分步参数: ```org - 普通的正常显示的条目 - 如果在前面存在 那么本项将参与到分步显示中 - <2> 如果在前面存在 <数字> 那么本项将会参与到分步显示,并且其显示的优先级 (priority) 为括号中的数字 - <1> 这个的优先级要比上面的高,所以会比上面的先显示 - 可以为 Appear 传入参数 - 如果尖括号内不以 A 或 NUM 开始,那么这些属性将传递给 ListItem - 对于这样的形式,priority/inactiveStyle 等属性将传递给 Appear,其他将传递给 ListItem ``` 借助上述方式,可以节省大量 Appear,让代码更简洁。 ### 特殊的 `` 节点 可以在 Org 文件中添加一个标题为 `` 的特殊 headline。其中的内容将不会被 export engine 导出,而仅作配置使用。 其基本规则如下: - `` 下如果存在命名格式为 `**