434 lines
10 KiB
Vue
434 lines
10 KiB
Vue
<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";
|
||
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);
|
||
};
|
||
|
||
const changeImageUrl = "/src/assets/work/1.PNG";
|
||
const loadImageUrlToLayer = async () => {
|
||
try {
|
||
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", // 设置底图包含在画布内
|
||
});
|
||
};
|
||
|
||
const canvasInit = () => {
|
||
console.log("画布初始化完成");
|
||
// 可以在这里执行一些初始化逻辑
|
||
// canvasEditor.value.changeFixedImage(clothingImageUrlInit, {
|
||
// imageMode: "contains", // 设置底图包含在画布内
|
||
// });
|
||
};
|
||
|
||
const isShowLeft = ref(true);
|
||
</script>
|
||
|
||
<template>
|
||
<div class="app-container-example">
|
||
<!-- 模拟左侧导航栏 -->
|
||
<div
|
||
class="app-wrapper-btns"
|
||
:class="{ hide: !isShowLeft }"
|
||
@click="isShowLeft = !isShowLeft"
|
||
>
|
||
<div class="app-btn">收起/展开</div>
|
||
</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"
|
||
@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 {
|
||
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;
|
||
width: 3.5rem;
|
||
height: 3.5rem;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: none;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
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;
|
||
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>
|