html2canvas截图div_建议精读史上最详细浏览器端⽹页截图
⽅案解析
1简介
剖析流⾏的截图插件 html2canvas 的实现⽅案,探索其功能上的⼀些不⾜之处及不能正确截取的⼀些场景,⽐如不⽀持 CSS 的 box-shadow 截取情况等。探索⼀种新的实现⽅式,能够避免多数⽬前 html2canvas 不⽀持的情况,解密其原理,深究 Canvas 绘图的机制。
本篇⽂章你可以学到:
1. 纯前端⽹页截图的基本原理
2. html2canvas 的核⼼原理
3. SVG 内嵌 HTML 的⽅式
4. Canvas 渲染 SVG 的⽅式及各种问题的解决⽅案
适合⼈:前端开发
开篇
平时很多时候,需要把当前页⾯或者页⾯某⼀部分内容保存为图⽚分享出去,也或者有其他的业务⽤途,这种在很多的营销场景和裂变的过程都会使⽤到,那我们要把⼀个页⾯的内容转化为图⽚的这个过程,就是⽐较需要探讨的了。
⾸先这种情况,想到的实现⽅案就是使⽤ Canvas 来实现,我们探索⼀下基本实现步骤:
1. 把需要分享或者记录的内容绘制到 Canvas 上
2. 把绘制之后的 Canvas 转换为图⽚
这⾥需要明确的⼀点就是,只要把数据绘制到 Canvas 上,这就在 Canvas 画布上形成了被保存在内存中的像素点信息,所以可以直接调⽤ Canvas 的 API ⽅法 toDataURL、toBlob,把已经形成的像素信息转化为可以被访问的资源 URI,同时保存在服务器当中。这就很轻松的解决了第⼆步(把 Canvas 转为图⽚链接),下⾯是代码的实现:
在实现了第⼆步的情况之下,需要关注的就是第⼀步的内容,怎么把内容绘制到 Canvas 上,我们知道 Canvas 的绘图环境有⼀个⽅法是ctx.drawImage,可以绘制部分元素到 Canvas 上,包含图⽚元素 Image、svg 元素、视频元素 Video、Canvas 元素、ImageBitmap
数据等,但是对于⼀般的其他 div 或者列表 li 元素它是不可以被绘制的。
所以,这不是直接调⽤绘图的 API 就可以办到的,我们就需要思考其他的⽅法。在⼀般的实现上,⽐较常见的就是使⽤ html2canvas,那么我们先来聊聊 html2canvas 的使⽤和实现。
html2canvas 的使⽤及实现
使⽤
⾸先看⼀下 html2cavas 的使⽤⽅法:
调⽤ html2canvas ⽅法传⼊想要截取的 Dom,执⾏之后,返回⼀个 Promise,接收到的 Canvas 上,就绘制了我们想要截取的 Dom 元素。到这⼀步之后,我们再调取 Canvas 转图⽚的⽅法,就可以对其做其他的处理。
这⾥它的 html2canvas ⽅法还⽀持第⼆个选项传⼊⼀些⽤户的配置参数,⽐如是否启⽤缓存、整个绘图 Canvas 的宽⾼值等。
在这个转换的过程,在 html2canvas 的内部,是怎么把 Dom 元素绘制到 Canvas 上的,这是咱们需要思考的问题!
实现
⾸先咱们先献上⼀个内部的⼤致流程图:
image
对⽐着内部的流程图,就可以理⼀下整体的思路,整体的思路就是遍历⽬标节点和⼦节点,收集样式,计算节点本⾝的层级关系和根据不同的优先级绘制到画布中,下⾯基于这个思路,咱们深⼊⼀下整个过程。
1. 调⽤ html2canvs 函数,直接返回⼀个执⾏函数,这⼀步没有什么。
2. 在执⾏函数的内部第⼀步是构建配置项 defaultOptions,在合并默认配置的过程中,有⼀个缓存的配置,它会⽣成处理缓存的⽅法。
处理缓存类,对于⼀个页⾯中的多个不同的地⽅渲染调⽤多次的情况做优化,避免同⼀个资源被多次加载;
缓存类⾥⾯控制了所有图⽚的加载和处理,包括使⽤ proxy 代理和使⽤ cors 跨域资源共享这两种情况资源的处理,同时也对 base64和 blob 这两种形式资源的处理。⽐如如果渲染 Dom ⾥⾯包含⼀个图⽚的链接类型是 blob,使⽤的⽅式就是如下处理,然后添加到缓存类中,下次使⽤就不需要再重新请求。
image
3. 在上⼀步⽣成了默认配置的情况之下,传⼊需要绘制的⽬标节点 element 和配置到 DocumentCloner ⾥⾯,这个过程会克隆⽬标节点所在的⽂档节点 document,同时把⽬标节点也克隆出来。这个过程中,只是克隆了开发者定义的对应节点样式,并不是结合浏览器渲染⽣成特定视图最后的样式。
image
如上这个 .box 的元素节点,定义的样式只有⾼度,但是在浏览器渲染之下,会对它设置默认的⽂字样式等等。
4. 基于上⼀步的情况,就需要把克隆出来的⽬标节点所在的⽂档节点 document 进⾏⼀次浏览器的渲染,然后在收集最终⽬标节点的样式。于此,把克隆出来的⽬标节点的 document 装载到⼀个 iframe ⾥⾯,进⾏⼀次渲染,然后就可以获取到经过浏览器视图真实呈现的节点样式。
svg和canvas的区别
image
在这个过程中,就可以通过 ComputedStyle 这个 API 拿到要克隆的⽬标节点上所有的样式了(包含⾃定义和浏览器默认的结合最终的样式)。
5. ⽬标节点的样式和内容都获取到了之后,就需要把它所承载的数据信息转化为 Canvas 可以使⽤的数据类型,⽐如某⼀个⼦节点的宽度设置为 50%或者 2rem,在这个过程中,就需要根据⽗级的宽度把它计算成为像素级别的单位。同时对于每⼀个节点⽽⾔需要绘制的包括了边框、背景、阴影、内容,⽽对于内容就包含图⽚、⽂字、视频等。这个过程就需要对⽬标节点的所有属性进⾏解析构造,分析成为可以理解的数据形式。
如上图⽚这种数据结构和我注释⼀样,在它内部把每⼀个节点处理成为了⼀个 container,它的上⾯有⼀个 styles 字段,这个字段是所有节点上的样式经过转换计算之后的数据,还有⼀个 textNodes 属性,它表⽰当前节点下的⽂本节点,如上,每⼀个⽂本的点的内容使⽤text 来表⽰,位置和⼤⼩信息放置在 textBounds 中。对于 elements 字段存放的就是当前节点下除了⽂本节点外,其他节点转换成为的container,最后⼀个就是 bounds 字段,存放的是当前节点的位置和⼤⼩信息。可以看⼀下 container 这个类的代码:
基于这种情况,每⼀个 container 数据结构的 elements 属性都是⼦节点,整个节点就够构造成⼀个 container tree。
6. 在通过解析器把⽬标节点处理成特定的数据结构 container 之后,就需要结合 Canvas 调⽤渲染⽅法了,我们在浏览器⾥⾯创建多个元素的时候,不同的元素设置不同的样式,最后展⽰的结果就可能不⼀样,⽐如下⾯代码:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。