官网:http://html2canvas.hertzen.com/
当前使用版本:v1.0.0-rc.7
前情提要
需求介绍
将左侧“实时”的有数据的地图和后台推送的文字“动态”生成右侧的图片并分享到朋友圈。
(前提:前端生成分享朋友圈的图片)
- 页面中的“动态”的地图 - 可点击查看当天该地区的停运车次 - 使用svg实现 - raphael
- 需要组装的内容:
- 背景图
- 地图(.svg)+ 地图右侧的南海缩略图(.png)
- 动态文字
思路分析
- 创建一个隐藏的canvas画布,
- 将地图、背景图、文字画/转移/拼接到画布上,(重点)
- 再将画布转换为base64的形式传给客户端分享的api。
问题转移:如何将以上那些“奇奇怪怪”的元素转移到canvas上!
步骤一:获取HTMLImageElement对象
步骤二:使用函数drawImage将图片绘制到画布上
问题再次转移:如何将html/svg元素转为HTMLImageElement了!
实践
1. 若是.svg的图片,或是简单的不含路径的svg元素
- 先将svg转化为元素;
- .svg文件:直接将文件赋值到img.src上
- image/svg+xml:https://jsbin.com/sahuxuduje/edit?html,js,output(同示例一)
- img.onload的时候调用canvas.drawImage(img)
image/svg+xml示例(一)
地址:https://jsbin.com/mohamikuru/1/edit?html,js,output
const canvas = document.querySelector('#canvas')
const context = canvas.getContext('2d')
const parent = document.querySelector('#parent')
// this is different
const svg_string = parent.innerHTML.trim().replace('https', 'http')
const img = new Image()
img.src = 'data:image/svg+xml;charset=utf-8,' + svg_string
console.log(img.src)
img.onload = function(){
console.log(img)
context.drawImage(img, 0, 0)
}
image/svg+xml示例(二)
地址:**https://jsbin.com/sahuxuduje/edit?html,js,output
const canvas = document.querySelector('#canvas')
const context = canvas.getContext('2d')
const svg = document.querySelector('#test')
// this is important
const svg_string = (new XMLSerializer).serializeToString(svg)
const img = new Image()
img.src = 'data:image/svg+xml;charset=utf-8,' + svg_string
img.onload = function(){
context.drawImage(img, 0, 0)
}
优点:简单、方便
缺点:地址内容太多了!
如果svg复杂,生成的src路径会特别大,导致图片onload失败(解决思路:**可用blob**)
2. 若为html元素或复杂的svg元素(同样也是我们面临的问题)
- 思路一:使用svg的
思路一示例
地址一:https://jsbin.com/tepufepeva/edit?html,js,output
地址二:https://jsbin.com/qamekekaki/edit?html,js,output
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const source = document.getElementsByClassName('wrapper');
// 创造svg
const 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">
${source[0].innerHTML}
</div>
</foreignObject>
</svg>
`
const img = new Image()
// 将svg的内容放在blob内
const svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'})
// 生成svg的指定源的objectUrl
const url = window.URL.createObjectURL(svg)
// 将url赋值给img
img.src = url
img.onload = function() {
ctx.drawImage(img, 0, 0)
window.URL.revokeObjectURL(url)
}
优点:简单、方便,解决了url特别长的问题
缺点:图片必须同源
- 思路二:需要一个工具来实现,他可以将html和svg转化为一种特定的“树”,然后一对一地调用canvas的api将这些内容全部画一遍。
坚持到这里的已经可以晋级了!
html2canvas
快速入手
install
npm install html2canvas -S
usage
import html2canvas from 'html2canvas';
html2canvas(document.body, {
// 根据官网提供的配置文件进行配置,选填
}).then(function(canvas) {
document.body.appendChild(canvas);
});
注意事项
使用的时候需要注意html2canvas官网提出的不支持的css特性(例如write-mode)
- 如果图片是跨域获取的需要进行单独配置或将压缩后图片放在本地
html2canvas工作原理
相关代码:https://github.com/niklasvh/html2canvas/blob/3982df1492bdc40a8e5fa16877cc0291883c8e1a/src/index.ts
其他知识点分享:
- element.ownerDocument - Node.ownerDocument 只读属性会返回当前节点的顶层的 document 对象。
- ownerDocument.defaultView - 在浏览器中,该属性返回当前 document 对象所关联的 window 对象,如果没有,会返回 null。
Q&A
- 长图为什么“截图”出现空白?
- 检查传入的windowWidth、windowHeight是否正确;
- 检查长度是否超过浏览器的限度;
- 检查元素是否“可见”(不能设置display:none)
- 检查传入的x,y坐标是否正确;
- getBoundingClientRect兼容问题
- 为什么“截图”的有时候正常有时候又为空?
- 等待所有请求(数据请求、图片请求)结束后再进行“抓取”
- 跨域图片无法正常“抓取”?
- 让图片不要跨域
- 图片压缩后放在本地
- 图片仍期待跨域
- 使用官方推荐的config配置
- 同时需要图片的服务器设置可跨域访问
- 让图片不要跨域
- 如何解决图片模糊的问题?
- 请用来代替css background-image引入的图片
- 请开启scale:2
- 安卓端只可以截取可视区域的内容,document.scrollingElement.scrollTo(0,0)
document.scrollingElement.scrollTo(0, 0)
// 等这么长时间主要是实际中发现的需要(手机端)
setTimeout(() => {
this.drawMapAndInvoke()
}, 1000)
使用场景
- 前端生成图片分享好友、朋友圈(回顾)
- 前端上报
参考文章
**