整理的前端截图的方案
HTML2canvas
该脚本通过读取 DOM 以及应用于元素的不同样式,将当前页面呈现为 canvas 图像。
它不需要来自服务器的任何渲染,因为整个图像是在客户端上创建的。但是,由于它太依赖于浏览器,因此该库不适合在 nodejs 中使用。它也不会神奇地规避任何浏览器内容策略限制,因此呈现跨域内容将需要代理来将内容提供给相同的源。
该脚本仍然处理非常实验状态,因此不建议在生产环境中使用它,也不建议使用它来构建应用程序,因为仍然会有重大更改。
该库应该可以在以下浏览器上正常工作
- Firefox 3.5+
- Google Chrome
- Opera 12+
- IE9+
- Edge
- Safari 6+
由于需要手动构建每一个 CSS 属性以支持,因此还有许多尚不支持的属性。
使用方法
// 安装
npm install html2canvas
// 引入
import html2canvas from 'html2canvas'
// 使用
html2canvas(document.body).then(function(canvas) {
document.body.appnedChild(canvas)
})
中文文档
https://allenchinese.github.io/html2canvas-docs-zh-cn/
缺陷
会有 dom 样式还原和 img 的同源策略的问题
dom-to-image 与 dom-to-image-more
dom-to-image-more 是 dom-to-image 的升级版
dom-to-image-more 是一个库,它可以将任意 DOM 节点(包括同源和 blob iframe)转换为用 JavaScript 编写的矢量(SVG)或光栅(PNG 或 JPEG)图像。
使用方法
// 安装
npm install dom-to-image-more
// 引入
/* in ES 6 */
import domtoimage from "dom-to-image-more";
/* in ES 5 */
var domtoimage = require("dom-to-image-more");
// 使用
var node = document.getElementById("my-node");
domtoimage
.toPng(node)
.then(function (dataUrl) {
var img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
})
.catch(function (error) {
console.error("oops, something went wrong!", error);
});
文档
https://www.npmjs.com/package/dom-to-image-more
缺陷
也有样式还原不完全或者不正确问题
使用 navigator.mediaDevices.getDisplayMedia() API
这个 MediaDevices 接口的 getDisplayMedia() 方法提示用户去选择和授权捕获展示的内容或部分内容(如一个窗口)在一个MediaStream 里。然后,这个媒体流可以通过使用 MediaStream Recording API 被记录或者作为WebRTC 会话的一部分被传输。
这个 api 返回的屏幕视频流可以被用于 video 标签来进行播放,然后 video 又可以通过 canvas 的 context 的 drawImage 方法绘制一帧作为 canvas 的页面。随后使用 canvas.toDataURL 绘制成 base64 的图像数据。
这个数据可以通过种种方式进行使用。
使用方法
// 容器
let imgFile, imgUrl;
// 点击进行截图
function leaderHandler() {
// 创建标签
const video = document.createElement("video");
// 自动播放
video.setAttribute("autoplay", "autoplay");
// 静音
video.setAttribute("muted", "muted");
startCapture(function (screen) {
video.srcObject = screen;
});
video.addEventListener("loadeddata", function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// 设置一个宽高
canvas.width = 1280;
canvas.height = 773;
if (!ctx) return;
setTimeout(() => {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
let base64 = canvas.toDataURL("image/png");
// 转为File对象
imgFile = base64ImgtoFile(base64);
// HACK 创建的地址 可以通过revokeObjectURL()清除以释放内存 是个优化点
imgUrl =
window.webkitURL.createObjectURL(img) ||
window.URL.createObjectURL(img);
// 停止以释放内存和停止分享流
let tracks = video.srcObject.getTracks();
tracks.forEach((track) => track.stop());
video.srcObject = null;
}, 250);
});
}
// 开启视频流截屏
async function startCapture(success) {
var constraints = {
audio: false,
video: true,
width: 1280,
height: 773,
};
if (navigator.mediaDevices?.getDisplayMedia) {
navigator.mediaDevices.getDisplayMedia(constraints).then(success);
} else {
navigator.getDisplayMedia(constraints).then(success);
}
}
// base64 转file对象
function base64ImgtoFile(data, filename = "file") {
const arr = data.split(",");
const mime = arr[0].match(/:(.*?);/)[1];
const suffix = mime.split("/")[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime,
});
}
缺陷
需要用户同意权限
有模糊的问题,不能做到完全清晰,但是也有对应缓解方案。
移动端兼容问题
缓解不太清晰的方案
canvas 标签,将 width 和 height 标签属性扩大一倍,然后通过 css 将样式上的 width 和 height 规定成正确的样式。
Puppeteer
这是一个使用 node 的后端解决方案,让 node 去加载页面,像爬虫一样。然后再使用浏览器的 devtool 进行截屏。
缺陷
需要 node 做中间层,而且是 node 去重新加载页面,不会保留用户的操作