事件替换颜色等画布

This commit is contained in:
李志鹏
2026-01-09 14:40:18 +08:00
parent 274ea15dcc
commit 88bd58fc66
8 changed files with 584 additions and 200 deletions

View File

@@ -977,6 +977,17 @@ defineExpose({
...opts, ...opts,
}); });
}, },
updateOtherLayers: async (otherData) => {
await canvasManager?.createOtherLayers?.(otherData, true);
layerManager.activeLayerId.value = ""
layerManager?.sortLayers();
await layerManager?.updateLayersObjectsInteractivity?.(true);
canvasManager?.canvas?.renderAll();
setTimeout(() => {
canvasManager.updateAllThumbnails();
}, 500);
return true;
},
//图片url或者base64 //图片url或者base64
addImageToLayer: async ( addImageToLayer: async (
url, url,

View File

@@ -1325,12 +1325,12 @@ export class CanvasManager {
// } // }
// 重载代码后支持回调中操作一些内容 // 重载代码后支持回调中操作一些内容
await calllBack?.();
// 确保所有对象的交互性正确设置 // 确保所有对象的交互性正确设置
await this.layerManager?.updateLayersObjectsInteractivity?.(); await this.layerManager?.updateLayersObjectsInteractivity?.();
console.log(this.layerManager.layers.value); console.log(this.layerManager.layers.value);
await calllBack?.();
// 更新所有缩略图 // 更新所有缩略图
setTimeout(() => { setTimeout(() => {
this.updateAllThumbnails(); this.updateAllThumbnails();
@@ -1357,13 +1357,22 @@ export class CanvasManager {
* 创建其他图层:印花、颜色、元素... * 创建其他图层:印花、颜色、元素...
* @param {Object} otherData - 其他图层数据 * @param {Object} otherData - 其他图层数据
*/ */
async createOtherLayers(otherData) { async createOtherLayers(otherData, isUpdate = false) {
if (!otherData) return console.warn("otherData 为空不需要添加"); if (!otherData) return console.warn("otherData 为空不需要添加");
const otherData_ = JSON.parse(JSON.stringify(otherData)); const otherData_ = JSON.parse(JSON.stringify(otherData));
console.log("==========创建其他图层", otherData_); console.log("==========创建其他图层", otherData_);
const updateColor = !!otherData_.color;
const updateSpecialGroup = !!otherData_.printObject || !!otherData_.trims;
// 删除颜色图层和特殊组图层 // 删除颜色图层和特殊组图层
const ids = [SpecialLayerId.COLOR, SpecialLayerId.SPECIAL_GROUP]; const ids = [];
if(isUpdate){
updateColor && ids.push(SpecialLayerId.COLOR)
updateSpecialGroup && ids.push(SpecialLayerId.SPECIAL_GROUP)
}else{
ids.push(SpecialLayerId.COLOR)
ids.push(SpecialLayerId.SPECIAL_GROUP)
}
this.layers.value = this.layers.value.filter((layer) => { this.layers.value = this.layers.value.filter((layer) => {
if(ids.includes(layer.id)){ if(ids.includes(layer.id)){
ids.push(...layer.children?.map((child) => child.id)); ids.push(...layer.children?.map((child) => child.id));
@@ -1375,11 +1384,11 @@ export class CanvasManager {
// 创建颜色图层 // 创建颜色图层
await this.createColorLayer(otherData_.color); otherData_.color && await this.createColorLayer(otherData_.color);
const printTrimsLayers = [];// 印花和元素图层 const printTrimsLayers = [];// 印花和元素图层
const singleLayers = [];// 平铺图层 const singleLayers = [];// 平铺图层
otherData_?.printObject?.prints?.forEach((print, index) => { otherData_.printObject?.prints?.forEach((print, index) => {
print.name = t("Canvas.Print") + (index + 1); print.name = t("Canvas.Print") + (index + 1);
if(print.ifSingle){ if(print.ifSingle){
printTrimsLayers.unshift({...print}); printTrimsLayers.unshift({...print});
@@ -1387,12 +1396,13 @@ export class CanvasManager {
singleLayers.unshift({...print}); singleLayers.unshift({...print});
} }
}) })
otherData_?.trims?.prints?.forEach((trims, index) => { otherData_.trims?.prints?.forEach((trims, index) => {
trims.name = t("Canvas.Elements") + (index + 1); trims.name = t("Canvas.Elements") + (index + 1);
printTrimsLayers.unshift({...trims}); printTrimsLayers.unshift({...trims});
}) })
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers); if(isUpdate ? updateSpecialGroup : true){
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
}
await this.changeCanvas(); await this.changeCanvas();
} }
@@ -1432,8 +1442,8 @@ export class CanvasManager {
}); });
tagObject.set('clipPath', transparentMask); tagObject.set('clipPath', transparentMask);
} }
async createColorLayer(color){ async createColorLayer(color_){
if(!color) return console.warn("颜色为空不需要添加"); const color = color_ || {r:0,g:0,b:0,a:0};
// if(findLayer(this.layers.value, SpecialLayerId.COLOR)) { // if(findLayer(this.layers.value, SpecialLayerId.COLOR)) {
// return console.warn("画布中已存在颜色图层"); // return console.warn("画布中已存在颜色图层");
// } // }
@@ -1619,7 +1629,7 @@ export class CanvasManager {
// }) // })
// children.push(layer); // children.push(layer);
// } // }
if(children.length === 0) return; // if(children.length === 0) return;
const groupRect = new fabric.Rect({}); const groupRect = new fabric.Rect({});
await this.setObjecCliptInfo(groupRect); await this.setObjecCliptInfo(groupRect);
// 插入组图层 // 插入组图层
@@ -1638,6 +1648,8 @@ export class CanvasManager {
specialType: SpecialType.PRINT_TRIMS_G, specialType: SpecialType.PRINT_TRIMS_G,
}); });
this.layers.value.splice(groupIndex, 0, groupLayer); this.layers.value.splice(groupIndex, 0, groupLayer);
console.log("==========layers", [...this.layers.value]);
} }
/** /**

View File

@@ -199,9 +199,12 @@ export class LayerManager {
if (!this.canvas) return; if (!this.canvas) return;
if (isUseOptimize) { if (isUseOptimize) {
// 优化渲染 - 统一批处理 支持异步回调 // 优化渲染 - 统一批处理 支持异步回调
await optimizeCanvasRendering(this.canvas, async () => { await new Promise((resolve) => {
// 应用图层交互规则 optimizeCanvasRendering(this.canvas, async () => {
await this._applyInteractionRules({ isMoveing }); // 应用图层交互规则
await this._applyInteractionRules({ isMoveing });
resolve();
});
}); });
} else { } else {
// 直接应用图层交互规则 // 直接应用图层交互规则
@@ -333,7 +336,6 @@ export class LayerManager {
const objects = this.canvas.getObjects(); const objects = this.canvas.getObjects();
const editorMode = this.editorMode || CanvasConfig.defaultTool; const editorMode = this.editorMode || CanvasConfig.defaultTool;
const layers = this.layers?.value || []; const layers = this.layers?.value || [];
// 创建缓存以避免重复查找 // 创建缓存以避免重复查找
const layerMap = {}; const layerMap = {};
layers.forEach((layer) => { layers.forEach((layer) => {

View File

@@ -109,7 +109,32 @@ const exportExtraInfo = async () => {
} }
}; };
// 更新其他图层颜色
const updateOtherLayersColor = async () => {
const obj = {
color: {rgba: {r:255,g:255,b:0,a:1}},
}
await canvasEditor?.value?.updateOtherLayers?.(obj);
};
// 更新其他图层印花
const updateOtherLayersPrint = async () => {
const obj = {
printObject: {
prints: [
{
ifSingle: true,
level2Type: "Pattern",
designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg",
location: [250, 780],
scale: [0.3, 0.4],
angle: 0,
},
]
},
}
await canvasEditor?.value?.updateOtherLayers?.(obj);
};
const changeCanvas = (command) => { const changeCanvas = (command) => {
console.log(command); console.log(command);
@@ -219,6 +244,20 @@ const customToolsList = ref([
label: "导E", label: "导E",
class: "export-btn", class: "export-btn",
}, },
{
id: "updateExtraInfo_color",
title: "更新颜色",
action: updateOtherLayersColor,
label: "更C",
class: "export-btn",
},
{
id: "updateExtraInfo_print",
title: "更新印花",
action: updateOtherLayersPrint,
label: "更P",
class: "export-btn",
},
{ {
id: "exportPNG", id: "exportPNG",
title: "导出PNG", //导出画布图片 title: "导出PNG", //导出画布图片
@@ -309,7 +348,7 @@ const otherData = {
level2Type: "Pattern", level2Type: "Pattern",
designType: "Library", designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg", path: "/src/assets/images/canvas/yinhua1.jpg",
location: [650, 650], location: [550, 650],
scale: [0.15, 0.2], scale: [0.15, 0.2],
angle: 0, angle: 0,
}, },

View File

@@ -0,0 +1,156 @@
<template>
<div class="pingpu" ref="el"><canvas ref="canvasRef"></canvas></div>
</template>
<script setup>
import { fabric } from "fabric-with-all";
import { ref, watch, onMounted } from "vue";
const props = defineProps({
url: { type: String, required: true },
offsetX: { type: Number, default: 0 }, // px
offsetY: { type: Number, default: 0 }, // px
angle: { type: Number, default: 0 }, // 角度
scale: { type: Number, default: 100 }, // %
gapX: { type: Number, default: 0 }, // px
gapY: { type: Number, default: 0 }, // px
});
watch(
() => props.url,
() => getOriginalImage()
);
watch(
() => [
props.offsetX,
props.offsetY,
props.angle,
props.scale,
props.gapX,
props.gapY,
],
() => setCanvasData()
);
const el = ref(null);
const canvasRef = ref(null);
const canvas = ref(null);
const observer = ref(null);
const id = "asfs123121sfe";
onMounted(async () => {
initCanvas();
await getOriginalImage();
setCanvasData();
let throttleTimeout = null;
let lastRunTime = 0;
let trailingTimeout = null;
observer.value = new ResizeObserver((entries) => {
const now = Date.now();
const throttleDelay = 100;
if (!throttleTimeout) {
updateCanvasSize();
lastRunTime = now;
throttleTimeout = setTimeout(() => {
throttleTimeout = null;
}, throttleDelay);
} else {
clearTimeout(trailingTimeout);
trailingTimeout = setTimeout(() => {
updateCanvasSize();
lastRunTime = Date.now();
}, throttleDelay);
}
});
observer.value.observe(el.value);
});
onBeforeUnmount(() => {
observer.value.disconnect();
});
const initCanvas = () => {
canvas.value = new fabric.Canvas(canvasRef.value, {
selection: false,
evented: false,
});
canvas.value.setWidth(el.value.offsetWidth);
canvas.value.setHeight(el.value.offsetHeight);
};
const updateCanvasSize = () => {
canvas.value.setWidth(el.value.offsetWidth);
canvas.value.setHeight(el.value.offsetHeight);
setCanvasData();
};
const originalImage = ref(null);
const getOriginalImage = () => {
return new Promise((resolve, reject) => {
fabric.Image.fromURL(
props.url,
(object) => {
const imgElement = object.getElement();
// 创建透明 Canvas
const tcanvas = document.createElement("canvas");
tcanvas.width = imgElement.width;
tcanvas.height = imgElement.height;
const ctx = tcanvas.getContext("2d");
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
ctx.drawImage(imgElement, 0, 0);
originalImage.value = tcanvas;
resolve(tcanvas);
},
{ crossOrigin: "anonymous" }
);
});
};
const setCanvasData = () => {
canvas.value.getObjects().forEach((obj) => {
if (obj.id === id) canvas.value.remove(obj);
});
const image = originalImage.value;
const cwidth = canvas.value.width;
const cheight = canvas.value.height;
const offsetX = props.offsetX;
const offsetY = props.offsetY;
const scaleX = ((cwidth / image.width) * (props.scale / 100)) / 5;
const scaleY = ((cheight / image.height) * (props.scale / 100)) / 5;
const scale = cwidth > cheight ? scaleX : scaleY;
const angle = props.angle;
const gapX = props.gapX;
const gapY = props.gapY;
const patternTransform = fabric.util.composeMatrix({
scaleX: scale,
scaleY: scale,
angle: angle,
});
// 创建透明 Canvas
const tcanvas = document.createElement("canvas");
tcanvas.width = image.width + gapX;
tcanvas.height = image.height + gapY;
const ctx = tcanvas.getContext("2d");
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
ctx.drawImage(image, 0, 0);
const pattern = new fabric.Pattern({
source: tcanvas,
repeat: "repeat",
patternTransform,
offsetX, // 水平偏移
offsetY, // 垂直偏移
});
const rect = new fabric.Rect({
id,
width: cwidth,
height: cheight,
top: 0,
left: 0,
// scaleX: 1,
// scaleY: 1,
fill: pattern,
// evented: false,
// selectable: false,
});
canvas.value.add(rect);
canvas.value.renderAll();
};
</script>
<style lang='less' scoped>
.pingpu {
width: 100%;
height: 100%;
}
</style>

View File

@@ -6,38 +6,16 @@
import { fabric } from "fabric-with-all"; import { fabric } from "fabric-with-all";
import { ref, watch, onMounted } from "vue"; import { ref, watch, onMounted } from "vue";
const props = defineProps({ const props = defineProps({
url: { type: String, required: true }, list: { type: Array, default: () => [] },
offsetX: { type: Number, default: 0 }, // px
offsetY: { type: Number, default: 0 }, // px
angle: { type: Number, default: 0 }, // 角度
scale: { type: Number, default: 100 }, // %
gapX: { type: Number, default: 0 }, // px
gapY: { type: Number, default: 0 }, // px
}); });
watch(
() => props.url,
() => getOriginalImage()
);
watch(
() => [
props.offsetX,
props.offsetY,
props.angle,
props.scale,
props.gapX,
props.gapY,
],
() => setCanvasData()
);
const el = ref(null); const el = ref(null);
const canvasRef = ref(null); const canvasRef = ref(null);
const canvas = ref(null);
const observer = ref(null); const observer = ref(null);
const id = "asfs123121sfe"; var canvas = null;
onMounted(async () => { onMounted(async () => {
initCanvas(); initCanvas();
await getOriginalImage(); await setCanvasData();
setCanvasData();
let throttleTimeout = null; let throttleTimeout = null;
let lastRunTime = 0; let lastRunTime = 0;
let trailingTimeout = null; let trailingTimeout = null;
@@ -64,23 +42,21 @@
observer.value.disconnect(); observer.value.disconnect();
}); });
const initCanvas = () => { const initCanvas = () => {
canvas.value = new fabric.Canvas(canvasRef.value, { canvas = new fabric.Canvas(canvasRef.value, {
selection: false, selection: false,
evented: false,
}); });
canvas.value.setWidth(el.value.offsetWidth); canvas.setWidth(el.value.offsetWidth);
canvas.value.setHeight(el.value.offsetHeight); canvas.setHeight(el.value.offsetHeight);
}; };
const updateCanvasSize = () => { const updateCanvasSize = async () => {
canvas.value.setWidth(el.value.offsetWidth); canvas.setWidth(el.value.offsetWidth);
canvas.value.setHeight(el.value.offsetHeight); canvas.setHeight(el.value.offsetHeight);
setCanvasData(); await setCanvasData();
}; };
const originalImage = ref(null); const urlToCanvas = (url) => {
const getOriginalImage = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fabric.Image.fromURL( fabric.Image.fromURL(
props.url, url,
(object) => { (object) => {
const imgElement = object.getElement(); const imgElement = object.getElement();
// 创建透明 Canvas // 创建透明 Canvas
@@ -90,62 +66,66 @@
const ctx = tcanvas.getContext("2d"); const ctx = tcanvas.getContext("2d");
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height); ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
ctx.drawImage(imgElement, 0, 0); ctx.drawImage(imgElement, 0, 0);
originalImage.value = tcanvas;
resolve(tcanvas); resolve(tcanvas);
}, },
{ crossOrigin: "anonymous" } { crossOrigin: "anonymous" }
); );
}); });
}; };
const setCanvasData = () => { const setCanvasData = async () => {
canvas.value.getObjects().forEach((obj) => { canvas.clear();
if (obj.id === id) canvas.value.remove(obj); const cwidth = canvas.width;
}); const cheight = canvas.height;
const image = originalImage.value;
const cwidth = canvas.value.width; for (let i = 0; i < props.list.length; i++) {
const cheight = canvas.value.height; let item = props.list[i];
const offsetX = props.offsetX; let image = await urlToCanvas(item.path);
const offsetY = props.offsetY; let offsetX = item.location[0];
const scaleX = ((cwidth / image.width) * (props.scale / 100)) / 5; let offsetY = item.location[1];
const scaleY = ((cheight / image.height) * (props.scale / 100)) / 5; let scaleX = ((cwidth / image.width) * item.scale[0]) / 5;
const scale = cwidth > cheight ? scaleX : scaleY; let scaleY = ((cheight / image.height) * item.scale[1]) / 5;
const angle = props.angle; let scale = cwidth > cheight ? scaleX : scaleY;
const gapX = props.gapX; let angle = item.angle;
const gapY = props.gapY; let gapX = item.object.gapX;
const patternTransform = fabric.util.composeMatrix({ let gapY = item.object.gapY;
scaleX: scale, let patternTransform = fabric.util.composeMatrix({
scaleY: scale, scaleX: scale,
angle: angle, scaleY: scale,
}); angle: angle,
// 创建透明 Canvas });
const tcanvas = document.createElement("canvas"); // 创建透明 Canvas
tcanvas.width = image.width + gapX; let tcanvas = document.createElement("canvas");
tcanvas.height = image.height + gapY; tcanvas.width = image.width + gapX;
const ctx = tcanvas.getContext("2d"); tcanvas.height = image.height + gapY;
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height); let ctx = tcanvas.getContext("2d");
ctx.drawImage(image, 0, 0); ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
const pattern = new fabric.Pattern({ ctx.drawImage(image, 0, 0);
source: tcanvas, let pattern = new fabric.Pattern({
repeat: "repeat", source: tcanvas,
patternTransform, repeat: "repeat",
offsetX, // 水平偏移 patternTransform,
offsetY, // 垂直偏移 offsetX, // 水平偏移
}); offsetY, // 垂直偏移
const rect = new fabric.Rect({ });
id, let rect = new fabric.Rect({
width: cwidth, id: item.id,
height: cheight, width: cwidth,
top: 0, height: cheight,
left: 0, fill: pattern,
// scaleX: 1,
// scaleY: 1, ...item.object,
fill: pattern, });
evented: false, canvas.add(rect);
selectable: false, }
}); canvas.renderAll();
canvas.value.add(rect);
canvas.value.renderAll();
}; };
const updataList = (list) => {
console.log(list);
};
defineExpose({
updataList,
});
</script> </script>
<style lang='less' scoped> <style lang='less' scoped>

View File

@@ -0,0 +1,104 @@
<template>
<div class="test">
<!-- <div class="control" v-for="(item, index) in list" :key="index">
<div>
<span>偏移X</span>
<input type="number" v-model="item.offsetX" />
</div>
<div>
<span>偏移Y</span>
<input type="number" v-model="item.offsetY" />
</div>
<div>
<span>角度</span>
<input type="number" v-model="item.angle" />
</div>
<div>
<span>缩放</span>
<input type="number" v-model="item.scale" />
</div>
<div>
<span>水平间距</span>
<input type="number" v-model="item.gapX" />
</div>
<div>
<span>垂直间距</span>
<input type="number" v-model="item.gapY" />
</div>
<h2>---------整体--------</h2>
<div>
<span>缩放</span>
<input type="number" v-model="item.object.scale" step="0.1" />
</div>
<div>
<span>X</span>
<input type="number" v-model="item.object.left" />
</div>
<div>
<span>Y</span>
<input type="number" v-model="item.object.top" />
</div>
</div> -->
<div class="box">
<pingpu :list="list" />
</div>
</div>
</template>
<script lang="ts" setup>
import pingpu from "./pingpu.vue";
const list = ref([
{
ifSingle: false,
level2Type: "Pattern",
designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg",
location: [0, 0],
scale: [1, 1],
angle: 0,
name: "Print2",
priority: 1,
object: {
top: 0,
left: 0,
scaleX: 1,
scaleY: 1,
opacity: 1,
angle: 0,
flipX: false,
flipY: false,
blendMode: "multiply",
gapX: 0,
gapY: 0,
},
gap: [0, 0],
},
]);
</script>
<style lang='less' scoped>
.test {
> .control {
margin-left: 10px;
margin-top: 10px;
> div {
display: flex;
align-items: center;
margin-bottom: 5px;
> span {
width: 80px;
}
> input {
padding-left: 10px;
border-radius: 5px;
}
}
}
> .box {
width: 300px;
height: 500px;
overflow: hidden;
border: 1px solid #000;
}
}
</style>

View File

@@ -1,121 +1,201 @@
<template> <template>
<div class="test"> <div class="test">
<div v-for="(item, index) in data" :key="index"> <div class="control" v-for="(item, index) in list" :key="index">
<div class="control"> <div>
<div> <span>偏移X</span>
<span>偏移X</span> <input type="number" v-model="item.location[0]" />
<input type="number" v-model="item.offsetX" />
</div>
<div>
<span>偏移Y</span>
<input type="number" v-model="item.offsetY" />
</div>
<div>
<span>角度</span>
<input type="number" v-model="item.angle" />
</div>
<div>
<span>缩放</span>
<input type="number" v-model="item.scale" />
</div>
<div>
<span>水平间距</span>
<input type="number" v-model="item.gapX" />
</div>
<div>
<span>垂直间距</span>
<input type="number" v-model="item.gapY" />
</div>
<h2>---------整体--------</h2>
<div>
<span>缩放</span>
<input type="number" v-model="item.object.scale" step="0.1" />
</div>
<div>
<span>X</span>
<input type="number" v-model="item.object.left" />
</div>
<div>
<span>Y</span>
<input type="number" v-model="item.object.top" />
</div>
</div> </div>
<div class="box"> <div>
<pingpu v-bind="item" :style="{ <span>偏移Y</span>
transform: `translate(${item.object.left}px, ${item.object.top}px) scale(${item.object.scale})`, <input type="number" v-model="item.location[1]" />
transformOrigin: 'top left'
}" />
</div> </div>
<div>
<span>角度</span>
<input type="number" v-model="item.angle" />
</div>
<div>
<span>缩放</span>
<input type="number" v-model="item.scale" />
</div>
<div>
<span>水平间距</span>
<input type="number" v-model="item.object.gapX" />
</div>
<div>
<span>垂直间距</span>
<input type="number" v-model="item.object.gapY" />
</div>
<hr />
<div>
<span>缩放X</span>
<input type="number" v-model="item.object.scaleX" step="0.1" />
</div>
<div>
<span>缩放Y</span>
<input type="number" v-model="item.object.scaleY" step="0.1" />
</div>
<div>
<span>X</span>
<input type="number" v-model="item.object.left" />
</div>
<div>
<span>Y</span>
<input type="number" v-model="item.object.top" />
</div>
<div>
<span>透明度</span>
<input
type="range"
v-model="item.object.opacity"
step="0.1"
min="0"
max="1"
/>
</div>
</div>
<div class="box">
<pingpu :list="list" ref="pingpuRef" />
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, watch } from "vue";
import pingpu from "./pingpu.vue"; import pingpu from "./pingpu.vue";
const data = ref([ const convertDotNotationToBracket = (str) =>
{ str.replace(/(?:^|\.)(\d+)(?=\.|$)/g, "[#$1]").replace(/\[#/g, "[");
url: "/src/assets/images/canvas/yinhua1.jpg",
offsetX: 0, // px
offsetY: 0, // px
angle: 0, // 角度
scale: 100, // %
gapX: 0, // px
gapY: 0, // px
object:{ const list = ref([
scale: 1, // % {
top: 0, // px id: "1",
left: 0, // px ifSingle: false,
} level2Type: "Pattern",
designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg",
location: [0, 0],
scale: [1, 1],
angle: 0,
name: "Print2",
priority: 1,
object: {
top: 0,
left: 0,
scaleX: 1,
scaleY: 1,
opacity: 1,
angle: 0,
flipX: false,
flipY: false,
blendMode: "multiply",
gapX: 10,
gapY: 20,
},
}, },
{ {
url: "/src/assets/images/canvas/yinhua1.jpg", id: "2",
offsetX: 0, // px ifSingle: false,
offsetY: 0, // px level2Type: "Pattern",
angle: 0, // 角度 designType: "Library",
scale: 100, // % path: "/src/assets/images/canvas/yinhua1.jpg",
gapX: 0, // px location: [0, 0],
gapY: 0, // px scale: [2, 2],
angle: -45,
object:{ name: "Print2",
scale: 1, // % priority: 1,
top: 0, // px object: {
left: 0, // px top: 150,
} left: 250,
scaleX: 0.5,
scaleY: 0.5,
opacity: 1,
angle: 45,
flipX: false,
flipY: false,
blendMode: "multiply",
gapX: 0,
gapY: 0,
},
}, },
]); ]);
// 深拷贝
const deepCopy = (obj) => JSON.parse(JSON.stringify(obj));
const oldList = ref(deepCopy(list.value));
const pingpuRef = ref(null);
watch(
() => list.value,
() => updateList(),
{ deep: true }
);
const updateList = () => {
const changeList = [];
oldList.value.forEach((oldItem) => {
const newItem = list.value.find((v) => v.id === oldItem.id);
if (newItem) {
const arr = findDiffProps(oldItem, newItem);
arr.forEach((item) => {
changeList.push({
...item,
id: oldItem.id,
action: "update-item",
key: convertDotNotationToBracket(item.key),
});
});
} else {
changeList.push({
id: oldItem.id,
action: "delete-item",
});
}
});
oldList.value = deepCopy(list.value);
console.log(changeList);
if (changeList.length) {
pingpuRef.value.updataList(changeList);
}
};
// 递归查找不同的属性
const findDiffProps = (obj1, obj2, diffProps = [], path = "") => {
for (const key in obj1) {
if (typeof obj1[key] === "object") {
findDiffProps(obj1[key], obj2[key], diffProps, `${path}${key}.`);
} else if (obj1[key] !== obj2[key]) {
diffProps.push({
key: `${path}${key}`,
oldValue: obj1[key],
newValue: obj2[key],
});
}
}
return diffProps;
};
</script> </script>
<style lang='less' scoped> <style lang='less' scoped>
.test { .test {
display: flex; > .control {
> div { display: inline-block;
width: 50%; border: 1px solid #000;
> .control { padding: 10px;
margin-left: 10px; margin: 10px;
margin-top: 10px; border-radius: 10px;
> div { > div {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 5px; margin-bottom: 5px;
> span { > span {
width: 80px; width: 80px;
} }
> input { > input {
padding-left: 10px; padding-left: 10px;
border-radius: 5px; border-radius: 5px;
}
} }
} }
> .box { }
position: fixed; > .box {
top: 40%; width: 400px;
left: 10%; height: 500px;
width: 300px; overflow: hidden;
height: 500px; border: 1px solid #000;
overflow: hidden;
border: 1px solid #000;
}
} }
} }
</style> </style>