Files
aida_front/src/component/Canvas/canvasExample.vue

434 lines
10 KiB
Vue
Raw Normal View History

<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";
// 当前显示的组件
const canvasEditor = ref();
const currentView = ref("canvasEditor"); // 默认显示红绿图示例 canvasEditor redGreenExample
const clothingImageUrl = "/src/assets/work/3.PNG";
2025-06-29 23:29:47 +08:00
const clothingImageUrlInit = "/src/assets/work/5.PNG";
const imageData = [
{
name: "风景照片",
type: "风景",
imgList: [
{
url: "https://www.minio-api.aida.com.hk/aida-users/83/printboard/7a0cccfc-1e3c-4c38-af65-d6179f21a267.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20250723%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250723T025046Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=3612d6c168207bebdae2fa7c7c2c86ea0f48358b99b8b41dec9e2ff15e197e15",
name: "山景",
},
{ url: "/src/assets/work/2.PNG", name: "海景" },
],
},
{
name: "人物照片",
type: "人物",
imgList: [
{ url: "/src/assets/work/3.PNG", name: "肖像1" },
{ url: "/src/assets/work/5.PNG", name: "肖像2" },
],
},
{
name: "衣服照片",
type: "衣服",
imgList: [
{ url: "/src/assets/work/3.PNG", name: "肖像1" },
{ url: "/src/assets/work/5.PNG", name: "肖像2" },
],
},
{
name: "裤子照片",
type: "裤子",
imgList: [
{ url: "/src/assets/work/IMG_0001.PNG", name: "肖像1" },
{ url: "/src/assets/work/IMG_0008.PNG", name: "肖像2" },
],
},
];
const handleImageSelect = (selectedImage) => {
console.log("选中的图片:", selectedImage);
// selectedImage 包含url, name, categoryName, categoryType, originalItem
};
// 切换视图
function switchView(view) {
currentView.value = view;
}
// 定义编辑器配置
const editorConfig = {
width: 800,
height: 600,
backgroundColor: "#ffffff", // 画布背景色
};
const exportImage = async () => {
if (canvasEditor.value) {
const base64 = await canvasEditor.value.exportImage({
isContainFixed: true, // 是否导出底图
isContainBg: false, // 是否导出背景
});
// 模拟下载图片
const link = document.createElement("a");
link.href = base64;
link.download = "canvas_image.png"; // 设置下载文件名
document.body.appendChild(link);
link.click(); // 触发下载
document.body.removeChild(link); // 下载后移除链接元素
}
};
const changeCanvas = (command) => {
console.log(command);
};
2025-06-29 23:29:47 +08:00
const changeImageUrl = "/src/assets/work/1.PNG";
const loadImageUrlToLayer = async () => {
try {
2025-06-29 23:29:47 +08:00
const layerToLayer1 = canvasEditor?.value?.layers?.[0]?.id;
const layerId = await canvasEditor?.value?.addImageToLayer?.(
changeImageUrl,
{
layerId: layerToLayer1, // 指定添加到的图层ID
imageMode: "contains", // 设置图片包含在画布内
}
);
console.log("新图层ID", layerId);
} catch (error) {
console.error("加载图片到图层失败:", error);
}
};
// 自定义工具配置相关
const customToolsList = ref([
{
id: "exportPNG",
title: "导出PNG", //导出画布图片
action: exportAsPNG,
icon: { name: "CExport", size: "24" },
class: "export-btn",
},
{
id: "saveCanvas",
title: "保存画布",
action: saveCanvas,
icon: { name: "CBottom", size: "24" },
class: "save-btn",
},
{
id: "readCanvas",
title: "读取画布",
action: canvasProject,
icon: { name: "CMiniMap", size: "24" },
class: "clear-btn",
},
]);
// 自定义工具方法
function exportAsPNG() {
console.log("导出PNG");
// 实现导出PNG逻辑
exportImage();
}
function saveCanvas() {
console.log("保存项目");
// 实现保存画布逻辑
const json = canvasEditor.value.getJSON();
localStorage.setItem("canvasProject", json);
}
function canvasProject() {
console.log("读取项目");
// 实现读取画布逻辑
const json = localStorage.getItem("canvasProject");
if (json) {
console.log("读取的项目JSON", JSON.parse(json)?.layers);
canvasEditor.value.loadJSON(json);
} else {
console.warn("没有找到保存的画布项目");
}
}
// 处理自定义工具点击
const handleCustomToolClick = (tool) => {
tool.action();
};
const changeFixedImage = () => {
canvasEditor.value.changeFixedImage(changeImageUrl, {
imageMode: "contains", // 设置底图包含在画布内
});
};
2025-06-29 23:29:47 +08:00
const canvasInit = () => {
console.log("画布初始化完成");
// 可以在这里执行一些初始化逻辑
// canvasEditor.value.changeFixedImage(clothingImageUrlInit, {
// imageMode: "contains", // 设置底图包含在画布内
// });
2025-06-29 23:29:47 +08:00
};
const isShowLeft = ref(true);
</script>
<template>
<div class="app-container-example">
<!-- 模拟左侧导航栏 -->
2025-08-24 17:11:04 +08:00
<!-- <div
class="app-wrapper-btns"
:class="{ hide: !isShowLeft }"
@click="isShowLeft = !isShowLeft"
>
<div class="app-btn">收起/展开</div>
2025-08-24 17:11:04 +08:00
</div> -->
<!-- 内容区域 -->
<div class="app-content">
<!-- 红绿图模式示例 -->
<RedGreenModeExample
v-if="currentView === 'redGreenExample'"
key="redGreenExample"
>
</RedGreenModeExample>
<!-- 普通画布编辑器 -->
<CanvasEditor
ref="canvasEditor"
key="canvasEditor"
v-if="currentView === 'canvasEditor'"
:config="editorConfig"
:clothingImageUrl="clothingImageUrl"
:clothing-image-opts="{
imageMode: 'contains', // 设置底图包含在画布内
}"
@change-canvas="changeCanvas"
2025-06-29 23:29:47 +08:00
@canvas-init="canvasInit"
isFixedErasable
showFixedLayer
>
<template #existsImageList>
<ExistsImageList :list="imageData" @select="handleImageSelect" />
</template>
<!-- 使用插槽添加自定义工具栏按钮 -->
<template #customTools="{ toolButtonProps }">
<!-- 分隔线 -->
<div class="tool-separator"></div>
<!-- 自定义工具按钮 -->
<ToolButton
v-for="tool in customToolsList"
:key="tool.id"
:tool="tool"
:active-tool="toolButtonProps.activeTool"
@click="handleCustomToolClick"
/>
<!-- 也可以直接使用普通的按钮 -->
<div class="custom-tool-btn" @click="loadImageUrlToLayer">
<span>🎨</span>
<div class="tool-tooltip">添加画布图片</div>
</div>
<div class="custom-tool-btn" @click="switchView('redGreenExample')">
<span></span>
<div class="tool-tooltip">红绿图模式</div>
</div>
<div class="custom-tool-btn" @click="switchView('canvasEditor')">
<span></span>
<div class="tool-tooltip">普通模式</div>
</div>
<div class="custom-tool-btn" @click="changeFixedImage">
<span></span>
<div class="tool-tooltip">更换底图</div>
</div>
</template>
</CanvasEditor>
</div>
</div>
</template>
<style scoped lang="less">
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.canvas-wrapper-btns {
position: fixed;
top: 10px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
display: flex;
gap: 10px;
}
html,
body {
height: 100%;
overflow: hidden;
}
.app-container {
2025-07-24 09:26:27 +08:00
height: 100%;
// height: 100vh;
display: flex;
flex-direction: column;
}
.app-container-example {
height: 100vh;
display: flex;
flex-direction: row;
}
.app-wrapper-btns {
position: relative;
width: 200px;
height: 100vh;
background: #f8f9fa;
z-index: 1000;
transition: all 0.3s ease;
&.app-btn {
cursor: pointer;
}
&.hide {
width: 20px;
}
}
.view-switcher {
display: flex;
gap: 8px;
.switch-btn {
padding: 8px 16px;
border: 1px solid #ddd;
border-radius: 6px;
background: white;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
&:hover {
border-color: #007bff;
background: #f8f9fa;
}
&.active {
border-color: #007bff;
background: #007bff;
color: white;
}
}
}
.app-header {
position: fixed;
top: 10px;
right: 10px;
z-index: 1000;
background: rgba(255, 255, 255, 0.95);
padding: 8px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
}
.app-content {
position: relative;
flex: 1;
height: 100vh;
}
/* 自定义工具按钮样式 */
.tool-separator {
width: 100%;
height: 1px;
background-color: #e0e0e0;
margin: 8px 0;
}
.custom-tool-btn {
position: relative;
2025-07-24 20:15:39 +08:00
width: 3.5rem;
height: 3.5rem;
display: flex;
align-items: center;
justify-content: center;
background: none;
border: none;
border-radius: 4px;
cursor: pointer;
2025-07-24 20:15:39 +08:00
font-size: 1.6rem;
color: #333;
transition: all 0.2s ease;
}
.custom-tool-btn:hover {
background-color: #f0f0f0;
}
.custom-tool-btn:hover .tool-tooltip {
display: block;
}
.tool-tooltip {
display: none;
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 0.4rem 0.8rem;
border-radius: 0.4rem;
margin-left: 0.8rem;
white-space: nowrap;
2025-07-24 20:15:39 +08:00
font-size: 1.2rem;
z-index: 10;
}
.tool-tooltip:before {
content: "";
position: absolute;
top: 50%;
right: 100%;
margin-top: -0.5rem;
border-width: 0.5rem;
border-style: solid;
border-color: transparent rgba(0, 0, 0, 0.7) transparent transparent;
}
/* 深色模式适配 */
@media (prefers-color-scheme: dark) {
.app-header {
background: rgba(45, 45, 45, 0.95);
.view-switcher .switch-btn {
background: #3d3d3d;
border-color: #555;
color: #e0e0e0;
&:hover {
border-color: #007bff;
background: #444;
}
&.active {
background: #007bff;
color: white;
}
}
}
}
</style>