货代可以从哪些网站开发客户,建设项目查询官网,手机网站开发外文文献,如何制作微视频宣传片Electron 应用实现截图并编辑功能
Electron 应用如何实现截屏功能#xff0c;有两种思路#xff0c;作为一个框架是否可以通过框架实现截屏#xff0c;另一种就是 javaScript 结合 html 中画布功能实现截屏。 在初步思考之后#xff0c;本文优先探索使用 Electron 实现截屏…Electron 应用实现截图并编辑功能
Electron 应用如何实现截屏功能有两种思路作为一个框架是否可以通过框架实现截屏另一种就是 javaScript 结合 html 中画布功能实现截屏。 在初步思考之后本文优先探索使用 Electron 实现截屏功能。作为一个成熟的框架如果能够完成截屏那自然是已经考虑了各种会出现的问题。 Electron 想要截屏还是要用到 desktopCapturer API。这个 API 也是用来实现录屏。 首先创建一个项目直接 clone angular-electron。
环境
Angular13.3.1Electron18.0.1ngx-img-cropper11.0.0
流程
1.渲染进程向主进程取截屏的数据。 2.主进程获取截屏数据并返回。 3.渲染进程取到数据后将数据转为图片显示在页面上。 4.页面编辑图片并获取新的图片数据保存到本地。
首先在 home.component.ts 中绑定一个点击事件向主进程发送一个消息取得录屏的初始数据
async getScreensht() {let data await this.electron.ipcRenderer.invoke(get-screenshot);
}在主进程 main.ts 中首先获取当前屏幕可能存在多个屏幕再取得当前屏幕的截屏数据:
先看取得截屏数据的方法
let sources await desktopCapturer.getSources({ types: [screen], thumbnailSize: thumbSize });结果如下只有一个屏幕数据如果有两个屏幕则有两条数据依次类推 [{name: Entire Screen,id: screen:0:0,thumbnail: NativeImage {toPNG: [Function: toPNG],toJPEG: [Function: toJPEG],toBitmap: [Function: toBitmap],getBitmap: [Function: getBitmap],toDataURL: [Function: toDataURL],...},display_id: 2528732444,appIcon: null}
]这个结果中有一个参数 display_id代表着对应的屏幕。那么怎么知道截屏哪个屏幕呢需要利用鼠标点击事件鼠标在哪个屏幕点击则截屏哪个屏幕。 鼠标点击位于当前屏幕的窗口方法如下通过 BrowserWindow 找到聚焦的窗口再根据位置判断当前窗口位于哪个屏幕
// 获取当前窗口所在屏幕
function getCurrentScreen() {let focusedWindow BrowserWindow.getFocusedWindow();let currentBounds focusedWindow.getBounds();let currentDisplay screen.getAllDisplays().find((display) {return (currentBounds.x display.bounds.x currentBounds.x display.bounds.x display.bounds.width currentBounds.y display.bounds.y currentBounds.y display.bounds.y display.bounds.height);});return currentDisplay;
}以上方法返回的结果如下可以看到其中的 id 参数与上文中的 display_id 一致。 由此可以从 desktopCapturer.getSources() 返回的多个数据中找到当前点击的屏幕。
{id: 2528732444,bounds: { x: 0, y: 0, width: 1920, height: 1080 },workArea: { x: 0, y: 0, width: 1920, height: 1040 },accelerometerSupport: unknown,...
}遗憾的是在后续的测试中竟然存在部分设备返回 currentDisplay 中的 id 参数为 “”(空字符串)。 这样无法通过 display_id 与 id 的一一对应而确定截取的是哪个屏幕。 为什么会出现这种情况在 github 上 electron 的代码库中有此讨论。 请看这里 desktopCapturer display_id is empty string
根据讨论另一种方法为下
function getCurrentScreen() {let currentBounds win.getBounds();let currentDisplay screen.getDisplayNearestPoint({ x: currentBounds.x, y: currentBounds.y });let allDisplays screen.getAllDisplays();let currentDisplayIndex allDisplays.findIndex((display) {return display.id currentDisplay.id});return { screen_index: currentDisplayIndex };;
}那么梳理一下流程渲染进程响应一个点击事件向主进程发送一个消息获取当前屏幕的截屏数据
// 渲染进程
let data await this.electron.ipcRenderer.invoke(get-screenshot);// 主进程
ipcMain.handle(get-screenshot, async (e, args) {let current_screen getCurrentScreen(); // 取得当前屏幕let primaryDisplay screen.getPrimaryDisplay();// 这里的 primaryDisplay.size 由于缩放的原因可能与系统设置的分辨率不一样, 再乘上缩放比 scaleFactorlet reality_width primaryDisplay.size.width * primaryDisplay.scaleFactor;let reality_height primaryDisplay.size.height * primaryDisplay.scaleFactor;let thumbSize { width: reality_width, height: reality_height };let source await getDesktopCapturer(current_screen, thumbSize); // 取得当前屏幕截屏数据if (source) {return source;}
});async function getDesktopCapturer(current_screen, thumbSize) {let screenName current_screen[screen_index] 1;let screen_names [];screen_names.push(Screen screenName); // 中文为 screen_names.push(屏幕 screenName);screen_names.push(Entire Screen); // 中文为 screen_names.push(整个屏幕);// 以 thumbSize 屏幕分辨率取得所有屏幕截屏数据如果 types 设置为 [screen window] 同时可以获取各个窗口的截屏数据let sources await desktopCapturer.getSources({ types: [screen], thumbnailSize: thumbSize });// 如果只有一个屏幕则 name 为整个屏幕如果有两个及以上屏幕则 name 为 屏幕 1 和 屏幕 2if (sources) {for (let source of sources) {if (screen_names.indexOf(source.name) ! -1) { // 通过 name 确定屏幕return source;}}}
}渲染进程中取到的截屏数据如下
{name: Entire Screen,id: screen:0:0,thumbnail: NativeImage {toPNG: [Function: toPNG],toJPEG: [Function: toJPEG],toBitmap: [Function: toBitmap],getBitmap: [Function: getBitmap],toDataURL: [Function: toDataURL],...},display_id: 2528732444,appIcon: null
}thumbnail 为一个对象通过其中的 toPNG、toJPG、toDataURL 等方法可以将数据转为 PNG、JPG 等格式。 例如以下转为 dataURL即 base64 编码格式以便在 web 中显示在 img 标签中
let data await this.electron.ipcRenderer.invoke(get-screenshot);
let image_url data.thumbnail.toDataURL();又或者在主进程中先转为 PNG 格式 let png_data data.thumbnail.toPNG(); 再使用 fs 模块直接保存到本地 fs.writeFileSync(D:\\1.png, png_data);。
在渲染进程中得到了截屏数据然后就是显示和编辑。
这里选取 ngx-img-cropper 插件。安装 npm i ngx-img-cropper11.0.0 --save由于本项目使用 Angular13.3.1 所以使用 v11.0.0 版本。 ngx-img-cropper 教程。
在 module.ts 中导入 import { ImageCropperModule } from ngx-img-cropper;。
然后根据教程中 Customizing Image cropper 一节内容这里做如下修改
home.conponent.html 文件内容如下去掉多余的选择文件和预览显示留下编辑部分再加上三个 button用于获取截屏清除截屏和保存结果。
div classcontainerdiv styledisplay: flex;button (click)getScreensht()get/buttonbutton (click)clear()clear/buttonbutton (click)save()save/button/divimg-cropper #cropper [image]image_data [settings]cropperSettings/img-cropper
/divhome.component.ts 文件修改如下首先修改 constructor 中的内容
this.cropperSettings new CropperSettings();
this.cropperSettings.preserveSize true; // 不缩放裁剪图像 以裁剪大小保存
this.cropperSettings.keepAspect false; // 不保持裁剪图片纵横比
this.cropperSettings.noFileInput true; // 不要 input 标签
this.cropperSettings.cropperDrawSettings.strokeWidth 2; // 选择框边框宽度
this.cropperSettings.cropperDrawSettings.strokeColor #1296db; // 选择框边框颜色
this.cropperSettings.cropperDrawSettings.fillColor #fff; // 角选择块颜色
this.cropperSettings.markerSizeMultiplier 1; // 角选择块大小
this.cropperSettings.canvasWidth 960; // 画布宽
this.cropperSettings.canvasHeight 540;
this.cropperSettings.width 960; // 初始选择框的宽
this.cropperSettings.height 540;
this.data { image: };以上配置参数与页面样式或保存图片相关添加了部分注释点击 get button 对应的代码如下首先是向主进程取得数据转换后赋值。
async getScreensht() {let data await this.electron.ipcRenderer.invoke(get-screenshot);let image_url data.thumbnail.toDataURL();this.data[image] image_url;let image: any new Image();image.src image_url;this.cropper.setImage(image);
}此时页面如下图显示 这时拖动四个角可以选择截图区域拖动中间图标可以移动选择截取的区域点击 clear 清除页面。
clear() {this.cropper.reset();
}点击 save button则会将图片保存保存图片方法如下首先是取得截取的数据再发送到主进程并重置页面。
save() {let base64Data this.data[image];if (base64Data) {this.electron.ipcRenderer.send(save-screenshot, {data: base64Data});this.clear();}
}主进程接收到数据后处理数据去除 base64 文件编码信息部分再通过 fs.writeFileSync() 方法保存本地。
ipcMain.on(save-screenshot, (e, args) {let temp_file C:\\temp\\test.png; // 文件路径let base64Data args[data].replace(/^data:image\/png;base64,/, );let imageBuffer Buffer.from(base64Data, base64);fs.writeFileSync(temp_file, imageBuffer);
});到此即可将截屏数据显示再页面上编辑后保存到本地。不过 ngx-img-cropper 这个插件的功能较少暂时只能编辑大小。 CropperSettings 还有一些其他的参数可以看 ngx-img-cropper 教程centerTouchRadius 可以设置拖动图标的范围默认是图标所在区域的一小部分。 一些问题如果编辑图片的窗口是动态的则 this.cropperSettings.canvasWidth 960; 这些设置宽高的参数可以在 ngOnInit() 初始化中取得参数后设置。 当前截图类似与 QQ 聊天窗口中的屏幕截图按钮会将主窗口一同截取。如果想实现 QQ 截图快捷键的操作不截取聊天窗口本项目是主窗口 一种办法是在通过 desktopCapturer.getSources() 取得屏幕资源数据前最小化minimize 方法主窗口。并在资源数据返回到渲染进程时再显示show 方法主窗口。 需要注意要先判断主窗口最小化再取数据因为 minimize 需要等待时间才能获取数据。