页面截屏是前端经常遇到的需求,比如页面生成海报,弹窗图片分享等。
以下是我整理三种截图方案:
- html2canvas
- dom-to-image
- webRTC
html2canvas 用的比较广泛的前端截图方案,先将 DOM 一个个 转为 Canvas 然后导出图片(使用 canvas 自带的 toDataUrl、toBobl)即可。使用起来应该是兼容性比较好的方案了,能解决大部分的需求,但是也有一写小问题,如:
- 图片跨域,开启
- CSS 属性错乱
- 遇到 canvas 元素导出后为透明色。
大部分问题还是可以通过配置和百度解决的
const getDomImg = (eleId) => {
html2canvas(document.getElementById(eleId), {
//superMap整个页面的节点
backgroundColor: null, //画出来的图片有白色的边框,不要可设置背景为透明色(null)
allowTaint: true,
useCORS: true, //支持图片跨域
scale: 1, //设置放大的倍数
})
.then((canvas) => {
//截图用img元素承装,显示在页面的上
let img = new Image();
img.src = canvas.toDataURL("image/jpg"); // toDataURL :图片格式转成 base64
// 直接下载
let a = document.createElement("a");
a.href = canvas.toDataURL("image/jpeg");
a.download = "test";
a.click();
})
.catch((err) => {
console.log("html2canvas err", err);
});
};
使用 svg,通过 createObjectURL 或 encodeURIComponent 处理 svg 得到图像资源,可以把 svg 绘制到 canvas。
dom-to-image-more 是dom-to-image的升级版 将 HTMl 放到 SVG 里,然后创建一个以 SVG 作为源的 Image 元素
但是也有一些问题如: svg 中不允许外部资源(js,css,img 的 url 等),svg 中不支持执行 js,需要经过处理,也不能完全还原
const getDomImg = (eleId) => {
domtoimage
.toPng(document.getElementById(eleId))
.then(function (dataUrl) {
let a = document.createElement("a");
a.href = dataUrl;
a.download = "test";
a.click();
})
.catch(function (error) {
console.error("生成失败", error);
});
};
使用浏览器原生 API——webERT中的getDisplayMedia可以将窗口中的资源以录屏方式从其中拿出一帧,但是需要用户授权和做一些窗口选择,相比于前两种方案做不到默认截图。
但是优势也很明显,就是不会有什么样式错乱、图片跨域等问题。因为使用的浏览器原生方法,基本上用户看到是什么样子,截图出来就是什么样子,1: 1 还原。
function getDomImg(videoId: string) {
const videoElem: any = document.getElementById(videoId);
const displayMediaOptions = {
audio: false,
video: { width: window.screen.width, height: window.screen.height }, // cursor: "always"
};
navigator.mediaDevices
.getDisplayMedia(displayMediaOptions)
.then((stream) => {
videoElem.srcObject = stream;
setTimeout(() => {
var canvas = document.createElement("canvas");
canvas.width = videoElem.clientWidth;
canvas.height = videoElem.clientHeight;
canvas
.getContext("2d")
.drawImage(
videoElem,
0,
0,
window.screen.width,
window.screen.height,
0,
0,
canvas.width,
canvas.height
);
var dataURL = canvas.toDataURL("image/webp");
let a = document.createElement("a");
a.href = dataURL;
a.download = "test";
a.click();
let tracks = videoElem.srcObject.getTracks();
tracks.forEach((track) => track.stop());
videoElem.srcObject = null;
}, 200);
})
.finally(() => {});
}
需要在代码中放入一个 Video 标签
<video id="video"></video>
如果有将截图上传的需求,可以转换一个格式在上传。
canvas 导出的 base64 是不可以直接上传到服务器的,所以需要转一下格式,我这边找了转换 Blob 和 file 两种格式的方法。我用的将图片转为 Blob后上传的。
- base64转化为Blob对象
//
function convertBase64ToBlob(imageEditorBase64) {
var base64Arr = imageEditorBase64.split(",");
var imgtype = "";
var base64String = "";
if (base64Arr.length > 1) {
//如果是图片base64,去掉头信息
base64String = base64Arr[1];
imgtype = base64Arr[0].substring(
base64Arr[0].indexOf(":") + 1,
base64Arr[0].indexOf(";")
);
}
// 将base64解码
var bytes = atob(base64String);
//var bytes = base64;
var bytesCode = new ArrayBuffer(bytes.length);
// 转换为类型化数组
var byteArray = new Uint8Array(bytesCode);
// 将base64转换为ascii码
for (var i = 0; i < bytes.length; i++) {
byteArray[i] = bytes.charCodeAt(i);
}
// 生成Blob对象(文件对象)
return new Blob([bytesCode], { type: imgtype });
}
- base64 转 formData
function base64ToFile(data, fileName) {
const dataArr = data.split(",");
const byteString = atob(dataArr[1]);
const options: any = {
type: "image/jpeg",
endings: "native",
};
const u8Arr = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
u8Arr[i] = byteString.charCodeAt(i);
}
let formData = new FormData();
let fileOfBlob = new File([u8Arr], fileName + ".jpg", options); //返回文件流
formData.append("file", fileOfBlob);
console.log("file", formData);
debugger;
return formData;
}
GitHub:https://github.com/AnsonZnl/web-screenshot
代码基于 Create React App 演示三种截图方法的基本使用方式。