深度画布

This commit is contained in:
lzp
2026-03-24 11:49:53 +08:00
parent ef13f815f9
commit a7812a250e
15 changed files with 95 additions and 22 deletions

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,3 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.157122 13.4725L0.526449 10.3737C0.550847 10.1699 0.642783 9.98015 0.787096 9.83413L9.86461 0.654119C10.1812 0.334063 10.6118 0.151974 11.062 0.149342C11.5123 0.146814 11.9457 0.324063 12.2659 0.640637L13.8892 2.24581C14.2094 2.56248 14.3915 2.99386 14.394 3.44416C14.3964 3.89432 14.2192 4.32698 13.9027 4.64714L4.8252 13.8272C4.68081 13.9731 4.49215 14.0672 4.28861 14.0938L1.19415 14.4979C1.05466 14.5161 0.912174 14.5022 0.778872 14.4573C0.645754 14.4124 0.525008 14.3374 0.425099 14.2386C0.325191 14.1398 0.248858 14.0199 0.202421 13.8873C0.167635 13.7878 0.14946 13.6829 0.149902 13.578L0.157122 13.4725ZM11.752 5.29496L13.1397 3.89164C13.2557 3.77408 13.3206 3.61539 13.3198 3.45019C13.3189 3.28482 13.2522 3.12618 13.1347 3.00982L11.5104 1.40368C11.3928 1.28748 11.2334 1.22262 11.068 1.22354C10.9028 1.22458 10.7449 1.29127 10.6286 1.40863L9.24097 2.81195L11.752 5.29496ZM1.84678 13.3302L4.09809 13.0363L10.9965 6.05996L8.4845 3.57598L1.58606 10.5523L1.31747 12.8068L1.24565 13.4088L1.84678 13.3302Z" fill="#0D0D0D" stroke="black" stroke-width="0.3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.26 0.555138L15.4449 4.74003C15.6209 4.91603 15.7605 5.12497 15.8557 5.35492C15.951 5.58488 16 5.83134 16 6.08024C16 6.32914 15.951 6.5756 15.8557 6.80556C15.7605 7.03551 15.6209 7.24445 15.4449 7.42045L8.13225 14.7322L13.2682 14.7331C13.421 14.7329 13.5687 14.7881 13.6839 14.8884C13.7992 14.9888 13.8741 15.1275 13.8949 15.2789L13.9 15.3648C13.9 15.5175 13.8447 15.665 13.7443 15.7801C13.644 15.8951 13.5054 15.97 13.3541 15.9907L13.2682 15.9966L6.18385 15.9975C5.91791 16.0124 5.65181 15.9709 5.403 15.8758C5.15418 15.7807 4.92826 15.6342 4.74003 15.4457L0.555138 11.26C0.379138 11.084 0.239526 10.875 0.144276 10.6451C0.0490248 10.4151 0 10.1687 0 9.91976C0 9.67086 0.0490248 9.4244 0.144276 9.19444C0.239526 8.96449 0.379138 8.75555 0.555138 8.57955L8.57955 0.555138C8.75555 0.379138 8.96449 0.239527 9.19444 0.144276C9.4244 0.049025 9.67086 0 9.91976 0C10.1687 0 10.4151 0.049025 10.6451 0.144276C10.875 0.239527 11.084 0.379138 11.26 0.555138ZM2.70065 8.21986L1.44805 9.4733C1.32974 9.59176 1.26328 9.75234 1.26328 9.91976C1.26328 10.0872 1.32974 10.2478 1.44805 10.3662L5.63378 14.5519C5.75677 14.6749 5.9185 14.7373 6.08024 14.7373L6.10467 14.7331L6.13668 14.7347C6.28414 14.7216 6.42224 14.6569 6.5267 14.5519L7.7793 13.2993L2.69981 8.21986H2.70065ZM9.4733 1.44805L3.59356 7.3261L8.67305 12.4056L14.552 6.52754C14.6107 6.46887 14.6572 6.3992 14.689 6.32253C14.7208 6.24585 14.7371 6.16366 14.7371 6.08066C14.7371 5.99766 14.7208 5.91547 14.689 5.8388C14.6572 5.76212 14.6107 5.69245 14.552 5.63378L10.3662 1.44805C10.2478 1.32974 10.0872 1.26328 9.91976 1.26328C9.75234 1.26328 9.59176 1.32974 9.4733 1.44805Z" fill="#212121"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 962 B

After

Width:  |  Height:  |  Size: 962 B

View File

@@ -1,23 +1,25 @@
<template>
<transition name="fade">
<div v-if="show" class="ai-selectbox-panel">
<div>
<span class="icon"><svg-icon name="dc-add" size="16" /></span>
<span class="label">Add</span>
</div>
<div>
<span class="icon"><svg-icon name="dc-remove" size="16" /></span>
<span class="label">Remove</span>
<div
v-for="item in list"
:key="item.type"
:class="{ active: item.name === props.currentTool }"
>
<span class="icon"><svg-icon :name="item.name" size="16" /></span>
<span class="label">{{ item.label }}</span>
</div>
<button>创建</button>
</div>
</transition>
<brush-control-panel :currentTool="show ? 'draw' : ''" style="top: 14rem" />
</template>
<script setup lang="ts">
import { ref, inject, computed, watch } from 'vue'
import depthSlider from './tools/depth-slider.vue'
import { OperationType } from '../tools/layerHelper'
import { OperationType, AI_SELECTBOX_TYPE } from '../tools/layerHelper'
const props = defineProps({
currentTool: { required: true, type: [String, null] }
})
@@ -25,6 +27,28 @@
const toolManager = inject('toolManager') as any
const showTools = [OperationType.SELECTBOX]
const show = computed(() => showTools.includes(props.currentTool))
const list = ref([
{
type: AI_SELECTBOX_TYPE.ADD,
name: 'dc-add_sb',
label: 'Add'
},
{
type: AI_SELECTBOX_TYPE.REMOVE,
name: 'dc-remove_sb',
label: 'Remove'
},
{
type: AI_SELECTBOX_TYPE.DRAW,
name: 'dc-brush_sb',
label: 'Brush'
},
{
type: AI_SELECTBOX_TYPE.ERASER,
name: 'dc-erase_sb',
label: 'Erase'
}
])
</script>
<style lang="less" scoped>
// 淡入淡出动画

View File

@@ -46,6 +46,10 @@
watch(brushState, (value) => {
if (value) updateBrushState()
})
watch(
() => props.currentTool,
(value) => updateBrushState()
)
const brushSize = ref(40)
const brushOpacity = ref(100)
const brushColor = ref('#000000')

View File

@@ -1,5 +1,5 @@
import { fabric } from 'fabric-with-all'
import { OperationType } from '../tools/layerHelper'
import { OperationType, AI_SELECTBOX_TYPE } from '../tools/layerHelper'
import { getObjectAlphaToCanvas, traceImageContour } from '../tools/canvasMethod'
/** 智能框选工具管理器 */
@@ -20,6 +20,15 @@ export class AISelectboxToolManager {
this.layerManager = options.layerManager
this.toolManager = options.toolManager
}
/** 处理切换工具 */
handleToolChange(oldTool: string, newTool: string) {
if (newTool === OperationType.SELECTBOX) {
// 切换到智能框选工具
} else {
// 切换到普通框选工具
}
}
mouseDownEvent(e) {
this.isDragging = true
this.startX = e.absolutePointer.x
@@ -65,7 +74,7 @@ export class AISelectboxToolManager {
this.canvasManager.canvas.remove(this.demoObject)
this.canvasManager.canvas.renderAll()
this.createSelectbox()
// this.createSelectbox()
}

View File

@@ -142,9 +142,9 @@ export class CanvasManager {
this.resetZoom(false, true)// 画布居中
this.stateManager.toolManager.setTool(OperationType.SELECT)
this.layerManager.updateLayers()
this.stateManager.recordState()
// this.stateManager.toolManager.setTool(OperationType.RECTANGLE)
}
/** 画布添加对象 */
async add(obj: any, isRecord = true) {
@@ -293,7 +293,7 @@ export class CanvasManager {
async handleDrawImage(fabricImage: fabric.Object) {
const activeID = this.stateManager.layerManager.activeID.value
const activeLayer = this.getObjectById(activeID)
if (activeLayer) {
if (activeLayer && activeLayer.fill?.repeat !== "repeat") {
this.layerManager.imageMergeToLayer(activeLayer, fabricImage)
} else {
const emptyLayer = await this.layerManager.createEmptyLayer(false);

View File

@@ -2,7 +2,7 @@ import { ref } from 'vue'
import { fabric } from 'fabric-with-all'
import { createId } from '../../tools/tools'
import { exportObjectsToImage, exportObjectToThumbnail } from '../tools/exportMethod'
import { OperationType } from '../tools/layerHelper'
import { OperationType, BlendMode } from '../tools/layerHelper'
import { getArrowPath, cloneObjects, getStarArr } from '../tools/canvasMethod'
export class LayerManager {
@@ -18,7 +18,12 @@ export class LayerManager {
}
onMounted() { }
setActiveID(id: string, isActive = true) {
this.activeID.value = id
const layer = this.getLayerById(id)
if (layer?.type === "group") {
this.activeID.value = ""
} else {
this.activeID.value = id
}
if (isActive) {
this.canvasManager.setActiveObjectById(id)
this.stateManager.toolManager.setTool(OperationType.SELECT)
@@ -399,9 +404,15 @@ export class LayerManager {
})
const index = this.canvasManager.getObjects().indexOf(targetLayer);
this.deleteLayerById(targetLayer.info.id, false)
this.setActiveID(mergedImage.info.id, false)
const nid = mergedImage.info.id
await this.canvasManager.add(mergedImage, false);
this.setActiveID(nid, false)
this.canvasManager.canvas.moveTo(mergedImage, index);
// this.stateManager.objectManager.setBlendMode(nid, BlendMode.MULTIPLY)
// this.stateManager.objectManager.setFillRepeat(nid, false)
this.canvasManager.renderAll()
this.updateLayers()
this.stateManager.recordState()

View File

@@ -87,7 +87,7 @@ export class ObjectManager {
}
/** 设置平铺状态 */
setFillRepeat(id: string) {
setFillRepeat(id: string, isRecord = true) {
const object = this.canvasManager.getObjectById(id)
if (!object) return console.warn('设置平铺状态失败对象不存在ID:', id)
if (object.type !== 'image') return console.warn('设置平铺状态失败,对象不是图片类型:', id)
@@ -133,7 +133,7 @@ export class ObjectManager {
});
rect.set("fill", pattern)
this.canvasManager.canvas.remove(object)
this.canvasManager.add(rect)
this.canvasManager.add(rect, isRecord)
}
/** 获取填充对象 */
getFillRepeatObject(id: string) {

View File

@@ -38,6 +38,7 @@ export class StateManager {
brushManager: any
keyEventManager: any
objectManager: any
aiSelectboxToolManager: any
// 设置管理器
setManager(options) {
options.eventManager && (this.eventManager = options.eventManager)
@@ -47,6 +48,7 @@ export class StateManager {
options.brushManager && (this.brushManager = options.brushManager)
options.keyEventManager && (this.keyEventManager = options.keyEventManager)
options.objectManager && (this.objectManager = options.objectManager)
options.aiSelectboxToolManager && (this.aiSelectboxToolManager = options.aiSelectboxToolManager)
}
constructor(options) {
this.mxHistory = ref(50)

View File

@@ -101,6 +101,7 @@ export class ToolManager {
setTool(value: string) {
const tool = this.tools.find((t) => t.name === value)
if (!tool) return console.warn(`工具${tool}不存在`)
const oldTool = this.currentTool.value
this.currentTool.value = tool.name
this.canvasManager.canvas.defaultCursor = tool.cursor
this.setCanvasEvented(!!tool.selection)
@@ -110,6 +111,7 @@ export class ToolManager {
if (tool.setup) tool.setup()
this.stateManager?.aiSelectboxToolManager?.handleToolChange?.(oldTool, tool.name)
setTimeout(() => {
this.canvasManager.renderAll()
});
@@ -142,13 +144,16 @@ export class ToolManager {
const brushStore = this.brushManager?.brushStore
if (brushStore) {
// 同步基本属性
this.brushManager.setBrushSize(brushStore.state.size);
this.brushManager.setBrushColor(brushStore.state.color);
this.brushManager.setBrushOpacity(brushStore.state.opacity);
// this.brushManager.setBrushSize(brushStore.state.size);
// this.brushManager.setBrushColor(brushStore.state.color);
// this.brushManager.setBrushOpacity(brushStore.state.opacity);
// 同步笔刷类型 - 修复方法名使用正确的setBrushType方法
this.brushManager.setBrushType("pencil");
}
this.brushManager.setBrushSize(5);
this.brushManager.setBrushColor("#000");
this.brushManager.setBrushOpacity(1);
// 更新应用到画布
this.brushManager.updateBrush();
@@ -168,6 +173,7 @@ export class ToolManager {
this.brushManager.createEraser();
}
this.brushManager.setBrushSize(5);
this.stateManager.layerManager.setActiveObjectErasable()
// 启用笔刷指示器
this._enableBrushIndicator();

View File

@@ -34,6 +34,7 @@ export class CanvasEventManager {
}
this.shapeToolManager = new ShapeToolManager(managers)
this.aiSelectboxToolManager = new AISelectboxToolManager(managers)
this.stateManager.setManager({ aiSelectboxToolManager: this.aiSelectboxToolManager })
// 初始化所有事件
this.initEvents();

View File

@@ -68,3 +68,11 @@ export const BlendMode = {
DESTINATION_IN: "destination-in", // 目标内
DESTINATION_OUT: "destination-out", // 目标外
};
/** 智能框选工具类型枚举 */
export const AI_SELECTBOX_TYPE = {
ADD: "add", // 添加模式
REMOVE: "remove", // 删除模式
DRAW: "draw", // 绘画模式
ERASER: "eraser", // 橡皮擦模式
}

View File

@@ -44,7 +44,7 @@
<span class="label" v-show="!item.edit">{{ item.name }}</span>
<el-popover
placement="right"
trigger="click"
trigger="contextmenu"
width="10rem"
popper-style="
padding: .6rem 0.7rem;
@@ -54,7 +54,9 @@
v-model:visible="item.visible"
>
<template #reference>
<span @click.stop class="icon"><svg-icon name="more" size="16" /></span>
<span @click.stop="item.visible = !item.visible" class="icon">
<svg-icon name="more" size="16" />
</span>
</template>
<div class="history-item-menu">
<div class="rename" @click="onRenameHistoryItem(item)">