From f4245a5744286297b50bb9da7511b24337bcfa41 Mon Sep 17 00:00:00 2001 From: fofolee Date: Thu, 23 Jan 2025 10:03:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E6=88=AA=E5=9B=BE=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=9A=E6=96=B0=E5=A2=9EDOM=E5=85=83=E7=B4=A0?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=99=A8=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=88=AA=E5=9B=BE=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/lib/quickcomposer/browser/browser.js | 86 ++++++++++++++------- src/js/composer/commands/browserCommands.js | 37 +++++---- 2 files changed, 80 insertions(+), 43 deletions(-) diff --git a/plugin/lib/quickcomposer/browser/browser.js b/plugin/lib/quickcomposer/browser/browser.js index 7ef9784..ac52c18 100644 --- a/plugin/lib/quickcomposer/browser/browser.js +++ b/plugin/lib/quickcomposer/browser/browser.js @@ -202,8 +202,8 @@ const initCDP = async (targetId) => { if (!clients.has(targetId)) { try { const client = await CDP({ target: targetId }); - const { Page, Runtime, Target, Network, Emulation } = client; - await Promise.all([Page.enable(), Runtime.enable()]); + const { Page, Runtime, Target, Network, Emulation, DOM } = client; + await Promise.all([Page.enable(), Runtime.enable(), DOM.enable()]); clients.set(targetId, { client, Page, @@ -211,6 +211,7 @@ const initCDP = async (targetId) => { Target, Network, Emulation, + DOM, }); } catch (err) { console.log(err); @@ -381,42 +382,75 @@ const getCookie = async (tab, name) => { // 捕获标签页截图 const captureScreenshot = async (tab, options = {}) => { const target = await searchTarget(tab); - const { format = "png", quality = 100, fullPage = false, savePath } = options; + const { + format = "png", + quality = 100, + savePath, + selector = null, + } = options; try { - const { Page, Emulation } = await initCDP(target.id); + const { Page, Emulation, DOM } = await initCDP(target.id); + await DOM.enable(); - if (fullPage) { - const metrics = await Page.getLayoutMetrics(); - const width = Math.max( - metrics.contentSize.width, - metrics.layoutViewport.clientWidth, - metrics.visualViewport.clientWidth - ); - const height = Math.max( - metrics.contentSize.height, - metrics.layoutViewport.clientHeight, - metrics.visualViewport.clientHeight - ); - await Emulation.setDeviceMetricsOverride({ - width, - height, - deviceScaleFactor: 1, - mobile: false, + let clip = null; + if (selector) { + // 获取DOM节点 + const { root } = await DOM.getDocument(); + const { nodeId } = await DOM.querySelector({ + nodeId: root.nodeId, + selector: selector, }); + + if (!nodeId) { + throw new Error(`未找到元素: ${selector}`); + } + + // 获取元素的精确四边形坐标 + const { quads } = await DOM.getContentQuads({ nodeId }); + if (!quads || quads.length === 0) { + throw new Error("无法获取元素位置信息"); + } + + // 获取布局指标 + const { visualViewport } = await Page.getLayoutMetrics(); + const { pageX, pageY } = visualViewport; + + // 计算边界框 + const quad = quads[0]; + const x = Math.min(quad[0], quad[2], quad[4], quad[6]); + const y = Math.min(quad[1], quad[3], quad[5], quad[7]); + const width = Math.max(quad[0], quad[2], quad[4], quad[6]) - x; + const height = Math.max(quad[1], quad[3], quad[5], quad[7]) - y; + + clip = { + x: Math.round(x - pageX), + y: Math.round(y - pageY), + width: Math.round(width), + height: Math.round(height), + scale: 1, + }; + + // 确保尺寸不为0 + if (clip.width === 0) clip.width = 1; + if (clip.height === 0) clip.height = 1; } - const { data } = await Page.captureScreenshot({ + const screenshotParams = { format, quality: format === "jpeg" ? quality : undefined, fromSurface: true, - captureBeyondViewport: fullPage, - }); + captureBeyondViewport: !!selector, + }; - if (fullPage) { - await Emulation.clearDeviceMetricsOverride(); + if (clip) { + screenshotParams.clip = clip; } + const { data } = await Page.captureScreenshot(screenshotParams); + + await DOM.disable(); + if (savePath) { fs.writeFileSync(savePath, data, "base64"); } diff --git a/src/js/composer/commands/browserCommands.js b/src/js/composer/commands/browserCommands.js index a97b4ba..cc5f7b1 100644 --- a/src/js/composer/commands/browserCommands.js +++ b/src/js/composer/commands/browserCommands.js @@ -183,6 +183,7 @@ export const browserCommands = { value: "quickcomposer.browser.captureScreenshot", label: "捕获截图", icon: "screenshot", + isAsync: true, config: [ tabConfig, { @@ -191,37 +192,39 @@ export const browserCommands = { icon: "settings", width: 12, options: { + quality: { + label: "质量", + component: "NumberInput", + width: 2, + min: 0, + max: 100, + }, + selector: { + label: "指定元素(CSS选择器)", + component: "VariableInput", + icon: "code", + width: 10, + placeholder: "留空截取可视区域,截取整个页面可填body", + options: { + cssSelector: true, + }, + }, format: { label: "格式", component: "QSelect", - icon: "format", - width: 4, + width: 2, options: [ { label: "PNG", value: "png" }, { label: "JPEG", value: "jpeg" }, { label: "WebP", value: "webp" }, ], }, - quality: { - label: "质量", - component: "NumberInput", - icon: "quality", - width: 4, - min: 0, - max: 100, - }, - fullPage: { - label: "全屏截图", - component: "CheckButton", - icon: "fullscreen", - width: 4, - }, savePath: { label: "保存路径", component: "VariableInput", icon: "folder", placeholder: "留空则不保存", - width: 12, + width: 10, options: { dialog: { type: "save",