今天是中秋国庆的最后一天,先祝大家节后工作顺利!
也不知道大家都去哪里旅游了,我是翱翔在代码的海洋里无法自拔了哈哈哈(ps:手上累积的活太多了)。节前一个好manager拍了拍我的肩膀说给xxx应用加个花活,嗯,说到花活那我可来劲了,比起crud的工作多了点刺激不是吗😌。遂问:什么花活。m曰:xxx希望(要求)给应用加个截图并且要截这个区域的内容然后把这个图片先这样再那样。ok,听上去也不难啊,满口答应,毕竟作为前端仔指定画一片区域的事也没少做过不是吗。
踩坑
说到截图就想到之前做过把某个div打印出来,类似的理所应当的觉得照葫芦画瓢应该也可以截图吧,无非就是转base64呗。说干就干,无脑安装html2canvas
shell
npm i html2canvas
然后import html2canvas from "html2canvas";嘿,轻松,到这里我依然没有想到我要面对的到底是什么样的需求,然后开心的传入对应的dom,嗯,让我们来consolo.log看看有没有数据。完美!!!有base64数据,好,接下来:先创建一个a标签,然后设置link,然后append to body,再然后a.click,最后remove。哦,来看看我们生成的图片,啊?(张雪峰老师表情包,我没找到hhh)为什么是一片空白呢,会不会是dom没加载好,setTimeout个10秒再试试,waiting~,再看看啊,为什么还是空白!!!
这个时候脑子终于清醒了一点,一定哪里有问题(废话),来看看我要截图的是什么东西:
vue3
<div style="width: 100%; height: 100%" v-html="webviewHtml" id="webview-ele"></div>
const webviewHtml = ref('<webview src="https://www.baidu.com/" style="width: 100%; height: 100%"></webview>');
噢~想起来我现在写的不是纯纯的web了,是electron,怪不得里面还能内嵌一个webview,那html2canvas弄不来不是应该的吗。那怎么办呢,好像也没有能把webview里的东西给画出来的工具,看来想用纯纯的web去实现这个功能应该是不行了,打开百度no,熟练的打开gpt,这里不得不感叹一下科技的进步,gpt给咱们带来的效率提升可太大了(别问怎么访问的gpt,遵纪守法,爱国爱党)。
开始尝试不一样的
第一问:web页面调用系统截图
xml
<!DOCTYPE html>
<html>
<head>
<title>Web 页面截图示例</title>
</head>
<body>
<button onclick="takeScreenshot()">截取屏幕</button>
<script>
function takeScreenshot() { // 调用浏览器的截图功能
navigator.mediaDevices.getDisplayMedia({ video: true })
.then(stream => { const videoTrack = stream.getVideoTracks()[0];
const imageCapture = new ImageCapture(videoTrack);
imageCapture.grabFrame() .then(imageBitmap => {
const canvas = document.createElement('canvas');
canvas.width = imageBitmap.width;
canvas.height = imageBitmap.height;
const context = canvas.getContext('2d');
context.drawImage(imageBitmap, 0, 0); // 将截图显示在页面上或执行其他操作
const screenshotImage = new Image();
screenshotImage.src = canvas.toDataURL('image/png');
document.body.appendChild(screenshotImage); }) .catch(
error => { console.error('截图失败:', error);
});
}) .catch(error => { console.error('获取屏幕截图权限失败:', error); }); }
</script>
</body>
</html>
真好啊,管他对不对呢,试了才知道,哎,怎么没反应,来看看控制台
获取屏幕截图权限失败: DOMException: Not supported
啊?这是为什么。我猜测是electron的原因,我预想的应该会有一个让用户给权限的弹窗才对,但是实际并没有,这第一次问就失败了,开头不顺啊。
第二问:我用electron,能否截图页面某一个区域呢
go
在 Electron 中,你可以使用 `electron` 模块的 `desktopCapturer` API 来截取屏幕或应用程序窗口的图像。但是,如果你需要截取页面上的特定区域,你可以使用 HTML5 的 Canvas 元素和 JavaScript 来进行截图。
第三问:desktopCapturer怎么用呢
electron
const { desktopCapturer } = require('electron');
首先吗,你要在你的主进程里引入desktopCapturer,必须要在主进程才行,渲染进程会报错 desktopCapture undefined。然后在主进程中
electron
ipcMain.on('get-sources', async (event) => {
const sources = await desktopCapturer.getSources({ types: ['window', 'screen'] });
event.reply('got-sources', sources); });
然后在渲染进程,也就是你的页面里
electron
<script>
const { ipcRenderer } = require('electron');
const captureBtn = document.getElementById('captureBtn');
const capturedImage = document.getElementById('capturedImage'); captureBtn.addEventListener('click', () => {
ipcRenderer.send('get-sources');
ipcRenderer.on('got-sources', (event, sources) => { // Handle the sources (sources is an array of screen/window information)
console.log(sources); // For example, you can display the first source as an image
capturedImage.innerHTML = `<img src="${sources[0].thumbnail.toDataURL()}" alt="Captured Screen">`; }); });
</script>
那我们就来试试看看,先打印看看sources里有些啥?
找了半天,不对啊,为什么name没有我自己应用的呢,不信邪,打个包再安装实验一下,还是不行,ok,查了一下好像确实获取不到自己应用的流,好吧,那就用整个屏幕好了,再裁剪一下。叮~下载完成,看看截图效果
为什么会这么小,而且很模糊呢,不解,再看看代码,突然thumbnail这个单词映入眼帘,这不是缩略的意思吗,所以只能拿到缩略图吗?不,肯定有其他方法。
第四问:我想用这个获取屏幕的截图不要缩略图怎么弄呢
electron
const { desktopCapturer } = require('electron');
async function getScreenCapture() {
try {
const sources = await desktopCapturer.getSources({ types: ['screen'] });
for (const source of sources) {
if (source.name === 'Entire Screen') {
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: source.id,
},
},
});
const videoTrack = stream.getVideoTracks()[0];
const imageCapture = new ImageCapture(videoTrack);
// 截图并处理结果
const bitmap = await imageCapture.grabFrame();
// 在这里你可以将 bitmap 转换为 Data URL 或者其他格式
console.log(bitmap);
// 释放资源
stream.getTracks().forEach(track => track.stop());
}
}
} catch (error) {
console.error('Error capturing screen:', error);
}
}
// 调用函数获取屏幕截图
getScreenCapture();
看到上面的代码心中还是充满了不安,毕竟navigator.mediaDevices.getDisplayMedia这个就失败了没有权限,但是也没有更好的办法了不是吗,试试!皇天不负苦心人,成功了。终于拿到了完整的截图,也没有报权限问题。后续就是计算要截图的dom距离的x,y坐标和宽高再用canvas转一遍了。自此终于完成啦。
后续
manager看完效果,不错不错。大受鼓舞。臣鞠躬尽瘁。这个代码一定要好好珍藏,万一以后还有类似的需求呢,想了一下哪里能用的到呢,突然惊悚的发现这个截图的实现完全绕过了用户,也就是说如果我想,只要你打开了我的软件,我完全可以不经过你的同意,偷偷的在后台对你的整个屏幕进行抓取。哦吼,感觉好像有点违法,有没有法律大佬解释一下。想起来前段时间听到的风言风语说某量级非常大的chat软件会在后台抓取用户的界面,他的客户端也是electron开发的,不知道用的什么方法🧐