Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite

This commit is contained in:
X1627315083
2026-01-16 10:34:17 +08:00
7 changed files with 636 additions and 584 deletions

View File

@@ -7,6 +7,7 @@ import {
insertObjectAtZIndex,
removeCanvasObjectByObject,
createPatternTransform,
imageAddGapToCanvas,
} from "../utils/helper";
import { restoreFabricObject } from "../utils/objectHelper";
@@ -308,16 +309,8 @@ export class FillRepeatGapChangeCommand extends Command {
await image.decode();
object.fill_.width = image.width;
object.fill_.height = image.height;
// 创建透明 Canvas
const tcanvas = document.createElement('canvas');
tcanvas.width = image.width + object.fill_.gapX;
tcanvas.height = image.height + object.fill_.gapY;
const ctx = tcanvas.getContext('2d');
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
ctx.drawImage(image, 0, 0);
const fill = object.get("fill");
fill.source = tcanvas;
fill.source = imageAddGapToCanvas(image, object.fill_.gapX, object.fill_.gapY);
object.set("fill", new fabric.Pattern(fill));
this.canvas.renderAll();
return true;

View File

@@ -433,7 +433,7 @@
};
changeFill(obj, pattern);
};
// 改变填充便宜
// 改变填充偏移
const inputFillOffset = (value, obj) => {
if (!obj.oldPattern) obj.oldPattern = obj.get("fill");
const pattern = new fabric.Pattern({

View File

@@ -34,6 +34,7 @@ import {
createPatternTransform,
getTransformScaleAngle,
base64ToCanvas,
imageAddGapToCanvas,
} from "../utils/helper";
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
import { isFunction } from "lodash-es";
@@ -1630,38 +1631,57 @@ export class CanvasManager {
resolve(tcanvas);
}, { crossOrigin: "anonymous" });
})
let scaleX = fixedLayerObj.width / image.width * (item.scale?.[0] || 1) / 5;
let scaleY = fixedLayerObj.height / image.height * (item.scale?.[1] || 1) / 5;
let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX : scaleY;
let left = (item.location?.[0] || 0) - image.width * scale / 2
let top = (item.location?.[1] || 0) - image.height * scale / 2
let scaleX_ = fixedLayerObj.width / image.width * (item.scale?.[0] || 1) / 5;
let scaleY_ = fixedLayerObj.height / image.height * (item.scale?.[1] || 1) / 5;
let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX_ : scaleY_;
let offsetX = (item.location?.[0] || 0) - image.width * scale / 2
let offsetY = (item.location?.[1] || 0) - image.height * scale / 2
let top = fixedLayerObj.top - fixedLayerObj.height * fixedLayerObj.scaleY / 2
let left = fixedLayerObj.left - fixedLayerObj.width * fixedLayerObj.scaleX / 2
let scaleX = fixedLayerObj.scaleX
let scaleY = fixedLayerObj.scaleY
let opacity = 1
let angle = 0
let gapX = 0
let gapY = 0
let fillSource = image
if(item.object){
top += item.object.top * fixedLayerObj.scaleY
left += item.object.left * fixedLayerObj.scaleX
scaleX *= item.object.scaleX
scaleY *= item.object.scaleY
opacity = item.object.opacity
angle = item.object.angle
gapX = item.object.gapX
gapY = item.object.gapY
fillSource = imageAddGapToCanvas(image, gapX, gapY);
}
let rect = new fabric.Rect({
id: id,
layerId: id,
layerName: name,
width: fixedLayerObj.width,
height: fixedLayerObj.height,
top: fixedLayerObj.top - fixedLayerObj.height * fixedLayerObj.scaleY / 2,
left: fixedLayerObj.left - fixedLayerObj.width * fixedLayerObj.scaleX / 2,
scaleX: fixedLayerObj.scaleX,
scaleY: fixedLayerObj.scaleY,
top: top,
left: left,
scaleX: scaleX,
scaleY: scaleY,
globalCompositeOperation: BlendMode.MULTIPLY,
fill: new fabric.Pattern({
source: image,
source: fillSource,
repeat: "repeat",
patternTransform: createPatternTransform(scale, item.angle || 0),
offsetX: left, // 水平偏移
offsetY: top, // 垂直偏移
offsetX: offsetX, // 水平偏移
offsetY: offsetY, // 垂直偏移
}),
fill_ : {
source: item.path,
gapX: 0,
gapY: 0,
gapX: gapX,
gapY: gapY,
width: image.width,
height: image.height,
},
isPrintTrims: true,
// ...(item.object || {}),
});
this.canvas.add(rect);
let layer = createLayer({

View File

@@ -1094,3 +1094,21 @@ export function traceImageContour(canvas) {
return contour;
}
/**
* 图片添加gap转换
* @param {HTMLCanvasElement} image - img元素
* @param {Number} gapX - 水平gap
* @param {Number} gapY - 垂直gap
* @returns {HTMLCanvasElement} 转换后的canvas元素
*/
export function imageAddGapToCanvas(image, gapX, gapY) {
// 创建透明 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);
return tcanvas;
}

View File

@@ -139,6 +139,8 @@
<pingpu
:list="list"
ref="pingpuRef"
:width="600"
:height="900"
@change-canvas="updateCanvas"
/>
</div>
@@ -197,12 +199,12 @@
name: "Print2",
priority: 1,
object: {
top: 150,
left: 250,
top: 450,
left: 300,
scaleX: 0.5,
scaleY: 0.5,
opacity: 1,
angle: 45,
angle: 0,
flipX: false,
flipY: false,
blendMode: "multiply",

View File

@@ -33,6 +33,8 @@
const emit = defineEmits(["change-canvas", "init-canvas"]);
const props = defineProps({
list: { type: Array, default: () => [] },
width: { type: Number, required: true },
height: { type: Number, required: true },
});
const el = ref(null);
const canvasRef = ref(null);
@@ -106,13 +108,13 @@
token,
action: ACTIONS.UPDATE,
key: KEYS.O_TOP,
value: object.top,
value: (props.height / canvas.height) * object.top,
});
list.push({
token,
action: ACTIONS.UPDATE,
key: KEYS.O_LEFT,
value: object.left,
value: (props.width / canvas.width) * object.left,
});
if (action === "rotate") {
list.push({
@@ -188,6 +190,8 @@
height: cheight,
fill: pattern,
...item.object,
top: item.object.top / (props.height / canvas.height),
left: item.object.left / (props.width / canvas.width),
onDelete: (v) => onDeleteItem(v),
});
canvas.add(rect);
@@ -237,10 +241,10 @@
let value = item.value;
switch (item.key) {
case KEYS.O_TOP:
object.set("top", value);
object.set("top", value / (props.height / canvas.height));
break;
case KEYS.O_LEFT:
object.set("left", value);
object.set("left", value / (props.width / canvas.width));
break;
case KEYS.O_OPACITY:
object.set("opacity", value);

View File

@@ -1,18 +1,18 @@
<script setup>
import { ref } from "vue";
import CanvasEditor from "./CanvasEditor/index.vue";
import RedGreenModeExample from "./RedGreenModeExample.vue";
import ExistsImageList from "@/component/Canvas/ExistsImageList/index.vue";
import ToolButton from "@/component/Canvas/ExistsImageList/ToolButton.vue";
import { ref } from "vue";
import CanvasEditor from "./CanvasEditor/index.vue";
import RedGreenModeExample from "./RedGreenModeExample.vue";
import ExistsImageList from "@/component/Canvas/ExistsImageList/index.vue";
import ToolButton from "@/component/Canvas/ExistsImageList/ToolButton.vue";
// 当前显示的组件
const canvasEditor = ref();
const currentView = ref("canvasEditor"); // 默认显示红绿图示例 canvasEditor redGreenExample
// 当前显示的组件
const canvasEditor = ref();
const currentView = ref("canvasEditor"); // 默认显示红绿图示例 canvasEditor redGreenExample
const clothingImageUrl = "/src/assets/images/canvas/xiangao.png";
const clothingImageUrlInit = "/src/assets/images/canvas/xiangaofenge.png";
const clothingImageUrl = "/src/assets/images/canvas/xiangao.png";
const clothingImageUrlInit = "/src/assets/images/canvas/xiangaofenge.png";
const imageData = [
const imageData = [
{
name: "风景照片",
type: "风景",
@@ -48,27 +48,27 @@ const imageData = [
{ url: "/src/assets/work/IMG_0008.PNG", name: "肖像2" },
],
},
];
];
const handleImageSelect = (selectedImage) => {
const handleImageSelect = (selectedImage) => {
console.log("选中的图片:", selectedImage);
console.log(selectedImage)
console.log(selectedImage);
// selectedImage 包含url, name, categoryName, categoryType, originalItem
};
};
// 切换视图
function switchView(view) {
// 切换视图
function switchView(view) {
currentView.value = view;
}
}
// 定义编辑器配置
const editorConfig = {
// 定义编辑器配置
const editorConfig = {
width: 800,
height: 600,
backgroundColor: "#ffffff", // 画布背景色
};
};
const exportImage = async () => {
const exportImage = async () => {
if (canvasEditor.value) {
const base64 = await canvasEditor.value.exportImage({
isContainFixed: true, // 是否导出底图
@@ -85,12 +85,12 @@ const exportImage = async () => {
link.click(); // 触发下载
document.body.removeChild(link); // 下载后移除链接元素
}
};
// 导出颜色图层
const exportColorLayer = async () => {
};
// 导出颜色图层
const exportColorLayer = async () => {
if (canvasEditor.value) {
const colorLayer = await canvasEditor.value.exportColorLayer();
console.log("导出颜色图层:",colorLayer);
console.log("导出颜色图层:", colorLayer);
// 模拟下载图片
const link = document.createElement("a");
link.href = colorLayer.base64;
@@ -99,26 +99,26 @@ const exportColorLayer = async () => {
link.click(); // 触发下载
document.body.removeChild(link); // 下载后移除链接元素
}
};
};
// 导出所有信息
const exportExtraInfo = async () => {
// 导出所有信息
const exportExtraInfo = async () => {
if (canvasEditor.value) {
const extraInfo = await canvasEditor.value.exportExtraInfo();
console.log("==========导出信息:", extraInfo);
}
};
};
// 更新其他图层颜色
const updateOtherLayersColor = async () => {
// 更新其他图层颜色
const updateOtherLayersColor = async () => {
const obj = {
color: {rgba: {r:255,g:255,b:0,a:1}},
}
color: { rgba: { r: 255, g: 255, b: 0, a: 1 } },
};
await canvasEditor?.value?.updateOtherLayers?.(obj);
};
// 更新其他图层印花
const updateOtherLayersPrint = async () => {
// document.querySelector(".app-container").style.width = "50vw"
};
// 更新其他图层印花
const updateOtherLayersPrint = async () => {
// document.querySelector(".app-container").style.width = "50vw"
const obj = {
printObject: {
prints: [
@@ -131,18 +131,18 @@ const updateOtherLayersPrint = async () => {
scale: [0.3, 0.4],
angle: 0,
},
]
],
},
}
};
await canvasEditor?.value?.updateOtherLayers?.(obj);
};
};
const changeCanvas = (command) => {
const changeCanvas = (command) => {
console.log(command);
};
};
const changeImageUrl = "/src/assets/work/1.PNG";
const loadImageUrlToLayer = async () => {
const changeImageUrl = "/src/assets/work/1.PNG";
const loadImageUrlToLayer = async () => {
try {
const layerToLayer1 = canvasEditor?.value?.layers?.[0]?.id;
const layerId = await canvasEditor?.value?.addImageToLayer?.(
@@ -156,23 +156,23 @@ const loadImageUrlToLayer = async () => {
} catch (error) {
console.error("加载图片到图层失败:", error);
}
};
};
// 自定义工具方法
function exportAsPNG() {
// 自定义工具方法
function exportAsPNG() {
console.log("导出PNG");
// 实现导出PNG逻辑
exportImage();
}
}
function saveCanvas() {
function saveCanvas() {
console.log("保存项目");
// 实现保存画布逻辑
const json = canvasEditor.value.getJSON();
localStorage.setItem("canvasProject", json);
}
}
function canvasProject() {
function canvasProject() {
console.log("读取项目");
// 实现读取画布逻辑
const json = localStorage.getItem("canvasProject");
@@ -182,8 +182,8 @@ function canvasProject() {
} else {
console.warn("没有找到保存的画布项目");
}
}
const exportJSON = () => {
}
const exportJSON = () => {
console.log("导出JSON");
// 实现导出JSON逻辑
const json = canvasEditor.value.getJSON();
@@ -195,42 +195,41 @@ const exportJSON = () => {
a.download = "canvas_project.json";
a.click();
URL.revokeObjectURL(url);
};
// 复制JSON
const copyJSON = () => {
};
// 复制JSON
const copyJSON = () => {
console.log("复制JSON");
// 实现复制JSON逻辑
const json = canvasEditor.value.getJSON();
// 复制JSON到剪贴板
navigator.clipboard.writeText(json);
};
const getLayers = ()=>{
console.log("==========layers",canvasEditor.value?.layers)
}
// 处理自定义工具点击
const handleCustomToolClick = (tool) => {
};
const getLayers = () => {
console.log("==========layers", canvasEditor.value?.layers);
};
// 处理自定义工具点击
const handleCustomToolClick = (tool) => {
tool.action();
};
};
const changeFixedImage = () => {
const changeFixedImage = () => {
canvasEditor.value.changeFixedImage(changeImageUrl, {
imageMode: "contains", // 设置底图包含在画布内
});
};
};
const canvasInit = () => {
const canvasInit = () => {
console.log("画布初始化完成");
// 可以在这里执行一些初始化逻辑
// canvasEditor.value.changeFixedImage(clothingImageUrlInit, {
// imageMode: "contains", // 设置底图包含在画布内
// });
};
};
const frontBackChange =(value)=>{
}
const frontBackChange = (value) => {};
// 自定义工具配置相关
const customToolsList = ref([
// 自定义工具配置相关
const customToolsList = ref([
{
id: "exportColorLayer",
title: "导出颜色图层",
@@ -291,14 +290,14 @@ const customToolsList = ref([
{
id: "redGreenExample",
title: "红绿图模式",
action: () => switchView('redGreenExample'),
action: () => switchView("redGreenExample"),
label: "红",
class: "export-btn",
},
{
id: "canvasEditor",
title: "普通模式",
action: () => switchView('canvasEditor'),
action: () => switchView("canvasEditor"),
label: "普",
class: "export-btn",
},
@@ -330,41 +329,54 @@ const customToolsList = ref([
label: "查L",
class: "export-btn",
},
]);
const otherData = {
color: {rgba: {r:255,g:0,b:0,a:1}},
]);
const otherData = {
color: { rgba: { r: 255, g: 0, b: 0, a: 1 } },
printObject: {
prints: [
{
ifSingle: false,
level2Type: "Pattern",
designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg",
location: [250, 780],
scale: [1.2, 1.6],
angle: 0,
object: {
top: 600,
left: 800,
scaleX: 0.5,
scaleY: 0.5,
opacity: 1,
angle: 45,
flipX: false,
flipY: false,
blendMode: "multiply",
gapX: 0,
gapY: 0,
},
},
// {
// ifSingle: false,
// ifSingle: true,
// level2Type: "Pattern",
// designType: "Library",
// path: "/src/assets/images/canvas/yinhua1.jpg",
// location: [250, 780],
// scale: [0.3, 0.4],
// location: [550, 650],
// scale: [0.15, 0.2],
// angle: 0,
// },
{
ifSingle: true,
level2Type: "Pattern",
designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg",
location: [550, 650],
scale: [0.15, 0.2],
angle: 0,
// {
// ifSingle: true,
// level2Type: "Pattern",
// designType: "Library",
// path: "/src/assets/images/canvas/yinhua1.jpg",
// location: [700, 400],
// scale: [0.1, 0.133],
// angle: 0,
// },
],
},
{
ifSingle: true,
level2Type: "Pattern",
designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg",
location: [700, 400],
scale: [0.1, 0.133],
angle: 0,
}
]
},
}
};
</script>
<template>
@@ -405,7 +417,10 @@ const otherData = {
showFixedLayer
>
<template #existsImageList>
<ExistsImageList :list="imageData" @select="handleImageSelect" />
<ExistsImageList
:list="imageData"
@select="handleImageSelect"
/>
</template>
<!-- 使用插槽添加自定义工具栏按钮 -->
@@ -428,13 +443,13 @@ const otherData = {
</template>
<style scoped lang="less">
* {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}
.canvas-wrapper-btns {
.canvas-wrapper-btns {
position: fixed;
top: 10px;
left: 50%;
@@ -442,27 +457,27 @@ const otherData = {
z-index: 1000;
display: flex;
gap: 10px;
}
html,
body {
}
html,
body {
height: 100%;
overflow: hidden;
}
}
.app-container {
.app-container {
height: 100%;
// height: 100vh;
display: flex;
flex-direction: column;
}
}
.app-container-example {
.app-container-example {
height: 100vh;
display: flex;
flex-direction: row;
}
}
.app-wrapper-btns {
.app-wrapper-btns {
position: relative;
width: 200px;
height: 100vh;
@@ -475,9 +490,9 @@ body {
&.hide {
width: 20px;
}
}
}
.view-switcher {
.view-switcher {
display: flex;
gap: 8px;
@@ -501,9 +516,9 @@ body {
color: white;
}
}
}
}
.app-header {
.app-header {
position: fixed;
top: 10px;
right: 10px;
@@ -513,23 +528,23 @@ body {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
}
}
.app-content {
.app-content {
position: relative;
flex: 1;
height: 100vh;
}
}
/* 自定义工具按钮样式 */
.tool-separator {
/* 自定义工具按钮样式 */
.tool-separator {
width: 100%;
height: 1px;
background-color: #e0e0e0;
margin: 8px 0;
}
}
.custom-tool-btn {
.custom-tool-btn {
position: relative;
width: 3.5rem;
height: 3.5rem;
@@ -543,22 +558,22 @@ body {
font-size: 1.6rem;
color: #333;
transition: all 0.2s ease;
}
}
.custom-tool-btn:hover {
.custom-tool-btn:hover {
background-color: #f0f0f0;
}
}
.custom-tool-btn:hover .tool-tooltip {
.custom-tool-btn:hover .tool-tooltip {
display: block;
}
}
.tool-tooltip {
.tool-tooltip {
display: none;
pointer-events: none;
position: absolute;
writing-mode: vertical-rl; /* 竖直排列 */
text-orientation: upright; /* 保持文字正常显示 */// left: 100%;
text-orientation: upright; /* 保持文字正常显示 */ // left: 100%;
left: 50%;
top: -0.8rem;
transform: translate(-50%, -100%);
@@ -569,9 +584,9 @@ body {
white-space: nowrap;
font-size: 1.2rem;
z-index: 10;
}
}
.tool-tooltip:before {
.tool-tooltip:before {
content: "";
position: absolute;
left: 50%;
@@ -580,10 +595,10 @@ body {
border-width: 0.5rem;
border-style: solid;
border-color: rgba(0, 0, 0, 0.9) transparent transparent transparent;
}
}
/* 深色模式适配 */
@media (prefers-color-scheme: dark) {
/* 深色模式适配 */
@media (prefers-color-scheme: dark) {
.app-header {
background: rgba(45, 45, 45, 0.95);
@@ -603,5 +618,5 @@ body {
}
}
}
}
}
</style>