优化,贴图

This commit is contained in:
zhongluofeng
2026-08-07 18:09:13 +08:00
parent e09b0567d6
commit d09599fa95
40 changed files with 1627 additions and 493 deletions
+129 -69
View File
@@ -11,6 +11,7 @@ import {
} from '@lucide/vue'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Slider } from '@/components/ui/slider'
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
import {
TOOLS, COLORS, BLOCK_SIZES, ALPHAS, HANDLES, HANDLE_HIT, DRAG_THRESHOLD,
type Phase, type ToolType, type Annotation, type DrawableAnnotation,
@@ -1779,7 +1780,16 @@ async function finish() {
}
}
if (!out) return
await emit(EVENTS.screenshotExported, { pngBase64: out.b64, width: out.w, height: out.h })
// 附带框选区域的屏幕逻辑坐标(DIP):窗口逻辑位置 + 选区图像坐标。
// 截图图像按虚拟屏 DIP 捕获,坐标按 DIP 传递,贴图窗口按 DIP 布局即可 1:1 还原
const spPos = selPhys.value
await emit(EVENTS.screenshotExported, {
pngBase64: out.b64,
width: out.w,
height: out.h,
posX: Math.round(winOuterX / dpr + spPos.x),
posY: Math.round(winOuterY / dpr + spPos.y),
})
} catch (e) {
console.error('[screenshot] 完成失败', e)
} finally {
@@ -1801,7 +1811,14 @@ async function doSave() {
})
if (!path) return
await commands.screenshotSavePng(out.b64, path)
await emit(EVENTS.screenshotExported, { pngBase64: out.b64, width: out.w, height: out.h })
const spPos = selPhys.value
await emit(EVENTS.screenshotExported, {
pngBase64: out.b64,
width: out.w,
height: out.h,
posX: Math.round(winOuterX / dpr + spPos.x),
posY: Math.round(winOuterY / dpr + spPos.y),
})
await win.hide().catch(() => {})
} catch (e) {
console.error('[screenshot] 保存失败', e)
@@ -1985,6 +2002,7 @@ onUnmounted(() => {
@mousemove="onMouseMove"
@mouseup="onMouseUp"
>
<TooltipProvider>
<!-- 冻结的屏幕底图 -->
<img
v-if="imgSrc"
@@ -2094,31 +2112,37 @@ onUnmounted(() => {
<div v-if="showToolbar" ref="toolbarRef" class="toolbar" :style="toolbarPos" @mousedown.stop>
<!-- 标注工具 -->
<div class="tb-group">
<button
v-for="t in TOOLS"
:key="t.value"
class="tb-icon"
:class="{ active: phase === 'editing' && currentTool === t.value }"
:title="t.label"
@click="onSelectTool(t.value)"
>
<component :is="t.icon" class="tb-svg" />
</button>
<Tooltip v-for="t in TOOLS" :key="t.value">
<TooltipTrigger as-child>
<button
class="tb-icon"
:class="{ active: phase === 'editing' && currentTool === t.value }"
@click="onSelectTool(t.value)"
>
<component :is="t.icon" class="tb-svg" />
</button>
</TooltipTrigger>
<TooltipContent>{{ t.label }}</TooltipContent>
</Tooltip>
</div>
<div class="tb-sep" />
<!-- 颜色色卡显示当前颜色点击打开选色板 -->
<Popover v-model:open="colorPickerOpen">
<PopoverTrigger as-child>
<button
class="color-badge"
:class="{ disabled: !colorEnabled }"
:style="{ '--swatch-color': effectiveColor }"
:disabled="!colorEnabled"
title="颜色"
/>
</PopoverTrigger>
<Tooltip>
<TooltipTrigger as-child>
<PopoverTrigger as-child>
<button
class="color-badge"
:class="{ disabled: !colorEnabled }"
:style="{ '--swatch-color': effectiveColor }"
:disabled="!colorEnabled"
/>
</PopoverTrigger>
</TooltipTrigger>
<TooltipContent>颜色</TooltipContent>
</Tooltip>
<PopoverContent class="w-auto p-3" align="start">
<div class="color-popover">
<input
@@ -2128,15 +2152,17 @@ onUnmounted(() => {
@input="onCustomColorPick(($event.target as HTMLInputElement).value)"
/>
<div class="color-swatches">
<button
v-for="c in COLORS"
:key="c"
class="color-swatch-mini"
:class="{ active: effectiveColor === c }"
:style="{ backgroundColor: c }"
:title="c"
@click="effectiveColor = c"
/>
<Tooltip v-for="c in COLORS" :key="c">
<TooltipTrigger as-child>
<button
class="color-swatch-mini"
:class="{ active: effectiveColor === c }"
:style="{ backgroundColor: c }"
@click="effectiveColor = c"
/>
</TooltipTrigger>
<TooltipContent>{{ c }}</TooltipContent>
</Tooltip>
</div>
</div>
</PopoverContent>
@@ -2144,14 +2170,18 @@ onUnmounted(() => {
<!-- 粗细 badge点击打开 Slider -->
<Popover v-model:open="lineWidthPickerOpen">
<PopoverTrigger as-child>
<button
class="width-badge"
:class="{ disabled: !lineWidthEnabled }"
:disabled="!lineWidthEnabled"
title="粗细"
>{{ effectiveLineWidth }}</button>
</PopoverTrigger>
<Tooltip>
<TooltipTrigger as-child>
<PopoverTrigger as-child>
<button
class="width-badge"
:class="{ disabled: !lineWidthEnabled }"
:disabled="!lineWidthEnabled"
>{{ effectiveLineWidth }}</button>
</PopoverTrigger>
</TooltipTrigger>
<TooltipContent>粗细</TooltipContent>
</Tooltip>
<PopoverContent class="w-auto p-3" align="start">
<div class="width-popover">
<div class="width-popover-header">
@@ -2174,43 +2204,62 @@ onUnmounted(() => {
<template v-if="currentTool === 'mosaic'">
<div class="tb-sep" />
<div class="tb-group">
<button
v-for="b in BLOCK_SIZES"
:key="b"
class="tb-num"
:class="{ active: blockSize === b }"
title="马赛克块大小"
@click="blockSize = b"
>{{ b }}</button>
<Tooltip v-for="b in BLOCK_SIZES" :key="b">
<TooltipTrigger as-child>
<button
class="tb-num"
:class="{ active: blockSize === b }"
@click="blockSize = b"
>{{ b }}</button>
</TooltipTrigger>
<TooltipContent>马赛克块大小</TooltipContent>
</Tooltip>
</div>
</template>
<template v-if="currentTool === 'highlight'">
<div class="tb-sep" />
<div class="tb-group">
<button
v-for="a in ALPHAS"
:key="a"
class="tb-num"
:class="{ active: highlightAlpha === a }"
title="高亮透明度"
@click="highlightAlpha = a"
>{{ Math.round(a * 100) }}%</button>
<Tooltip v-for="a in ALPHAS" :key="a">
<TooltipTrigger as-child>
<button
class="tb-num"
:class="{ active: highlightAlpha === a }"
@click="highlightAlpha = a"
>{{ Math.round(a * 100) }}%</button>
</TooltipTrigger>
<TooltipContent>高亮透明度</TooltipContent>
</Tooltip>
</div>
</template>
<div class="tb-sep" />
<div class="tb-group">
<button class="tb-icon" :disabled="!canUndo" title="撤销" @click="undo">
<Undo2 class="tb-svg" />
</button>
<button class="tb-icon" :disabled="!canRedo" title="重做" @click="redo">
<Redo2 class="tb-svg" />
</button>
<button class="tb-icon" :disabled="!canUndo" title="清空标注" @click="clearAnnotations">
<Eraser class="tb-svg" />
</button>
<Tooltip>
<TooltipTrigger as-child>
<button class="tb-icon" :disabled="!canUndo" @click="undo">
<Undo2 class="tb-svg" />
</button>
</TooltipTrigger>
<TooltipContent>撤销</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger as-child>
<button class="tb-icon" :disabled="!canRedo" @click="redo">
<Redo2 class="tb-svg" />
</button>
</TooltipTrigger>
<TooltipContent>重做</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger as-child>
<button class="tb-icon" :disabled="!canUndo" @click="clearAnnotations">
<Eraser class="tb-svg" />
</button>
</TooltipTrigger>
<TooltipContent>清空标注</TooltipContent>
</Tooltip>
</div>
<div class="tb-sep" />
@@ -2221,12 +2270,22 @@ onUnmounted(() => {
<!-- 复制 / 保存(置末) -->
<div class="tb-group">
<button class="tb-icon" title="保存到文件" @click="doSave">
<Save class="tb-svg" />
</button>
<button class="tb-icon primary" title="复制到剪贴板 (Enter)" @click="finish">
<Copy class="tb-svg" />
</button>
<Tooltip>
<TooltipTrigger as-child>
<button class="tb-icon" @click="doSave">
<Save class="tb-svg" />
</button>
</TooltipTrigger>
<TooltipContent>保存到文件</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger as-child>
<button class="tb-icon primary" @click="finish">
<Copy class="tb-svg" />
</button>
</TooltipTrigger>
<TooltipContent>复制到剪贴板 (Enter)</TooltipContent>
</Tooltip>
</div>
</div>
@@ -2245,6 +2304,7 @@ onUnmounted(() => {
<div class="mag-hint">{{ hint.split(' · ').join('\n') }}</div>
</div>
</div>
</TooltipProvider>
</div>
</template>