深度画布bug

This commit is contained in:
lzp
2026-03-31 13:29:46 +08:00
parent f5efaa8eaf
commit b53d15d3f2
22 changed files with 381 additions and 91 deletions

View File

@@ -1,5 +1,5 @@
# VITE_APP_URL = http://192.168.31.82:8771
VITE_APP_URL = http://18.167.251.121:10015
# VITE_APP_URL = http://192.168.31.118:8080
VITE_APP_URL = http://192.168.31.82:8755
# VITE_APP_URL = http://192.168.31.82:8755
VITE_GOOGLE_CLIENT_ID = 216037134725-7q8vqp0ohtmohlosltkfg7bd2v29rm5a.apps.googleusercontent.com

View File

@@ -103,3 +103,12 @@ body,
border-radius: 0.4rem;
background: rgba(0, 0, 0, 0.2);
}
.mosaic-bg {
--mosaic-bg-size: 1rem;
--mosaic-bg-color1: #efefef;
--mosaic-bg-color2: #fff;
background-image: repeating-conic-gradient(var(--mosaic-bg-color1) 0% 25%, var(--mosaic-bg-color2) 0% 50%);
background-repeat: repeat;
background-position: 50% 50%;
background-size: var(--mosaic-bg-size) var(--mosaic-bg-size);
}

View File

@@ -117,3 +117,13 @@ body,
background: rgba(0, 0, 0, 0.2);
}
}
.mosaic-bg{
--mosaic-bg-size: 1rem;
--mosaic-bg-color1: #efefef;
--mosaic-bg-color2: #fff;
background-image: repeating-conic-gradient(var(--mosaic-bg-color1) 0% 25%, var(--mosaic-bg-color2) 0% 50%);
background-repeat: repeat;
background-position: 50% 50%;
background-size: var(--mosaic-bg-size) var(--mosaic-bg-size);
}

View File

@@ -20,7 +20,7 @@
</button>
</div>
</transition>
<brush-control-panel v-if="show" :currentTool="currentTool2" style="top: 14rem" />
<brush-control-panel v-if="show" :currentTool="currentTool" style="top: 14rem" />
</template>
<script setup lang="ts">
@@ -40,7 +40,6 @@
[OperationType.AISELECT_DRAW]: OperationType.ERASER,
[OperationType.AISELECT_ERASER]: OperationType.ERASER
}
const currentTool2 = computed(() => tool2[props.currentTool] || props.currentTool)
const show = computed(() => stateManager.aiSelectboxToolManager.tools.includes(props.currentTool))
const list = ref([
{

View File

@@ -32,7 +32,7 @@
</template>
<script setup lang="ts">
import { ref, inject, computed, watch } from 'vue'
import { ref, inject, computed, watch, nextTick } from 'vue'
import depthSlider from './tools/depth-slider.vue'
import { OperationType } from '../tools/layerHelper'
const props = defineProps({
@@ -40,20 +40,27 @@
})
const stateManager = inject('stateManager') as any
const brushState = computed(() => stateManager.brushManager.brushStore.state)
const showTools = [OperationType.DRAW, OperationType.ERASER]
const showTools = [
OperationType.DRAW,
OperationType.ERASER,
OperationType.AISELECT_DRAW,
OperationType.AISELECT_ERASER
]
const show = computed(() => showTools.includes(props.currentTool))
const brushSize = ref(40)
const brushOpacity = ref(100)
const brushColor = ref('#000000')
const updateBrushState = () => {
brushSize.value = brushState.value.size
brushOpacity.value = brushState.value.opacity
brushColor.value = brushState.value.color
const updateBrushState = async () => {
await nextTick()
brushSize.value = stateManager.brushManager.brushStore.state.size
brushOpacity.value = stateManager.brushManager.brushStore.state.opacity
brushColor.value = stateManager.brushManager.brushStore.state.color
}
updateBrushState()
watch(() => brushState.value.size, updateBrushState)
watch(() => brushState.value.opacity, updateBrushState)
watch(() => brushState.value.color, updateBrushState)
watch(() => props.currentTool, updateBrushState)
const onInputSize = (value: number) => {
stateManager.brushManager.setBrushSize(value)

View File

@@ -68,12 +68,25 @@
</div>
</div>
</div>
<div class="select">
<div class="title">{{ $t('DepthCanvas.blendMode') }}</div>
<div class="content">
<depth-select
v-model="data.globalCompositeOperation"
:list="layerCompositeOptions"
@change="onChange"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, inject, computed, nextTick, onBeforeUnmount, reactive, watch } from 'vue'
import DepthInput from '../tools/depth-input.vue'
import DepthSelect from '../tools/depth-select.vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const objectManager = inject('objectManager') as any
const stateManager = inject('stateManager') as any
const props = defineProps({
@@ -90,7 +103,8 @@
width: 0,
height: 0,
scaleX: 1,
scaleY: 1
scaleY: 1,
globalCompositeOperation: 'source-over'
})
const updateData = async () => {
await nextTick()
@@ -100,6 +114,7 @@
data.height = Math.round(props.object.height)
data.scaleX = Math.round(props.object.scaleX * 100)
data.scaleY = Math.round(props.object.scaleY * 100)
data.globalCompositeOperation = props.object.globalCompositeOperation
}
updateData()
watch(() => props.object, updateData)
@@ -121,6 +136,94 @@
stateManager.event.remove('canvas:undo', updateData)
stateManager.event.remove('canvas:redo', updateData)
})
const layerCompositeOptions = ref([
{
value: 'source-over',
label: t('DepthCanvas.compositeNormal'),
tip: t('DepthCanvas.compositeNormalTip')
}, // 正常
{
value: 'darken',
label: t('DepthCanvas.compositeDarken'),
tip: t('DepthCanvas.compositeDarkenTip')
}, // 变暗
{
value: 'multiply',
label: t('DepthCanvas.compositeMultiply'),
tip: t('DepthCanvas.compositeMultiplyTip')
}, // 正片叠底
{
value: 'color-burn',
label: t('DepthCanvas.compositeColorBurn'),
tip: t('DepthCanvas.compositeColorBurnTip')
}, // 颜色加深
{
value: 'lighten',
label: t('DepthCanvas.compositeLighten'),
tip: t('DepthCanvas.compositeLightenTip')
}, // 颜色减淡
{
value: 'screen',
label: t('DepthCanvas.compositeScreen'),
tip: t('DepthCanvas.compositeScreenTip')
}, // 滤色
{
value: 'color-dodge',
label: t('DepthCanvas.compositeColorDodge'),
tip: t('DepthCanvas.compositeColorDodgeTip')
}, // 颜色减淡
{
value: 'lighter',
label: t('DepthCanvas.compositeLighter'),
tip: t('DepthCanvas.compositeLighterTip')
}, // 颜色减淡
{
value: 'overlay',
label: t('DepthCanvas.compositeOverlay'),
tip: t('DepthCanvas.compositeOverlayTip')
}, // 叠加
{
value: 'soft-light',
label: t('DepthCanvas.compositeSoftLight'),
tip: t('DepthCanvas.compositeSoftLightTip')
}, // 柔光
{
value: 'hard-light',
label: t('DepthCanvas.compositeHardLight'),
tip: t('DepthCanvas.compositeHardLightTip')
}, // 强光
{
value: 'difference',
label: t('DepthCanvas.compositeDifference'),
tip: t('DepthCanvas.compositeDifferenceTip')
}, // 差值
{
value: 'exclusion',
label: t('DepthCanvas.compositeExclusion'),
tip: t('DepthCanvas.compositeExclusionTip')
}, // 排除
{ value: 'hue', label: t('DepthCanvas.compositeHue'), tip: t('DepthCanvas.compositeHueTip') }, // 色相
{
value: 'saturation',
label: t('DepthCanvas.compositeSaturation'),
tip: t('DepthCanvas.compositeSaturationTip')
}, // 饱和度
{
value: 'color',
label: t('DepthCanvas.compositeColor'),
tip: t('DepthCanvas.compositeColorTip')
}, // 颜色
{
value: 'luminosity',
label: t('DepthCanvas.compositeLuminosity'),
tip: t('DepthCanvas.compositeLuminosityTip')
} // 亮度
])
</script>
<style lang="less" scoped>
@@ -137,9 +240,9 @@
display: flex;
flex-direction: column;
--depth-input-height: 2.4rem;
--depth-input-bg-color: rgba(249, 249, 250, 1);
--depth-input-border-color: rgba(230, 230, 231, 1);
--depth-input-decorate-color: rgba(69, 71, 84, 0.1);
// --depth-input-bg-color: rgba(249, 249, 250, 1);
// --depth-input-border-color: rgba(230, 230, 231, 1);
// --depth-input-decorate-color: rgba(69, 71, 84, 0.1);
&:last-child {
margin-right: 0;
}

View File

@@ -2,7 +2,7 @@
<div class="fill-repeat h">
<div>
<div class="title">{{ $t('DepthCanvas.image') }}</div>
<div class="content">
<div class="content mosaic-bg">
<img :src="object.info.fill.source" alt="" />
</div>
</div>

View File

@@ -138,9 +138,9 @@
display: flex;
flex-direction: column;
--depth-input-height: 2.4rem;
--depth-input-bg-color: rgba(249, 249, 250, 1);
--depth-input-border-color: rgba(230, 230, 231, 1);
--depth-input-decorate-color: rgba(69, 71, 84, 0.1);
// --depth-input-bg-color: rgba(249, 249, 250, 1);
// --depth-input-border-color: rgba(230, 230, 231, 1);
// --depth-input-decorate-color: rgba(69, 71, 84, 0.1);
&:last-child {
margin-right: 0;
}

View File

@@ -2,7 +2,7 @@
<div class="layer-item">
<div class="item" @click="onClickLayer">
<div class="drag"><svg-icon name="dc-drag" size="18" /></div>
<div class="thumb">
<div class="thumb mosaic-bg">
<img v-if="layer.thumbnail" :src="layer.thumbnail" />
</div>
<div class="name">
@@ -16,6 +16,7 @@
:value="layer.info.name"
@blur="onChangeName"
@keyup.enter="onChangeName"
@keydown.stop
/>
</div>
<div class="icons">

View File

@@ -63,6 +63,7 @@
watch(
() => props.modelValue,
(v) => {
console.log(v)
value.value = v
}
)

View File

@@ -1,6 +1,6 @@
import { fabric } from 'fabric-with-all'
import { OperationType } from '../tools/layerHelper'
import { getObjectAlphaToCanvas, traceImageContour, cloneObjects } from '../tools/canvasMethod'
import { getObjectAlphaToCanvas, traceImageContour, clipCanvasTransparent, cloneObjects } from '../tools/canvasMethod'
import { getSegAnythingImage } from '@/api/depth-canvas'
import { useGlobalStore, useUserInfoStore } from '@/stores'
@@ -256,28 +256,46 @@ export class AISelectboxToolManager {
if (!this.demoObject) return
const fobject = this.demoObject
this.clearDemoObject()
const tcanvas = await this.createStaticCanvas(fobject)
const canvas = getObjectAlphaToCanvas(tcanvas, null, 0, { r: 255, g: 0, b: 0, a: 255 });
const arr = traceImageContour(canvas);
const scaleY = fobject.scaleY
const scaleX = fobject.scaleX
const top = fobject.top
const left = fobject.left
let minX = fobject.width;
let minY = fobject.height;
const str = arr.map((v) => {
if (v.x < minX) minX = v.x;
if (v.y < minY) minY = v.y;
return `${v.x} ${v.y}`
}).join(" L ");
const path = new fabric.Path(`M ${str} z`);
const tcanvas = await this.createStaticCanvas(fobject)
const canvas = getObjectAlphaToCanvas(tcanvas, null, 0, { r: 255, g: 255, b: 255, a: 255 });
/** 路径裁剪法 */
// const arr = traceImageContour(canvas);
// const str = arr.map((v) => {
// if (v.x < minX) minX = v.x;
// if (v.y < minY) minY = v.y;
// return `${v.x} ${v.y}`
// }).join(" L ");
// const path = new fabric.Path(`M ${str} z`);
// path.set({
// left: left + minX,
// top: top + minY,
// scaleX: scaleX,
// scaleY: scaleY,
// ...this.selectionStyle,
// });
/** 图片裁剪法 */
const info = clipCanvasTransparent(canvas)
minX = info.minX
minY = info.minY
const path = new fabric.Image(info.canvas)
path.set({
left: left + minX,
top: top + minY,
scaleX: scaleX,
scaleY: scaleY,
...this.selectionStyle,
});
absolutePositioned: true,
})
// 创建分组层
const group = await this.layerManager.createGroupLayer({
clipPath: path,
top: path.top + path.height / 2,

View File

@@ -189,16 +189,9 @@ export class CanvasManager {
const objects = this.getObjects().filter((v: any) => v.type !== "group" && !!v.info?.id);
for (let i = 0; i < objects.length; i++) {
let object = objects[i]
if (object.clipPath) object.set({ clipPath: null })
let group = this.getObjectById(object.info.parentId)
if (!group) continue
let path = group.clipPath
let path = this.getObjectById(object.info.parentId)?.clipPath
if (!path) continue
let clipPath = await cloneObjects([path]).then((v) => v[0])
clipPath.set({
absolutePositioned: true,
})
object.set({ clipPath })
object.set({ clipPath: path })
}
this.renderAll()
}

View File

@@ -32,7 +32,30 @@ export class LayerManager {
}
}
setActiveFirstLayer() {
this.setActiveID(this.layers.value[0]?.info?.id || "")
const layer = this.layers.value[0]
const id = layer.type === "group" ? layer.children[0]?.info?.id : layer.info?.id
this.setActiveID(id || "")
}
// 更新图层列表
async updateLayers(isSort = false) {
const objects = this.canvasManager.getObjects().map(v => v.toObject()).filter(v => !!v.info?.id).reverse()
objects.forEach(v => {
if (v.type === "group") {
if (!v.children) v.children = []
return;
}
const parentId = v.info?.parentId
if (!parentId) return
objects.forEach((obj: any) => {
if (obj.info?.id !== parentId) return
if (!obj.children) obj.children = []
obj.children.push(v)
})
})
const layers = objects.filter(v => !v.info?.parentId)
this.layers.value = layers
if (isSort) await this.sortLayers()
}
getActiveLayer() {
@@ -168,26 +191,6 @@ export class LayerManager {
}
}
// 更新图层列表
async updateLayers(isSort = false) {
const objects = this.canvasManager.getObjects().map(v => v.toObject()).filter(v => !!v.info?.id).reverse()
objects.forEach(v => {
if (v.type === "group") {
if (!v.children) v.children = []
return;
}
const parentId = v.info?.parentId
if (!parentId) return
objects.forEach((obj: any) => {
if (obj.info?.id !== parentId) return
if (!obj.children) obj.children = []
obj.children.push(v)
})
})
const layers = objects.filter(v => !v.info?.parentId)
this.layers.value = layers
if (isSort) await this.sortLayers()
}
/** 设置图层位置-不设置默认居中 */
setLayerPosition(object, options?: any) {
@@ -455,7 +458,7 @@ export class LayerManager {
const objects = this.canvasManager.getObjects()
objects.forEach((item: any) => {
item.set({
erasable: (item.info.id === this.activeID.value && item.type !== "group")
erasable: (item?.info?.id === this.activeID.value && item.type !== "group")
})
})
}

View File

@@ -117,6 +117,7 @@ export class ToolManager {
setTool(value: string) {
const tool = this.tools.find((t) => t.name === value)
if (!tool) return console.warn(`工具${tool}不存在`)
this.brushManager?.handleToolChange(tool.name)
const oldTool = this.currentTool.value
this.currentTool.value = tool.name
this.canvasManager.canvas.defaultCursor = tool.cursor
@@ -124,7 +125,6 @@ export class ToolManager {
this.canvasManager.canvas.isDrawingMode = !!tool.isDrawingMode;// 绘制模式
if (!tool.isDrawingMode) this._disableBrushIndicator()// 非绘制模式,禁用笔刷指示器
if (tool.setup) tool.setup()
this.stateManager?.aiSelectboxToolManager?.handleToolChange?.(oldTool, tool.name)
@@ -160,16 +160,13 @@ 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();
@@ -185,11 +182,12 @@ export class ToolManager {
if (!this.canvasManager.canvas) return;
// 确保有笔刷管理器
if (this.brushManager) {
const brushStore = this.brushManager?.brushStore
if (brushStore) {
this.brushManager.createEraser();
this.brushManager.setBrushSize(brushStore.state.size);
}
this.brushManager.setBrushSize(5);
this.stateManager.layerManager.setActiveObjectErasable()
// 启用笔刷指示器
this._enableBrushIndicator();
@@ -206,10 +204,16 @@ export class ToolManager {
console.warn("画笔正在更新中,请稍候...");
return;
}
this.brushManager.setBrushSize(5);
this.brushManager.setBrushColor("rgb(255, 0, 0)");
this.brushManager.setBrushOpacity(0.5);
this.brushManager.setBrushType("pencil");
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);
// 同步笔刷类型 - 修复方法名使用正确的setBrushType方法
this.brushManager.setBrushType("pencil");
}
// 更新应用到画布
this.brushManager.updateBrush();
}
@@ -222,9 +226,10 @@ export class ToolManager {
if (!this.canvasManager.canvas) return;
// 确保有笔刷管理器
if (this.brushManager) {
const brushStore = this.brushManager?.brushStore
if (brushStore) {
this.brushManager.createEraser();
this.brushManager.setBrushSize(5);
this.brushManager.setBrushSize(brushStore.state.size);
}
this.stateManager.layerManager.setAllObjectsErasable(false)

View File

@@ -3,13 +3,13 @@ import { reactive, readonly } from "vue";
class texturePresetManager { }
export class BrushState {
constructor(options) {
constructor(options = {}) {
this.state = reactive({
// 笔刷基础属性
size: 5, // 笔刷大小
color: "#000000", // 笔刷颜色
opacity: 1, // 笔刷透明度
type: "pencil", // 当前笔刷类型
size: options.size || 5, // 笔刷大小
color: options.color || "#000000", // 笔刷颜色
opacity: options.opacity || 1, // 笔刷透明度
type: options.type || "pencil", // 当前笔刷类型
// 阴影相关属性
shadowEnabled: false, // 是否启用阴影

View File

@@ -21,6 +21,8 @@ import { EraserStateManager } from "../EraserStateManager.js";
import { SprayBrush } from "./types/SprayBrush";
// import { SketchyBrush } from "./types/SketchyBrush";
// import { SpraypaintBrush } from "./types/SpraypaintBrush";
import { OperationType } from '../../tools/layerHelper'
/**
* 笔刷管理器
@@ -37,7 +39,17 @@ export class BrushManager {
*/
constructor(options = {}) {
this.canvas = options.canvas;
this.brushStore = new BrushState();
this.brushStoreList = {
[OperationType.DRAW]: new BrushState(),
[OperationType.ERASER]: new BrushState(),
[OperationType.AISELECT_DRAW]: new BrushState({
color: "rgb(255, 0, 0)",
opacity: 0.5,
}),
[OperationType.AISELECT_ERASER]: new BrushState(),
}
this.brushStore = this.brushStoreList[OperationType.DRAW];
this.layerManager = options.layerManager; // 添加图层管理器引用
this.brushIndicator = options.brushIndicator; // 添加笔刷指示器引用
// this.t = options.t
@@ -55,7 +67,15 @@ export class BrushManager {
this.isErasingActive = false;
this.currentErasedObjects = []; // 当前擦除会话中被影响的对象
}
/**
* 切换工具时,更新当前活动笔刷
* @param {string} toolName 当前工具名称
*/
handleToolChange(toolName) {
const store = this.brushStoreList[toolName];
if (!store) return;
this.brushStore = store;
}
/**
* 注册默认笔刷
* @private

View File

@@ -11,7 +11,6 @@ export class KeyEventManager {
/** 处理键盘事件 */
_handleKeyDown: any
handleKeyDown(event: any) {
event.preventDefault()
const activeID = this.stateManager.layerManager.activeID.value
const ctrl = event.ctrlKey ? 'ctrl-' : "";
const shift = event.shiftKey ? 'shift-' : "";
@@ -24,6 +23,7 @@ export class KeyEventManager {
{ key: "ctrl-s", handler: () => this.onWorkbench() },
{ key: "ctrl-shift-z", handler: () => this.stateManager.redoState() },
]
if (list.some((v) => reg.test(v.key))) event.preventDefault()
list.forEach((v: any) => {
if (reg.test(v.key)) v.handler(event)
})

View File

@@ -259,4 +259,41 @@ export function traceImageContour(canvas) {
);
return contour;
}
}
/** 裁剪画布透明像素 */
export function clipCanvasTransparent(canvas) {
const ctx = canvas.getContext("2d", { willReadFrequently: true });
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const width = canvas.width;
const height = canvas.height;
var minX = width;
var minY = height;
var maxX = 0;
var maxY = 0;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const index = (y * width + x) * 4;
if (data[index + 3] > 0) {
if (x < minX) minX = x;
if (y < minY) minY = y;
if (x > maxX) maxX = x;
if (y > maxY) maxY = y;
}
}
}
const newCanvas = document.createElement("canvas");
newCanvas.width = maxX - minX + 1;
newCanvas.height = maxY - minY + 1;
const newCtx = newCanvas.getContext("2d");
newCtx.drawImage(canvas, minX, minY, maxX - minX + 1, maxY - minY + 1, 0, 0, maxX - minX + 1, maxY - minY + 1);
return {
minX,
minY,
maxX,
maxY,
canvas: newCanvas,
};
}

View File

@@ -1,8 +1,16 @@
import { fabric } from 'fabric-with-all'
import { createStaticCanvas } from './canvasFactory'
import { getObjectsBoundingBox, cloneObjects } from './canvasMethod'
/** 导出画布为图片 */
export async function exportCanvasToImage(canvas) {
/** 导出画布为图片
* @param {fabric.Canvas} canvas - 画布对象
* @param {boolean} isDisscar - 是否丢弃选中对象
* @returns {Promise<string>} - 图片URL
*/
export async function exportCanvasToImage(canvas, isDisscar = true) {
if (isDisscar) {
canvas.discardActiveObject()
canvas.renderAll()
}
const clonedObjects = await cloneObjects(canvas.getObjects())
const staticCanvas = createStaticCanvas(document.createElement('canvas'))
const width = canvas.clipPath.width

View File

@@ -254,7 +254,43 @@ export default {
brush: 'Brush',
erase: 'Erase',
create: 'Create',
reset: 'Reset'
reset: 'Reset',
// 混合模式
blendMode: 'Blend Mode',
compositeNormal: 'Normal',
compositeNormalTip: 'Normal: Default, new graphics cover original content',
compositeDarken: 'Darken',
compositeDarkenTip: 'Darken: Take the darkest color',
compositeMultiply: 'Multiply',
compositeMultiplyTip: 'Multiply: Darken the image',
compositeColorBurn: 'Color Burn',
compositeColorBurnTip: 'Color Burn: Increase contrast and darken the bottom color',
compositeLighten: 'Lighten',
compositeLightenTip: 'Lighten: Take the brightest color',
compositeScreen: 'Screen',
compositeScreenTip: 'Screen: Lighten the image',
compositeColorDodge: 'Color Dodge',
compositeColorDodgeTip: 'Color Dodge: Reduce contrast and lighten the bottom color',
compositeLighter: 'Color Dodge (Add)',
compositeLighterTip: 'Color Dodge (Add): Add the brightness of the overlapping parts',
compositeOverlay: 'Overlay',
compositeOverlayTip: 'Overlay: Highlight effect',
compositeSoftLight: 'Soft Light',
compositeSoftLightTip: 'Soft Light: Blend effect',
compositeHardLight: 'Hard Light',
compositeHardLightTip: 'Hard Light: Highlight effect',
compositeDifference: 'Difference',
compositeDifferenceTip: 'Difference: Take the color difference between the two images',
compositeExclusion: 'Exclusion',
compositeExclusionTip: 'Exclusion: Take the absolute value of the color difference between the two images',
compositeHue: 'Hue',
compositeHueTip: 'Hue: Preserve the original image color and change the hue of the new image',
compositeSaturation: 'Saturation',
compositeSaturationTip: 'Saturation: Preserve the original image hue and change the saturation of the new image',
compositeColor: 'Color',
compositeColorTip: 'Color: Preserve the original image saturation and change the color of the new image',
compositeLuminosity: 'Luminosity',
compositeLuminosityTip: 'Luminosity: Preserve the original image color and change the luminosity of the new image',
},
clipDialog: {
title: 'Upload your profile photo',

View File

@@ -249,7 +249,43 @@ export default {
brush: '画笔',
erase: '擦除',
create: '创建',
reset: '重置'
reset: '重置',
// 混合模式
blendMode: '混合模式',
compositeNormal: '正常',
compositeNormalTip: '正常:默认,新图形覆盖原内容',
compositeDarken: '变暗',
compositeDarkenTip: '变暗:取暗部颜色',
compositeMultiply: '正片叠底',
compositeMultiplyTip: '正片叠底:图像变暗',
compositeColorBurn: '颜色加深',
compositeColorBurnTip: '颜色加深:增加对比度,变暗底层颜色',
compositeLighten: '变亮',
compositeLightenTip: '变亮:取亮部颜色',
compositeScreen: '滤色',
compositeScreenTip: '滤色:图像变亮',
compositeColorDodge: '颜色减淡',
compositeColorDodgeTip: '颜色减淡:降低对比度,加亮底层颜色',
compositeLighter: '颜色减淡(添加)',
compositeLighterTip: '颜色减淡(添加):重叠部分亮度叠加',
compositeOverlay: '叠加',
compositeOverlayTip: '叠加:高光效果',
compositeSoftLight: '柔光',
compositeSoftLightTip: '柔光:混合效果',
compositeHardLight: '强光',
compositeHardLightTip: '强光:高光效果',
compositeDifference: '差值',
compositeDifferenceTip: '差值:取两图像颜色差',
compositeExclusion: '排除',
compositeExclusionTip: '排除:取两图像颜色差的绝对值',
compositeHue: '色相',
compositeHueTip: '色相:保留原图像颜色,改变新图像色相',
compositeSaturation: '饱和度',
compositeSaturationTip: '饱和度:保留原图像色相,改变新图像饱和度',
compositeColor: '颜色',
compositeColorTip: '颜色:保留原图像饱和度,改变新图像颜色',
compositeLuminosity: '亮度',
compositeLuminosityTip: '亮度:保留原图像颜色,改变新图像亮度',
},
clipDialog: {
title: '上传您的个人资料照片',

View File

@@ -11,12 +11,16 @@
import { useRoute } from 'vue-router'
const route = useRoute()
const url =
'https://minio-api.aida.com.hk/fida-public-bucket/furniture/sketches/e3082a38-55d2-4313-ad53-55aad715cf67.png'
'https://www.minio-api.aida.com.hk/fida-user/2/d8512e53-f016-4ad6-8245-2f304d89e7b2.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260331%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260331T032733Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=25e5ec227a0ca22942e71eff3a4f07a23f8812ff3db5522e1466b3a77288be70'
const openCanvas = () => {
myEvent.emit('openFlowCanvas', { url })
}
const openDepthCanvas = () => {
myEvent.emit('openDepthCanvas', { url, canvasId: '' })
myEvent.emit('openDepthCanvas', {
url,
canvasId: '69cb3f244a1dd46c0bdbb432',
sketchId: '69c63417cb064e32ff6826a3'
})
}
onMounted(() => {
if (route.query.depth) {