官网:http://html2canvas.hertzen.com/
当前使用版本:v1.0.0-rc.7


前情提要

需求介绍

将左侧“实时”的有数据的地图和后台推送的文字“动态”生成右侧的图片并分享到朋友圈。
(前提:前端生成分享朋友圈的图片)
image.png

  1. 页面中的“动态”的地图 - 可点击查看当天该地区的停运车次 - 使用svg实现 - raphael
  2. 需要组装的内容:
  • 背景图
  • 地图(.svg)+ 地图右侧的南海缩略图(.png)
  • 动态文字

思路分析

  1. 创建一个隐藏的canvas画布,
  2. 将地图、背景图、文字画/转移/拼接到画布上,(重点)
  3. 再将画布转换为base64的形式传给客户端分享的api。

问题转移:如何将以上那些“奇奇怪怪”的元素转移到canvas上!

image.png


image.png
image.png

步骤一:获取HTMLImageElement对象

image.png

步骤二:使用函数drawImage将图片绘制到画布上

image.png


image.png

问题再次转移:如何将html/svg元素转为HTMLImageElement了!


实践

image.pngimage.png
**

1. 若是.svg的图片,或是简单的不含路径的svg元素

  1. 先将svg转化为html2canvas 技术分享 - 图10元素;
    1. .svg文件:直接将文件赋值到img.src上
    2. image/svg+xmlhttps://jsbin.com/sahuxuduje/edit?html,js,output(同示例一)
  2. img.onload的时候调用canvas.drawImage(img)

image.png

image/svg+xml示例(一)
地址:https://jsbin.com/mohamikuru/1/edit?html,js,output

  1. const canvas = document.querySelector('#canvas')
  2. const context = canvas.getContext('2d')
  3. const parent = document.querySelector('#parent')
  4. // this is different
  5. const svg_string = parent.innerHTML.trim().replace('https', 'http')
  6. const img = new Image()
  7. img.src = 'data:image/svg+xml;charset=utf-8,' + svg_string
  8. console.log(img.src)
  9. img.onload = function(){
  10. console.log(img)
  11. context.drawImage(img, 0, 0)
  12. }


image/svg+xml示例(二)
地址:**https://jsbin.com/sahuxuduje/edit?html,js,output

  1. const canvas = document.querySelector('#canvas')
  2. const context = canvas.getContext('2d')
  3. const svg = document.querySelector('#test')
  4. // this is important
  5. const svg_string = (new XMLSerializer).serializeToString(svg)
  6. const img = new Image()
  7. img.src = 'data:image/svg+xml;charset=utf-8,' + svg_string
  8. img.onload = function(){
  9. context.drawImage(img, 0, 0)
  10. }

image.png

优点:简单、方便
缺点:地址内容太多了!
如果svg复杂,生成的src路径会特别大,导致图片onload失败(解决思路:**可用blob**)
image.png
image.png

2. 若为html元素或复杂的svg元素(同样也是我们面临的问题)

  1. 思路一:使用svg的

思路一示例
image.png
地址一:https://jsbin.com/tepufepeva/edit?html,js,output
image.png
地址二:https://jsbin.com/qamekekaki/edit?html,js,output

  1. const canvas = document.getElementById('canvas');
  2. const ctx = canvas.getContext('2d');
  3. const source = document.getElementsByClassName('wrapper');
  4. // 创造svg
  5. const data = `
  6. <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  7. <foreignObject width="100%" height="100%">
  8. <div xmlns="http://www.w3.org/1999/xhtml">
  9. ${source[0].innerHTML}
  10. </div>
  11. </foreignObject>
  12. </svg>
  13. `
  14. const img = new Image()
  15. // 将svg的内容放在blob内
  16. const svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'})
  17. // 生成svg的指定源的objectUrl
  18. const url = window.URL.createObjectURL(svg)
  19. // 将url赋值给img
  20. img.src = url
  21. img.onload = function() {
  22. ctx.drawImage(img, 0, 0)
  23. window.URL.revokeObjectURL(url)
  24. }

优点:简单、方便,解决了url特别长的问题
缺点:图片必须同源
image.png

  1. 思路二:需要一个工具来实现,他可以将html和svg转化为一种特定的“树”,然后一对一地调用canvas的api将这些内容全部画一遍。

image.png

坚持到这里的已经可以晋级了!

html2canvas

快速入手

  1. install

    1. npm install html2canvas -S
  2. usage

    1. import html2canvas from 'html2canvas';
    2. html2canvas(document.body, {
    3. // 根据官网提供的配置文件进行配置,选填
    4. }).then(function(canvas) {
    5. document.body.appendChild(canvas);
    6. });

    image.png

    注意事项

  3. 使用的时候需要注意html2canvas官网提出的不支持的css特性(例如write-mode)

  4. 如果图片是跨域获取的需要进行单独配置或将压缩后图片放在本地

html2canvas工作原理

相关代码:https://github.com/niklasvh/html2canvas/blob/3982df1492bdc40a8e5fa16877cc0291883c8e1a/src/index.ts

其他知识点分享:

  1. element.ownerDocument - Node.ownerDocument 只读属性会返回当前节点的顶层的 document 对象。
  2. ownerDocument.defaultView - 在浏览器中,该属性返回当前 document 对象所关联的 window 对象,如果没有,会返回 null。

Q&A

  1. 长图为什么“截图”出现空白?
    1. 检查传入的windowWidth、windowHeight是否正确;
    2. 检查长度是否超过浏览器的限度;
    3. 检查元素是否“可见”(不能设置display:none)
    4. 检查传入的x,y坐标是否正确;
      1. getBoundingClientRect兼容问题
  2. 为什么“截图”的有时候正常有时候又为空?
    1. 等待所有请求(数据请求、图片请求)结束后再进行“抓取”
  3. 跨域图片无法正常“抓取”?
    1. 让图片不要跨域
      1. 图片压缩后放在本地
    2. 图片仍期待跨域
      1. 使用官方推荐的config配置
      2. 同时需要图片的服务器设置可跨域访问
  4. 如何解决图片模糊的问题?
    1. 请用html2canvas 技术分享 - 图20来代替css background-image引入的图片
    2. 请开启scale:2
  5. 安卓端只可以截取可视区域的内容,document.scrollingElement.scrollTo(0,0)
    1. document.scrollingElement.scrollTo(0, 0)
    2. // 等这么长时间主要是实际中发现的需要(手机端)
    3. setTimeout(() => {
    4. this.drawMapAndInvoke()
    5. }, 1000)

使用场景

  1. 前端生成图片分享好友、朋友圈(回顾
  2. 前端上报

参考文章
**

  1. html2canvas转图片遇到的坑(图片偏移,图片模糊,字体改变) - 链接
  2. html2canvas截图失真怎么解决? - 知乎链接
  3. HTML如何转化为canvas教程 - 链接
  4. html2canvas以及domtoimage的使用踩坑总结 - 链接
  5. html2canvas - 项目中遇到的那些坑点汇总(更新中…) - 链接
  6. html2canvas原理 - 链接
  7. 使用html2canvas实现浏览器截图 - 链接
  8. Web动态图片合成与分享——html2canvas方案实践 - 链接