前言
最近在项目中遇到一个需求,把活动页面转换为海报图片分享出去,在做这个需求时,尝试了以下两种方案,基于此做以下记录和分享。
方法一 html2canvas
使用方法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#poster{
width: 700px;
height: 500px;
background-color: green;
}
</style>
<script type="text/javascript" src="http://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
<script type="text/javascript">
function takeScreenshot() {
const node = document.getElementById('poster')
html2canvas(node, {
useCORS: true,
height: node.offsetHeight,
width: node.offsetWidth,
scrollY: 0,
scrollX: 0
}).then(async (canvas) => {
let oImg = new Image();
oImg.src = canvas.toDataURL(); // 导出图片
document.body.appendChild(oImg); // 将生成的图片添加到body
})
}
</script>
</head>
<body>
<div id="poster">
<input type="button" value="截图" onclick="takeScreenshot()">
</div>
</body>
</html>
复制代码
注意事项:
- 生成的图片模糊
有时候我们会发现,导出的图片局部有些图片看起来没有原图那么清晰,这其实是因为你使用背景图片的原因。解决方法也很简单,就是直接使用标签就好了
- 生成出来的图片有白色边框
在配置项中设置backgroundColor: null即可
- 生成图片不显示
这是最常见的一个bug,这基本就是因为图片素材出现跨域,基本就是在方法上调用时增加两个配置
allowTaint: true,
useCORS: true
复制代码
还有一个方法就是,把跨域的图片转为base64,也可以完美解决这个问题。
- PNG图片不透明
有时你可能用到透明的PNG图片作为背景图,可是结果最后生成的图片却并不透明,这是因为html2canvas生成的canvas背景颜色默认为白色的缘故,所以导出的图片背景颜色当然也是白色。 解决方案也是添加一个配置项就好(事实上经实验发现只要是非颜色类型的字符串都可以)。
backgroundColor: 'transparent'
复制代码
- 生成的图片加载闪动效果
先让生成的图片隐藏,添加图片加载状态,等图片生成好以后再展示。
GitHub
官方文档
方法二 dom-to-image
主要使用方法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#poster{
width: 700px;
height: 500px;
background-color: green;
}
</style>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js"></script>
<script type="text/javascript">
function takeScreenshot() {
const node = document.getElementById('poster')
domtoimage.toPng(node)
.then((dataUrl) => {
var img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
})
.catch((error) => {
console.error('oops, something went wrong!', error);
});
}
</script>
</head>
<body>
<div id="poster">
<input type="button" value="截图" onclick="takeScreenshot()">
</div>
</body>
</html>
复制代码
1. domtoimage.toPng(…);将节点转化为png格式的图片。
function takeScreenshot() {
const node = document.getElementById('poster')
domtoimage.toPng(node)
.then((dataUrl) => {
var img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
})
.catch((error) => {
console.error('oops, something went wrong!', error);
});
}
复制代码
2. domtoimage.toJpeg(…);将节点转化为jpg格式的图片。
function takeScreenshot() {
const node = document.getElementById('poster')
domtoimage.toJpeg(node, { quality: 0.95 })
.then((dataUrl) => {
var img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
})
.catch((error) => {
console.error('oops, something went wrong!', error);
});
}
复制代码
3. domtoimage.toSvg(…);将节点转化为svg格式的图片,生成的图片的格式都是base64格式。
function filter (node) {
return (node.tagName !== 'i');
}
function takeScreenshot() {
const node = document.getElementById('poster')
domtoimage.toSvg(node, { filter: filter })
.then((dataUrl) => {
var img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
})
.catch((error) => {
console.error('oops, something went wrong!', error);
});
}
复制代码
4. domtoimage.toBlob(…);将节点转化为二进制格式,这个可以直接将图片下载。
function takeScreenshot() {
const node = document.getElementById('poster')
domtoimage.toBlob(node)
.then((blob) => {
window.saveAs(blob, 'poster.png');
})
.catch((error) => {
console.error('oops, something went wrong!', error);
});
}
复制代码
5. domtoimage.toPixelData(…);获取原始像素值,以Uint8Array 数组的形式返回,每4个数组元素表示一个像素点,即rgba值。这个方法也是挺实用的,可以用于WebGL中编写着色器颜色。
function takeScreenshot() {
const node = document.getElementById('poster')
domtoimage.toPixelData(node)
.then((pixels) => {
for (var y = 0; y < node.scrollHeight; ++y) {
for (var x = 0; x < node.scrollWidth; ++x) {
pixelAtXYOffset = (4 * y * node.scrollHeight) + (4 * x);
/* pixelAtXY is a Uint8Array[4] containing RGBA values of the pixel at (x, y) in the range 0..255 */
pixelAtXY = pixels.slice(pixelAtXYOffset, pixelAtXYOffset + 4);
}
}
})
.catch((error) => {
console.error('oops, something went wrong!', error);
});
}
复制代码
注意事项:
- 用于生成图片的dom元素不能display:none或opacity:0。可以用z-index放到其它元素下面;或者用绝对定位,将其放到某个div容器的可视区之外,然后容器设置overflow:hidden
- 用于生成图片的dom元素的父元素们不能display:none
- 综合1、2,即用于生成图片的dom元素要本身是可见的,且能计算到尺寸值,但不需要出现在视野中
- 页面不能出现隐藏iframe的样式,例如iframe{display:none}
- 生成后的图,会自动追加到用于生成的dom元素后面,类名是dom2img-result,如果不想把图片的样子展示给用户,可将其样式设置为opacity:0