事件替换颜色等画布
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
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 { 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>
|
||||||
|
|||||||
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>
|
<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>
|
||||||
Reference in New Issue
Block a user