06月03, 2016

html2canvas 将代码转为图片

最近,我通过众成翻译学习和翻译了一系列优秀的文章,许多文章都值得向大家推荐。但是,好的技术文章通常都有大段的代码。一般情况下,这并没有什么问题,因为不论是众成翻译、我的博客或者是其他转载技术类文章的平台,一般都支持代码的语法高亮展示和阅读。

但是,也有一些公共的平台对代码的支持并不理想,比如微博的长微博:

长微博里编辑正常的代码

到了预览的时候被“自动压缩”了

再比如微信公众号,粘贴文章的时候,如果有代码,会丢失掉代码里面的一些换行。如果代码较短还好,可以手工编辑下,但是如果是长代码的话,效果简直是惨不忍睹,也很难编辑:

博客里正常的代码

贴到微信公众号编辑文章里惨不忍睹

如果一一把代码格式调整好会是一个非常巨大的工作量,偶尔做一下还好,如果天天要把时间花费在编辑和调整代码的格式上,那也太悲催了。

为了更好地在各种非主流(对程序员来说)的平台上愉快地贴代码,我们只能干脆将代码转为图片。最简单的转换方式,当然是——截屏。

截屏的问题

利用操作系统自带的截屏或者其他工具的截屏功能来截取代码当然是可行的。只是,不管是系统默认截屏还是截屏工具,都不是特别好用,尤其当代码超过一屏的时候,还得分屏截取,而当你决定修改代码的一部分的时候,有可能又要调整和重新截屏,这对于一大段一大段代码的文章编辑来说是个噩梦一样的体验,而且截成一段段的代码,对读者也不友好。

另外一个截屏的问题是,截屏效果依赖于我们使用的 IDE,有可能写不同的代码使用不同的 IDE,比如我喜欢用 vim 写 json 配置和 nginx 配置,但我用 Sublime Text 3 编辑 HTML、CSS 和 JS,如果要写 Android Native 代码,我用 Android Studio,写其他一些编程语言,我可能会使用 XCode。不同的 IDE 界面截出来的代码风格看起来不同,这样就会造成文章之间风格不统一或者甚至一篇文章中的代码风格也五花八门,这对于希望自己的文章被完美呈现的作者来说,简直不能忍。

用工具将代码转图片

为了解决截屏的这些问题,我写了一个在线代码转图片的工具(github 代码仓库),将代码粘贴进输入框,点击左上角的相机图标或者用键盘快捷键 Ctrl+D,就可以自动将代码转成语法高亮的图片。

代码段即使很长,超过屏幕高度,也能轻松截取下来:

语法高亮基于 Code Prettify,支持超过 40 种编程语言的语法高亮。主流编程语言自动识别,不需要选择语言类型就可以自动识别“Java、JS、HTML、C、C++、Python、PHP”等主流编程语言。

转换代码到图片使用 html2canvas,这是一个非常著名的从浏览器网页截图的开源库,使用很方便,功能也很强大。

使用 html2canvas

html2canvas 的使用非常简单,简单到只需要传入一个 DOM 元素,然后通过回调拿到 canvas:

html2canvas(element, {
    onrendered: function(canvas) {
        // canvas is the final rendered <canvas> element
    }
});

在实际使用的时候,有两个注意点:

  1. html2canvas 通过解析元素实际的样式来生成 canvas 图片内容,因此它对元素实际的布局和视觉显示有要求。如果要完整截图,最好将元素从文档流中独立出来(例如 position:absolute)
  2. 默认生成的 canvas 图片在 retina 设备上显示很模糊,处理成 2 倍图能解决这个问题:
var w = $("#code").width();
var h = $("#code").height();

//要将 canvas 的宽高设置成容器宽高的 2 倍
var canvas = document.createElement("canvas");
canvas.width = w * 2;
canvas.height = h * 2;
canvas.style.width = w + "px";
canvas.style.height = h + "px";
var context = canvas.getContext("2d");
//然后将画布缩放,将图像放大两倍画到画布上
context.scale(2,2);

html2canvas(document.querySelector("#code"), {
  canvas: canvas,
  onrendered: function(canvas) {
    ...
  }
});

对多语言的支持

Code Prettify 默认支持多种语言,所以一开始的时候,没有设计成让用户能够手动切换语言,但是发现这样做,在展示 CSS 的时候效果不好:

上面的代码里,CSS 的 ID 选择器被识别成代码注释了,查看 Code Prettify 的官方文档发现,它默认支持的语言中不包括 CSS,CSS 和其他一些语言的语法高亮需要通过扩展来实现。

使用扩展

我使用 URL 的 hash 来标记扩展,例如要使用 CSS 扩展,可以使用 http://code2img.test.h5jun.com/#css

使用 CSS 语法高亮扩展

使用 hash 来标记扩展的好处是,我在页面顶部放了一些常用语言的扩展链接,切换这些链接的时候由于是改变 hash,不会刷新页面。

第一次切换到某个扩展的语言时,需要加载相应的 js 文件:

var srcMap = {
  "apollo": "lang-apollo.js",
  "basic": "lang-basic.js",
  "clojure": "lang-clj.js",
  "css": "lang-css.js",
  "dart": "lang-dart.js",
  "erlang": "lang-erlang.js",
  "go": "lang-go.js",
  "haskell": "lang-hs.js",
  "lasso": "lang-lasso.js",
  "lisp": "lang-lisp.js",
  "scheme": "lang-lisp.js",
  "llvm": "lang-llvm.js",
  "logtalk": "lang-logtalk.js",
  "lua": "lang-lua.js",
  "matlab": "lang-matlab.js",
  "ml": "lang-ml.js",
  "mumps": "lang-mumps",
  "nemerle": "lang-n.js",
  "pascal": "lang-pascal.js",
  "protocol": "lang-proto.js",
  "r": "lang-r.js",
  "rd": "lang-rd.js",
  "rust": "lang-rust.js",
  "scala": "lang-scala.js",
  "sql": "lang-sql.js",
  "swift": "lang-swift.js",
  "tcl": "lang-tcl.js",
  "latek": "lang-tex.js",
  "vb": "lang-vb.js",
  "vhdl": "lang-vhdl.js",
  "wiki": "lang-wiki.js",
  "xq": "lang-xq.js",
  "yaml": "lang-yaml.js"
};

function loadPlugin(lang){
  var js = srcMap[lang.toLowerCase()];  

  if(typeof js === "string"){
    var script = document.createElement("script");
    script.src = "/static/js/" + js;
    document.body.appendChild(script);
  } 

  //srcMap[lang] 设为 true,表示已经加载过,下次切换就不会再加载
  srcMap[lang] = true;  
}

许可协议

由于 Code Prettify 采用 Apache License 2.0,而 html2canvas 采用一个非商业授权协议,因此,本项目采用双重许可协议,使用、修改和分发本项目代码需要遵守两个协议。

总结

由于一些平台对文本代码支持不友好,因此我使用 html2canvas 和 Code Prettify 来实现一个将代码转为图片的工具,它的特点如下:

  • 在线转换代码为 jpeg base64 图片
  • 支持 40+ 种不同语言的语法高亮
  • 支持多屏的长代码转成一张图片

如果你有在微信平台或者微博上发技术文章的需求,你应该可以用到它。

对它感兴趣,可以 check out 整个项目的最新版本。

本文链接:https://www.h5jun.com/post/convert-code-to-image-via-html2canvas.html

-- EOF --

Comments