屏幕捕获与截图是很多桌面应用(如远程协助、截图工具、录屏软件)的基础功能。Tauri 本身不直接提供截图 API,但可以通过 Rust 调用系统级库或第三方 crate 来实现高性能的屏幕捕获,再将图像数据传递给前端。下面给出两种实用方案:全屏捕获和区域捕获,并说明如何在 Tauri 中落地。
全屏捕获
全屏捕获的核心是获取整个桌面的像素数据,通常通过操作系统原生 API 或跨平台库完成。
推荐方案:使用 scrap crate(跨平台)scrap 是一个轻量的 Rust 屏幕捕获库,支持 Windows、macOS、Linux 的主要显示服务器(X11/Wayland)。它可以直接获取任意显示器的原始像素数据。
步骤示例:
- 添加依赖(
src-tauri/Cargo.toml):
[dependencies]
scrap = "0.5"
image = "0.24" # 用于 JPEG/PNG 编码
base64 = "0.21" # 可选,方便前端展示
- 编写 Rust 命令,捕获屏幕并返回 Base64 图片:
use scrap::{Capturer, Display};
use std::io::ErrorKind::WouldBlock;
use std::time::Duration;
use std::thread;
#[tauri::command]
fn capture_full_screen() -> Result<String, String> {
let display = Display::primary().map_err(|e| e.to_string())?;
let mut capturer = Capturer::new(display).map_err(|e| e.to_string())?;
let (w, h) = (display.width(), display.height());
// 等待一帧有效数据(部分后端需要预热)
loop {
match capturer.frame() {
Ok(frame) => {
// 将 BGRA 数据转为 RGB,然后用 image crate 编码为 PNG
let mut img = image::RgbaImage::new(w as u32, h as u32);
// 注意 scrap 默认返回 BGRA 格式
img.copy_from_slice(frame);
let mut buf = Vec::new();
let mut encoder = image::codecs::png::PngEncoder::new(&mut buf);
encoder.encode(&img, w as u32, h as u32, image::ExtendedColorType::Rgba8)
.map_err(|e| e.to_string())?;
let b64 = base64::encode(&buf);
return Ok(format!("data:image/png;base64,{}", b64));
}
Err(ref e) if e.kind() == WouldBlock => {
thread::sleep(Duration::from_millis(10));
}
Err(e) => return Err(e.to_string()),
}
}
}
- 前端调用(如 React):
const { invoke } = window.__TAURI__;
const base64 = await invoke('capture_full_screen');
document.getElementById('preview').src = base64;
注意事项:
- macOS 需要录屏权限,应在
Info.plist中声明NSDesktopCaptureUsageDescription,并在首次调用时触发系统授权弹窗。 - Linux 上可能需安装
libxcb/libwayland等依赖;Wayland 的支持因发行版而异。 - 性能优化:可以用
scrap的另一模式直接捕获为BGR格式以减少转换开销,或对变化不大的场景做差分编码。
区域捕获
区域捕获通常需要用户交互选择矩形范围,然后只截取该区域的内容。这可以通过两种途径实现:
方案 A:先全屏截取,再用坐标裁剪(最简单)
先调用上面的全屏命令获得整个桌面的图像,前端通过拖拽控件让用户选取区域,然后前端用 canvas 裁剪后再上传或直接使用。
优点:实现快,无需额外 Rust 代码。缺点:每次截取都要传一次全屏图像数据,如果截图频繁(如实时标注)会增加内存和带宽压力。
方案 B:在 Rust 中直接按坐标裁剪(推荐)
扩展上面的命令,增加 x, y, width, height 参数,在 Rust 侧调用 capturer.frame() 拿到全屏原始帧后,使用 image crate 的 crop(或用 sub_image)裁剪,只返回目标区域的 Base64。这样可大幅减少前端数据传输量。
代码片段(核心逻辑):
#[tauri::command]
fn capture_region(x: u32, y: u32, width: u32, height: u32) -> Result<String, String> {
// ... 获取全屏 frame 的代码同上 ...
let frame = capturer.frame().map_err(|e| e.to_string())?;
// 构建全尺寸 RgbaImage
let full_img = image::RgbaImage::from_raw(w as u32, h as u32, frame.to_vec())
.ok_or("Failed to create image")?;
// 裁剪
let cropped = full_img.view(x, y, width, height).to_image();
// 编码为 PNG
let mut buf = Vec::new();
let encoder = image::codecs::png::PngEncoder::new(&mut buf);
encoder.encode(&cropped, width, height, image::ExtendedColorType::Rgba8)
.map_err(|e| e.to_string())?;
Ok(format!("data:image/png;base64,{}", base64::encode(&buf)))
}
前端可以配合 Tauri 的窗口鼠标事件(或前端拖拽选择)来确定 x, y, width, height,然后调用该命令。
实用细节与兼容性
- 性能考虑:
scrap在每次调用frame()时都会返回最新的屏幕数据,没有硬件加速的情况下,高分辨率全屏捕获可能达到 100ms 以上。对于需要高帧率的场景(如录屏),建议使用更底层的库(如 Windows 的 DXGI、macOS 的 AVFoundation 或 ScreenCaptureKit),并通过 Tauri 的 Rust 插件集成。 - 多显示器:
scrap的Display::all()可获取所有显示器,按索引选择或合并。 - 安全权限:Tauri 的安全模型要求前端只能调用你注册的命令。截图命令本身不会泄露不安全数据,但应注意:若应用自动截取屏幕,需在隐私政策中明确说明,避免被系统或应用商店拒绝。
- Tauri 插件:社区有
tauri-plugin-screenshots等插件,但可能更新滞后。自己实现可更灵活控制。
一句话总结:借助 Rust 生态的 scrap 库,你可以在 Tauri 中轻松实现全屏与区域截图,用 Base64 或二进制流将图片传给前端显示或保存,全程体积小、无 Chromium 包袱。