经过了连续5
篇复杂度略高的物理模拟
系列,我在想看官们多少会有点疲乏~
所以我在规划了几篇『中场休息
』系列科普文,用来穿插在主要的chapter
之间,
休息是为了走更长远的路,还有看更多的物理模拟(X
主要内容会讲一些比较容易理解~一篇之内就可以讲完的案例。
这篇文是『中场休息
』系列的第一篇
文,我们这次会讲讲怎麽样在Canvas上实作 html 转图像的功能。
大部分人提到htmlToCanvas的实作,应该会直接想到html2Canvas这个着名的NPM
包,但是我们秉持着NINJA精神
当然不能光会用别人写的包,所以我们就来看看这个案例的实作原理
。
其实htmlToCanvas的实作在这一篇MDN
的旧版文章(已经被封存)里面有提到过做法
。
这边直接把源码贴上来:
<canvas id="canvas" style="border:2px solid black;" width="200" height="200"></canvas>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
'<em>I</em> like <span style="color:white; text-shadow:0 0 2px blue;">cheese</span>' +
'</div>' +
'</foreignObject>' +
'</svg>';
var DOMURL = window.URL || window.webkitURL || window;// 这是一个防呆,因为不同浏览器的createObjectURL方法可能存在於不同对象底下。
var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svg);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);//这个api是用来销毁已经用不到的URL,避免记忆体消耗
}
img.src = url;
codepen连结: https://codepen.io/mizok_contest/pen/RwgdpgR
在上面这个案例我们可以看到,html to canvas的实作流程大致如下:
foreignObject
tag 塞到svg内部Blob
类的建构式去把svg字串转换成Blob
物件URL.createObjectURL(Blob)
去取得转化出来的Blob
物件的URL
这边我们就每个步骤稍微说明一下~
首先我们来讲讲foreignObject
是什麽。
有自己写过svg
的同学应该很清楚,svg的原生元素是没有办法做文字段落自动换行的,一般要换行的话,我们只能透过把断行的部分写成多个<tspan>
,然後手动指定tspan
的座标值,让他看起来像换到下一行
(听起来很笨,但是确实就是这样)
延伸阅读:tspan 在MDN上的介绍页面: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/tspan
而foreignObject
的存在意义其实就是可以让我们在svg的XML namespace(命名空间)
底下去把不同namespace
的结构语言
给渲染出来,像这样透过使用foreignObject
,我们就可以轻松地在svg
内部实现文字换行~
XML 指的是结构性语言,html/svg都是一种XML,但是他们只能在特定的namespace底下被渲染,不同的namespace底下会有不同的渲染逻辑~
透过使用foreignObject
把html
的内容埋进去svg
里面,这样我们就得到了一张具有html
外观的svg
了~大概可以这样理解。
Blob
其实是一种 类档案(File-like)
的物件
。
举个例来说~我们常常看到有些网页会有利用<input type="file">
做档案上传
的功能,
这些input
在on change时接到的东西其实就是Blob
的一种。
而我们这边则是透过手动把svg
字串传到Blob
类的建构式
,来建立一个全新的Blob
实例。
new Blob(array, options);
Blob
的第一个参数固定要传一个阵列,而阵列的内容可以允许传入字串
(也可以传入其他的东西,可以读MDN上的解释)
延伸阅读: MDN 上关於 Blob()的页面:https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
之所以用Blob
是因为後面需要用createObjectURL(Blob)
来取得Blob
实例的URL。
延伸阅读: MDN 上关於 createObjectURL(Blob)的页面:https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
如果不想要用Blob来取得image src url
,其实也是可以直接把埋入foreignObject
的svg string
直接写入<img>
的src
attribute,就像这样:
<img width="600" height="450" src='data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="100%" height="100%"><body xmlns="http://www.w3.org/1999/xhtml"><span style="color:red">123123123</span></body></foreignObject></svg>'>
其实ctx.drawImage我们之前有提到过(在像素操作概论的篇章)
可以看这边
他就是2DContext所提供的一个用来把图片
画到Canvas
的api
而这边就是把前面拿到的Blob
url 去赋予给 Image.src,然後再把这张图片给画出来~
到这边为止我们大概可以理解htmlToCanvas
的实作,但是以实际场景来讲其实很多时候不会像我们上面给的范例一样这麽简单。
打个比方,例如典型的跨域
问题,导致我们在程序中无法顺利取得正确的图片/样式表
,除此之外,因为为了要防堵资安漏洞,利用foreignObject
去取得html
的渲染画面这一操作其实有很多限制,例如:
foreignObject
中引入js
文件,这意味着有些透过js
生成的样式变成需要手动赋予到foreignObject
的html
元素上而为了应对各种复杂的截图
需求,才会有了html2Canvas
这样的插件,这个插件实际上是用了很多比较tricky的方法去绕过防堵机制来达成部分因为上述限制
而难以实行的问题,但是也因为这样这个插件当然也就会有被认定为bug
的状况。
到这边为止是这次的html2Canvas
实作介绍~希望大家喜欢 :D
<<: DAY15 - 处理/读取档案不可或缺的FileReader
Redis.config EVENT NOTIFICATION notify-keyspace-ev...
文章说明 文章分段 文章说明 deeplab的简单介绍、於我的意义 ep.1 tensorflow的...
今天要介绍的 Tip 是有关於 pipe 的 pure 与 impure,当没有任何额外的设定下,自...
本系列文章同步发布於笔者网站 上篇介绍了 Neutron 的架构,再接下来几天会介绍 Neutron...
今天在男友家写这篇,我一边写他一边趴在我背上乱摸,烦死了快写不完,给我走开RRRRRRRR 因为很多...