2

基于 Prettydoc 包的模板改造

 3 years ago
source link: https://cosx.org/2019/10/prettydoc-internals-ljj/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

基于 Prettydoc 包的模板改造

关键词:R Markdown; Prettydoc

在之前我的文章中,概括地介绍了修改 R Markdown 模板的思路。本文希望基于一个创作 R Markdown 文档的例子 (在电脑上看效果比较好),对常用的报告模板 Prettydoc 进行分析。读者可以根据自己的程度和需求,选择是停留在对模板的颜色、字体大小的程度,或是往前走一点——自己发明创造,进行阅读。

改造原理及意义

其实自定义一份模板起初没什么特殊含义,但最近深感创作侵权违法成本太低,举证困难,却苦于没有方法。自定义模板可以设置水印,可以设置选择失效,防止直接复制粘贴。虽然对于会写网页的人来说,这样的设防约等于没设,但是增加一些抄袭成本还是挺有意义的。

初步尝试的失败

最起初的想法是自己写一个模板,然后不断调 CSS 和 HTML 的位置和情况,甚至可以不依托 Prettydoc 定义出自己的模板。但毕竟自己不是前端工程师,没有如此深厚的前端功力。之后的想法是利用已经写好的博客模板,直接迁移到 Prettydoc 上,我自身的博客在 Jekyll 的基础上进行开发,也改造过一些模板,于是我从 Jekyll 模板 http://jekyllthemes.org/ 网站上试图下载一些下来,并进行修改。最后下载下来的模板由于其自身依赖的复杂性,十改九失败。即使改造成功,也存在着没调整好标签定位所导致网页变形的情况。 不得不承认,在自己的前端水平限制下,无法在较短的时间内完成如此的工程。最后还是退而求其次,从原有的 Prettydoc 模板出发,进行改动。即使是 Prettydoc 的模板,起初也是 github 中各种高手实现的,邱怡轩做的 Prettydoc 也是将这些模板综合起来,形成了一套完整的生成报告的流程。

改造所需要的知识

改造过程中最不需要的就是 R 的知识,这可能是很少有人愿意去改的原因,但其实整个过程不难。 一般的静态网页可以分为三个部分,分别用三种语言完成。这三个部分可以对应于人骨骼、皮囊和以血液为代表的动力系统。骨骼对应的是 HTML 所写的文本,HTML 的知识可以自行搜索,概括地说就是文本及其定位。皮囊对应的是 CSS 给网页带来的层层渲染,最简单的例如颜色、字体、位置等。血液之类的动力其来源是 JavaScript ,其作用是时整个网页 “动” 起来,或者是可以相应某些动作。

回到 Prettydoc 生成的文档,自定义的第一步还是需要从 R Markdown 的链接生成命令开始,下面截取了一小部分:

/.../pandoc/pandoc ... temp.utf8.md ... --output temp.html ... --template /.../prettydoc/resources/templates/cayman.html ... --css temp_files/style.css 

这里解读编译命令需要参考上一篇文章。为了照顾读者的关注点,我将不重要的部分都用三个点代替。

而模板所在的位置,就在上面 prettydoc 的 templates 文件夹下,即--template 后面的路径。

找到路径后发现往上翻一下,有四个文件夹,css、fonts、images、templates,注意没有 js 的文件夹,但并不代表无法将 js 链接进模板中。最朴素的想法是,将 js 代码直接写到 html 里,虽然这并不是一种很好的编程风格。可以先从原理出发,把这些问题一一记下,之后再一一解决。

  • 问题一 如何单独写自定义的 js 并将其引用?
  • 问题二 到底 prettydoc 是如何将这些文件链接起来形成文档的?

先解决第二个问题,观察源 R Markdown 文件所在的目录,然后点击 Knitr 生成 HTML 可以发现,生成时会先产生一个临时文件夹和一个临时的 Markdown 文件,所以原理其实也就是把需要的文件都链接起来,然后利用 pandoc 把该转变的语言都转变了,再删除临时文件即可。整个事件都在底层完成了。链接的是什么文件呢?再回到模板的目录,不难发现,也就五个模板,对应五种主题。由于网页一定需要 CSS 文件,所以再看 CSS 文件夹,除了字体文件夹,其余的十个 CSS 文件分别对应五种主题,每个主题都有一个开发版 CSS 和压缩版 CSS。

根据面向对象的思想(类似于多态),主题不可能被写死,否则不利于 Prettydoc 作者哪天心血来潮的时候,想自己加个主题还要重新编译自己的 R 包。基于这样的猜想,自定义主题就是复制一下他写的 HTML 文件和 CSS 文件,按照一样的形式,一对二,并将名字改成一致的,即可 “抄袭” 原有的模板,开始自定义主题。 这里可以说是整个改造 Prettydoc 包能推进下去的核心原理。

这时候,再仔细把每个主题的 HTML 打开,很容易发现,其中的内容是几乎一样的。这给改造节约了很多时间。改来改去,用的标签都是差不多且在 HTML 中已经写好,会变的无非就是 CSS 中的样式而已。CSS 变来变去,骨骼是不变的,无非是对应标题的位置放在左边还是右边的区别,配色好不好看,字体好不好看的区别而已。

但这里的改造不一定就遵循原作的风格,骨骼一样可以变,也就是可以增加新的标签去记录信息,例如可以在左上角建立一个统计功能,例子中的左上角就是对文档中漂浮点的统计。

下面以 Cayman 为例,改造一下模板。选择改这个模板的原因很朴素,Cayman 的 CSS 代码最短,改造起来最容易,其造型也比较容易为人接受。

改造模板的第一步是将原有的 HTML 文件和 CSS 文件都复制一下,然后重新命名为自己喜欢的名字。改完名字后,做一个测试,将 yaml 中的 theme 后面的主题换成自己改的名字,之后生成文档(做测试的时候最好不要复制修改命名 Cayman ,因为默认找不到主题文件时,会使用 Cayman 作为主题,这样就不确定复制是否有效)。

假定读者都找到了模板的位置,并进行了复制和改名,生成也成功了,下面来了第三个问题。

  • 问题三 CSS 的代码还是茫茫多,如何才能改?

其实这个问题对会使用 CSS 的人来说应该不是问题,但毕竟不是人人都会,这里可以简单提一下,详细理解还是需要参考讲解 CSS 的网站,本文无法穷尽。一般标签有两种方式定位,一种是 class,一种是 id。在 CSS 中定义 class 的属性,会对所有带同一个 class 名字的标签都进行渲染。id 是唯一标识,仅会渲染唯一的带有那个 id 的标签。也就是说 CSS 中一个个的大括号中的就是定义着某个类或者某个 id 的属性,找到想改的标签下对应的属性做修改即可。

不写网页的人对标签位置的寻找也许不太清楚。寻找内容所对应的标签其实很简单。利用浏览器右键检查,就会打开开发者工具,自动定位到鼠标所在位置的标签名字。

一般正常人想修改的就是背景颜色和字体颜色而已。检查后可以根据开发者工具中 style 颜色的具体情况,返回到 CSS 文件中,修改对应的颜色即可。

  • 问题四 有两个 CSS 文件,到底修改哪一个?

其实两个 CSS 文件的内容是一致的,但实际上 R Markdown 用的是\*.min.css。原因也很简单,\*.css的文件是正常的开发文件,\*.min.css是利用 CSS 工具手动压缩后的文件。因为后者删掉了很多冗余字符,可以使生成的文档更小。

下面就 Cayman 简单修改一下:

最醒目的颜色是 Cayman 最上面的一长条的颜色框,其颜色是渐变的,寻找一下渐变并带有 head 的属性,可以找到:

.page-header {
  color: #fff;
  text-align: center;
  background-color: #159957;
  background-image: linear-gradient(120deg, #155799, #159957);
  padding: 1.5rem 2rem;
}

渐变色显然就是background-image这里的颜色。不想随便试颜色最好的方法是上网去找喜欢的配色方案,之后可以得到以下 Head:

简单调头部的背景色

除此以外,还可以修改渐变颜色的方向,也可以将背景换成图片。但这些都仅仅是限制于表面的修改,最多能让文档的展示从表面上看上去与众不同。更深层次的修改,也就是添加 JavaScript 才是让报告更上一个台阶的关键。也是下面要谈的重点。

让文档动起来

要引入 JavaScript 就必须先回答问题一,最朴素的想法是在模板外的目录下创建 js 文件夹,按照 CSS 文件的引用情况,有可能会自动将 js 文件夹复制过去,但实际上,这样的想法还是太天真。只要在生成 temp_files 文件夹时,将其拖拽出原来的文件夹(只有在 R Markdown 将临时文件删除前拖出去才能看),就可以发现,在生成临时文件时,并没有生成 js 文件夹,自然直接放 js 文件夹不可行。转念再想,至少复制了 images 文件夹下的所有东西,那么复制 images 文件夹不也可以吗? 基于这个朴素的想法和路径的尝试,在将 script.js 文件放进 images 文件夹后,在 HTML 中的引用路径就可以如下:

<script  src="temp_files/images/script.js"></script>

引用其实只要找到路径即可,若是临时的,特殊的不属于特定模板的 js 文件,也可以用更简单的方法引用,即在 R Markdown 文件所在目录下放置 script.js 文件,那么引用的路径就可以是:

<script  src="script.js"></script>
<script  src="./script.js"></script>

这些都是比较取巧的解决方法,更为合理的解决方案还是应该从制作这个包的过程中,就将这个需求考虑进去。

起初可以加 JavaScript 之后,我停留在比较低级的阶段——无非加点水印之类的,知道后来在读 Graph Embedding 代码时,发现了一个有意思的博客 https://zhuo931077127.github.io/。其背景的粒子效果特别炫酷,于是有了据为己有的贪念。上网一搜粒子动画,就找到了一个制作此类动画的 JavaScript 库——https://github.com/VincentGarreau/particles.js

之后的制作过程也就比较简单了,在 https://codepen.io/VincentGarreau/pen/pnlso 上,先编辑好代码,然后将 HTML 、 CSS 、 JavaScript 的部分加到相应的部分中。例如 HTML 部分:

<div id="particles-js">
<section class="main-content" id="scro">
$body$
<span class="js-count-particles">--</span>
</section>
</div>

其中,JavaScript 部分直接生成 script.js 文件,并在 HTML 中添加 particle.min.js 等的引用。 CSS 部分则稍微需要一些调整,这也是困扰我两天的问题。

  • 问题五 canvas 和 div 同时存在时,会 “井水不犯河水”,如何做到 github 上那位用户的效果?
  • 问题六 生成的 canvas 太短,仅够遮住第一页,但 section 标签里的内容太长怎么办?

问题五很好解决,将其中的一个 position 改成固定即可。问题六确实困扰了我很久,但在反复观察别人的博客后,领悟到了一句话:风动还是云动?运动是相对的。起初我想的都是怎么让 canvas 不断填充 div,哪怕复制重复也可以,但效果并不好。其实博客中,根本是 canvas 没有动,动的是 div 标签。那么解决方法就很简单了,即给 section 加上一个新 id(参看上一个代码段,section 有一个idscro), 并在 CSS 中增加:

#scro{
overflow-y:scroll
}
::-webkit-scrollbar {
display: none;
}

其作用是让这个 div 中的内容滚动播放。但由于 canvas 的位置是 absolute ,canvas 就不会随滚轮向下走。后面的 display: none;是不显示滚轮。

最后可以得到的效果可以参看例子

此外,利用 canvas 写报告还是存在一个硬伤,在打印 pdf 时,canvas 是不能随着文档的展开而展开的,这也是需要不断探索解决方案的,动静之间,不可兼得。

但用 JavaScript 添加动画并不一定要 canvas。这里是想暗示,所有用 JavaScript 可以实现的效果都可以加到文档里。当把这些文档抽离出来,形成类似电子相册的东西时,就得到了博客。改造 Prettydoc 的一切技巧和解决思路都可以原模原样搬到自己的博客上。站在我的肩膀上,希望读者能创造出更精彩的模板,并跳出 Prettydoc 的框架。我可以假想有这样一个 R 包,以拖拽、绘画的方式在网页服务器上定制自己的模板,再通过命令反过来将生成的模板添加到这个 R 包中。那么毫无疑问 R Markdown 会绽放出更多不寻常的可能性。

有时候并不是能力做不到,在互联网的帮助下,基本有问题就能找到方法解决,只是方法的好坏而已。探索时最大的难题是没有想法,失去了灵感,做的工作也就是如工匠般的简单搬运。直接改 Prettydoc 的文件有违我的本意,虽然这是最容易自定义的方法。其实修改一个包没有什么难的,难的是能搭建起这个框架,让我能在其中修改。整个修改过程只是心血来潮地简单尝试,还有很多可以尝试的空间。JavaScript 可以加的动画无穷无尽,但有时我不得不反省,脱离了文档内容,而一味追求外观的酷炫是否南辕北辙?添加这样的动画,或许没有一开始时,简单地在文档中加水印和加反复制粘贴代码来的有意义。

本科就读于中国人民大学统计学院,研究生将就读于中国人民大学信息学院。热爱编程、运动和艺术。个人博客 https://llijiajun.github.io/github-io/李家郡

敬告各位友媒,如需转载,请与统计之都小编联系(直接留言或发至邮箱:[email protected]),获准转载的请在显著位置注明作者和出处(转载自:统计之都),并在文章结尾处附上统计之都微信二维码。

统计之都微信二维码

← 从贝叶斯视角看多层模型 翻译:常见统计检验的本质都是线性模型(或:如何教统计学) →

发表 / 查看评论


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK