事件替换颜色等画布
This commit is contained in:
@@ -977,6 +977,17 @@ defineExpose({
|
||||
...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
|
||||
addImageToLayer: async (
|
||||
url,
|
||||
|
||||
@@ -1325,12 +1325,12 @@ export class CanvasManager {
|
||||
// }
|
||||
|
||||
// 重载代码后支持回调中操作一些内容
|
||||
await calllBack?.();
|
||||
|
||||
// 确保所有对象的交互性正确设置
|
||||
await this.layerManager?.updateLayersObjectsInteractivity?.();
|
||||
console.log(this.layerManager.layers.value);
|
||||
|
||||
await calllBack?.();
|
||||
// 更新所有缩略图
|
||||
setTimeout(() => {
|
||||
this.updateAllThumbnails();
|
||||
@@ -1357,13 +1357,22 @@ export class CanvasManager {
|
||||
* 创建其他图层:印花、颜色、元素...
|
||||
* @param {Object} otherData - 其他图层数据
|
||||
*/
|
||||
async createOtherLayers(otherData) {
|
||||
async createOtherLayers(otherData, isUpdate = false) {
|
||||
if (!otherData) return console.warn("otherData 为空不需要添加");
|
||||
const otherData_ = JSON.parse(JSON.stringify(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) => {
|
||||
if(ids.includes(layer.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 singleLayers = [];// 平铺图层
|
||||
otherData_?.printObject?.prints?.forEach((print, index) => {
|
||||
otherData_.printObject?.prints?.forEach((print, index) => {
|
||||
print.name = t("Canvas.Print") + (index + 1);
|
||||
if(print.ifSingle){
|
||||
printTrimsLayers.unshift({...print});
|
||||
@@ -1387,12 +1396,13 @@ export class CanvasManager {
|
||||
singleLayers.unshift({...print});
|
||||
}
|
||||
})
|
||||
otherData_?.trims?.prints?.forEach((trims, index) => {
|
||||
otherData_.trims?.prints?.forEach((trims, index) => {
|
||||
trims.name = t("Canvas.Elements") + (index + 1);
|
||||
printTrimsLayers.unshift({...trims});
|
||||
})
|
||||
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
|
||||
|
||||
if(isUpdate ? updateSpecialGroup : true){
|
||||
await this.createPrintTrimsLayers(printTrimsLayers, singleLayers);
|
||||
}
|
||||
await this.changeCanvas();
|
||||
}
|
||||
|
||||
@@ -1432,8 +1442,8 @@ export class CanvasManager {
|
||||
});
|
||||
tagObject.set('clipPath', transparentMask);
|
||||
}
|
||||
async createColorLayer(color){
|
||||
if(!color) return console.warn("颜色为空不需要添加");
|
||||
async createColorLayer(color_){
|
||||
const color = color_ || {r:0,g:0,b:0,a:0};
|
||||
// if(findLayer(this.layers.value, SpecialLayerId.COLOR)) {
|
||||
// return console.warn("画布中已存在颜色图层");
|
||||
// }
|
||||
@@ -1619,7 +1629,7 @@ export class CanvasManager {
|
||||
// })
|
||||
// children.push(layer);
|
||||
// }
|
||||
if(children.length === 0) return;
|
||||
// if(children.length === 0) return;
|
||||
const groupRect = new fabric.Rect({});
|
||||
await this.setObjecCliptInfo(groupRect);
|
||||
// 插入组图层
|
||||
@@ -1638,6 +1648,8 @@ export class CanvasManager {
|
||||
specialType: SpecialType.PRINT_TRIMS_G,
|
||||
});
|
||||
this.layers.value.splice(groupIndex, 0, groupLayer);
|
||||
console.log("==========layers", [...this.layers.value]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -199,9 +199,12 @@ export class LayerManager {
|
||||
if (!this.canvas) return;
|
||||
if (isUseOptimize) {
|
||||
// 优化渲染 - 统一批处理 支持异步回调
|
||||
await optimizeCanvasRendering(this.canvas, async () => {
|
||||
// 应用图层交互规则
|
||||
await this._applyInteractionRules({ isMoveing });
|
||||
await new Promise((resolve) => {
|
||||
optimizeCanvasRendering(this.canvas, async () => {
|
||||
// 应用图层交互规则
|
||||
await this._applyInteractionRules({ isMoveing });
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// 直接应用图层交互规则
|
||||
@@ -333,7 +336,6 @@ export class LayerManager {
|
||||
const objects = this.canvas.getObjects();
|
||||
const editorMode = this.editorMode || CanvasConfig.defaultTool;
|
||||
const layers = this.layers?.value || [];
|
||||
|
||||
// 创建缓存以避免重复查找
|
||||
const layerMap = {};
|
||||
layers.forEach((layer) => {
|
||||
|
||||
@@ -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) => {
|
||||
console.log(command);
|
||||
@@ -219,6 +244,20 @@ const customToolsList = ref([
|
||||
label: "导E",
|
||||
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",
|
||||
title: "导出PNG", //导出画布图片
|
||||
@@ -309,7 +348,7 @@ const otherData = {
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||
location: [650, 650],
|
||||
location: [550, 650],
|
||||
scale: [0.15, 0.2],
|
||||
angle: 0,
|
||||
},
|
||||
|
||||
156
src/component/Canvas/pingpu copy.vue
Normal file
156
src/component/Canvas/pingpu copy.vue
Normal 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>
|
||||
@@ -6,38 +6,16 @@
|
||||
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
|
||||
list: { type: Array, default: () => [] },
|
||||
});
|
||||
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";
|
||||
var canvas = null;
|
||||
onMounted(async () => {
|
||||
initCanvas();
|
||||
await getOriginalImage();
|
||||
setCanvasData();
|
||||
await setCanvasData();
|
||||
|
||||
let throttleTimeout = null;
|
||||
let lastRunTime = 0;
|
||||
let trailingTimeout = null;
|
||||
@@ -64,23 +42,21 @@
|
||||
observer.value.disconnect();
|
||||
});
|
||||
const initCanvas = () => {
|
||||
canvas.value = new fabric.Canvas(canvasRef.value, {
|
||||
canvas = new fabric.Canvas(canvasRef.value, {
|
||||
selection: false,
|
||||
evented: false,
|
||||
});
|
||||
canvas.value.setWidth(el.value.offsetWidth);
|
||||
canvas.value.setHeight(el.value.offsetHeight);
|
||||
canvas.setWidth(el.value.offsetWidth);
|
||||
canvas.setHeight(el.value.offsetHeight);
|
||||
};
|
||||
const updateCanvasSize = () => {
|
||||
canvas.value.setWidth(el.value.offsetWidth);
|
||||
canvas.value.setHeight(el.value.offsetHeight);
|
||||
setCanvasData();
|
||||
const updateCanvasSize = async () => {
|
||||
canvas.setWidth(el.value.offsetWidth);
|
||||
canvas.setHeight(el.value.offsetHeight);
|
||||
await setCanvasData();
|
||||
};
|
||||
const originalImage = ref(null);
|
||||
const getOriginalImage = () => {
|
||||
const urlToCanvas = (url) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fabric.Image.fromURL(
|
||||
props.url,
|
||||
url,
|
||||
(object) => {
|
||||
const imgElement = object.getElement();
|
||||
// 创建透明 Canvas
|
||||
@@ -90,62 +66,66 @@
|
||||
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();
|
||||
const setCanvasData = async () => {
|
||||
canvas.clear();
|
||||
const cwidth = canvas.width;
|
||||
const cheight = canvas.height;
|
||||
|
||||
for (let i = 0; i < props.list.length; i++) {
|
||||
let item = props.list[i];
|
||||
let image = await urlToCanvas(item.path);
|
||||
let offsetX = item.location[0];
|
||||
let offsetY = item.location[1];
|
||||
let scaleX = ((cwidth / image.width) * item.scale[0]) / 5;
|
||||
let scaleY = ((cheight / image.height) * item.scale[1]) / 5;
|
||||
let scale = cwidth > cheight ? scaleX : scaleY;
|
||||
let angle = item.angle;
|
||||
let gapX = item.object.gapX;
|
||||
let gapY = item.object.gapY;
|
||||
let patternTransform = fabric.util.composeMatrix({
|
||||
scaleX: scale,
|
||||
scaleY: scale,
|
||||
angle: angle,
|
||||
});
|
||||
// 创建透明 Canvas
|
||||
let tcanvas = document.createElement("canvas");
|
||||
tcanvas.width = image.width + gapX;
|
||||
tcanvas.height = image.height + gapY;
|
||||
let ctx = tcanvas.getContext("2d");
|
||||
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
|
||||
ctx.drawImage(image, 0, 0);
|
||||
let pattern = new fabric.Pattern({
|
||||
source: tcanvas,
|
||||
repeat: "repeat",
|
||||
patternTransform,
|
||||
offsetX, // 水平偏移
|
||||
offsetY, // 垂直偏移
|
||||
});
|
||||
let rect = new fabric.Rect({
|
||||
id: item.id,
|
||||
width: cwidth,
|
||||
height: cheight,
|
||||
fill: pattern,
|
||||
|
||||
...item.object,
|
||||
});
|
||||
canvas.add(rect);
|
||||
}
|
||||
canvas.renderAll();
|
||||
};
|
||||
const updataList = (list) => {
|
||||
|
||||
console.log(list);
|
||||
};
|
||||
defineExpose({
|
||||
updataList,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
104
src/component/Canvas/test copy.vue
Normal file
104
src/component/Canvas/test copy.vue
Normal 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>
|
||||
@@ -1,121 +1,201 @@
|
||||
<template>
|
||||
<div class="test">
|
||||
<div v-for="(item, index) in data" :key="index">
|
||||
<div class="control">
|
||||
<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 class="control" v-for="(item, index) in list" :key="index">
|
||||
<div>
|
||||
<span>偏移X</span>
|
||||
<input type="number" v-model="item.location[0]" />
|
||||
</div>
|
||||
<div class="box">
|
||||
<pingpu v-bind="item" :style="{
|
||||
transform: `translate(${item.object.left}px, ${item.object.top}px) scale(${item.object.scale})`,
|
||||
transformOrigin: 'top left'
|
||||
}" />
|
||||
<div>
|
||||
<span>偏移Y</span>
|
||||
<input type="number" v-model="item.location[1]" />
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import pingpu from "./pingpu.vue";
|
||||
const data = ref([
|
||||
{
|
||||
url: "/src/assets/images/canvas/yinhua1.jpg",
|
||||
offsetX: 0, // px
|
||||
offsetY: 0, // px
|
||||
angle: 0, // 角度
|
||||
scale: 100, // %
|
||||
gapX: 0, // px
|
||||
gapY: 0, // px
|
||||
const convertDotNotationToBracket = (str) =>
|
||||
str.replace(/(?:^|\.)(\d+)(?=\.|$)/g, "[#$1]").replace(/\[#/g, "[");
|
||||
|
||||
object:{
|
||||
scale: 1, // %
|
||||
top: 0, // px
|
||||
left: 0, // px
|
||||
}
|
||||
const list = ref([
|
||||
{
|
||||
id: "1",
|
||||
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",
|
||||
offsetX: 0, // px
|
||||
offsetY: 0, // px
|
||||
angle: 0, // 角度
|
||||
scale: 100, // %
|
||||
gapX: 0, // px
|
||||
gapY: 0, // px
|
||||
|
||||
object:{
|
||||
scale: 1, // %
|
||||
top: 0, // px
|
||||
left: 0, // px
|
||||
}
|
||||
id: "2",
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||
location: [0, 0],
|
||||
scale: [2, 2],
|
||||
angle: -45,
|
||||
name: "Print2",
|
||||
priority: 1,
|
||||
object: {
|
||||
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>
|
||||
|
||||
<style lang='less' scoped>
|
||||
.test {
|
||||
display: flex;
|
||||
> div {
|
||||
width: 50%;
|
||||
> .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;
|
||||
}
|
||||
> .control {
|
||||
display: inline-block;
|
||||
border: 1px solid #000;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border-radius: 10px;
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
> span {
|
||||
width: 80px;
|
||||
}
|
||||
> input {
|
||||
padding-left: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
> .box {
|
||||
position: fixed;
|
||||
top: 40%;
|
||||
left: 10%;
|
||||
width: 300px;
|
||||
height: 500px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
}
|
||||
> .box {
|
||||
width: 400px;
|
||||
height: 500px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user