滚动截图,细节调整

This commit is contained in:
zhongluofeng
2026-09-04 19:56:23 +08:00
parent 6b9f71da08
commit 27ad5d89a5
34 changed files with 3766 additions and 559 deletions
+116 -11
View File
@@ -10,13 +10,14 @@
//! - screenshot_show_overlay:一次 IPC 完成覆盖层 show + focus(关键路径减少往返)
//! - screenshot_enum_windows:枚举可见顶层窗口
//! - screenshot_capture_window:按 hwnd 捕获指定窗口
//! - screenshot_set_editor_image / screenshot_get_editor_image:编辑器图片传递
//! - screenshot_take_editor_image_raw:取出编辑器图片(raw IPC,滚动截图会话直接写入)
//! - screenshot_compose_png / screenshot_compose_copyraw RGBA → PNG(仅编码 / 剪贴板+编码)
//! - screenshot_copy_image:写入剪贴板(CF_DIB
//! - screenshot_save_png:写入文件
//! - screenshot_disable_transitions:禁用窗口显示/隐藏过渡动画(消除覆盖层缩放动画)
use super::{CaptureData, CaptureStart, WindowInfo};
use tauri::{Emitter, Manager};
use tauri::{AppHandle, Emitter, Manager};
/// 禁用指定窗口(按 label 查找)的显示/隐藏过渡动画,消除覆盖层出现/消失时的缩放动画
#[tauri::command]
@@ -297,19 +298,90 @@ pub async fn screenshot_capture_window(hwnd: isize) -> Result<CaptureData, Strin
}
}
/// 存入编辑器图片(base64 PNG
/// 滚动截图:从窗口当前滚动位置向下拼接到底部,返回超长 PNG
/// `region` 为 Some 时仅在框选区域(屏幕物理坐标)内捕捉,宽 = 选区宽;
/// 为 None 时捕捉整个客户区。结束后会把窗口滚回起始位置,不打扰用户。
#[tauri::command]
#[specta::specta]
pub async fn screenshot_set_editor_image(png_base64: String) -> Result<(), String> {
super::set_editor_image(png_base64);
Ok(())
pub async fn screenshot_scroll_capture(
hwnd: isize,
region: Option<super::ScrollRegion>,
) -> Result<CaptureData, String> {
#[cfg(windows)]
{
tauri::async_runtime::spawn_blocking(move || {
super::scroll_capture::scroll_capture(hwnd, region)
})
.await
.map_err(|e| format!("滚动截图任务失败: {}", e))?
}
#[cfg(not(windows))]
{
let _ = hwnd;
let _ = region;
Err("截图仅支持 Windows".into())
}
}
/// 取出编辑器图片(编辑器窗口加载时调用,取出即清除)
/// 启动滚动截图会话(后台线程持续捕捉拼接,实时推进度事件)。
/// `auto = true` 为自动滚动(线程主动下滚拼到底部);`false` 为手动(等用户滚动窗口)。
#[tauri::command]
#[specta::specta]
pub async fn screenshot_get_editor_image() -> Result<Option<String>, String> {
Ok(super::take_editor_image())
pub fn screenshot_scroll_start(
app: AppHandle,
hwnd: isize,
region: Option<super::ScrollRegion>,
auto: bool,
) -> Result<(), String> {
#[cfg(windows)]
{
super::scroll_session::start(app, hwnd, region, auto)
}
#[cfg(not(windows))]
{
let _ = (app, hwnd, region, auto);
Err("截图仅支持 Windows".into())
}
}
/// 结束滚动截图会话并导出结果。
#[tauri::command]
#[specta::specta]
pub fn screenshot_scroll_finish() -> Result<(), String> {
#[cfg(windows)]
{
super::scroll_session::finish()
}
#[cfg(not(windows))]
{
Err("截图仅支持 Windows".into())
}
}
/// 取消滚动截图会话(不导出)。
#[tauri::command]
#[specta::specta]
pub fn screenshot_scroll_cancel() -> Result<(), String> {
#[cfg(windows)]
{
super::scroll_session::cancel_now()
}
#[cfg(not(windows))]
{
Err("截图仅支持 Windows".into())
}
}
/// 取出编辑器图片(原始 PNG 字节,raw IPC → 前端 ArrayBuffer → Blob URL,取出即清除)
///
/// 长图(滚动截图)可达数十 MBraw IPC 相比 base64 JSON 事件传输省 ~33% 体积,
/// 且避免 JSON 序列化/多次广播。注:返回 ipc::Responsespecta 无法生成,豁免标注。
#[tauri::command]
pub async fn screenshot_take_editor_image_raw() -> Result<tauri::ipc::Response, String> {
match super::take_editor_image_raw() {
Some(bytes) => Ok(tauri::ipc::Response::new(bytes)),
None => Err("无待编辑的截图".into()),
}
}
/// 将 PNG base64 写入系统剪贴板(转 CF_DIB)
@@ -339,6 +411,39 @@ pub async fn screenshot_copy_image(png_base64: String) -> Result<(), String> {
#[tauri::command]
pub async fn screenshot_compose_copy(
request: tauri::ipc::Request<'_>,
) -> Result<super::CaptureData, String> {
#[cfg(windows)]
{
let body = match request.body() {
tauri::ipc::InvokeBody::Raw(data) => data.clone(),
_ => return Err("需要 raw bodyArrayBuffer".into()),
};
if body.len() < 8 {
return Err("数据不足:缺少尺寸头".into());
}
let width = i32::from_le_bytes([body[0], body[1], body[2], body[3]]);
let height = i32::from_le_bytes([body[4], body[5], body[6], body[7]]);
let rgba = body[8..].to_vec();
tauri::async_runtime::spawn_blocking(move || {
super::capture::compose_copy_rgba(&rgba, width, height)
})
.await
.map_err(|e| format!("合成复制任务失败: {}", e))?
}
#[cfg(not(windows))]
{
let _ = request;
Err("截图仅支持 Windows".into())
}
}
/// 有标注导出(仅编码):接收 raw RGBA(前端 canvas.getImageData 直传),一次完成 PNG 编码。
/// 与 screenshot_compose_copy 的区别:不写剪贴板(编辑器「保存到文件」用)。
/// body 格式:前 8 字节 = width(i32 LE) + height(i32 LE),之后为 raw RGBA 像素。
/// 注:参数为 tauri::ipc::Request(原始 body),specta 无法生成,豁免标注。
#[tauri::command]
pub async fn screenshot_compose_png(
request: tauri::ipc::Request<'_>,
) -> Result<super::CaptureData, String> {
#[cfg(windows)]
{
@@ -353,10 +458,10 @@ pub async fn screenshot_compose_copy(
let height = i32::from_le_bytes([body[4], body[5], body[6], body[7]]);
let rgba = body[8..].to_vec();
tauri::async_runtime::spawn_blocking(move || {
super::capture::compose_copy_rgba(&rgba, width, height)
super::capture::compose_png_rgba(&rgba, width, height)
})
.await
.map_err(|e| format!("合成复制任务失败: {}", e))?
.map_err(|e| format!("合成编码任务失败: {}", e))?
}
#[cfg(not(windows))]
{