Files
aida_front/src/component/Canvas/CanvasEditor/components/HeaderMenu.vue

526 lines
13 KiB
Vue
Raw Normal View History

2025-06-09 10:25:54 +08:00
<script setup>
import {
inject,
ref,
provide,
onMounted,
computed,
watch,
onUnmounted,
} from "vue";
import { OperationType } from "../utils/layerHelper";
import BrushPanel from "./BrushPanel.vue";
import { BrushStore } from "../store/BrushStore";
// 提供brushStore给子组件
provide("brushStore", BrushStore);
const toolManager = inject("toolManager");
const layerManager = inject("layerManager");
const props = defineProps({
activeTool: String,
canvasWidth: Number,
canvasHeight: Number,
canvasColor: String,
brushSize: Number,
});
const emit = defineEmits([
"update:canvasWidth",
"update:canvasHeight",
"update:canvasColor",
"update:brushSize",
"canvas-size-change",
"canvas-color-change",
]);
// 笔刷面板相关状态
const showBrushPanel = ref(false);
const brushPanelRef = ref(null);
// 计算属性
const shouldShowBrushSettings = computed(() => {
return props.activeTool === OperationType.DRAW;
});
function updateCanvasSize() {
if (!layerManager) {
console.warn("LayerManager 未初始化,无法调整背景层尺寸");
return;
}
// 检查画布上是否有除了背景层的其他元素
const hasOtherElements = layerManager.layers.value.some((layer) => {
if (layer.isBackground) return false;
// 检查普通图层是否有对象
if (layer.fabricObjects && layer.fabricObjects.length > 0) return true;
// 检查固定图层是否有对象
if (layer.isFixed && layer.fabricObjects && layer.fabricObjects.length > 0)
return true;
return false;
});
if (hasOtherElements) {
// 有其他元素时使用等比缩放命令
layerManager.resizeCanvasWithScale(props.canvasWidth, props.canvasHeight);
} else {
// 只有背景层时使用普通调整命令
layerManager.resizeCanvas(props.canvasWidth, props.canvasHeight);
}
emit("canvas-size-change");
}
function updateCanvasColor() {
if (!layerManager) {
console.warn("LayerManager 未初始化,无法更改背景色");
return;
}
// 更新背景层颜色而不是画布颜色
layerManager.updateBackgroundColor(props.canvasColor);
emit("canvas-color-change");
}
// 切换笔刷面板显示状态
function toggleBrushPanel() {
showBrushPanel.value = !showBrushPanel.value;
}
// 处理笔刷大小变化
function handleBrushSizeChange(event) {
const newSize = parseFloat(event.target.value);
emit("update:brushSize", newSize);
}
// 处理笔刷设置变化将BrushStore的数据同步到brushManager
function syncBrushStoreToManager() {
if (!toolManager?.brushManager) return;
const brushManager = toolManager.brushManager;
// 检查画笔是否正在更新中
if (brushManager.isUpdatingBrush) {
console.warn("画笔正在更新中,请稍候...");
// 延迟重试,确保在画笔更新完成后应用最新设置
setTimeout(syncBrushStoreToManager, 100);
return;
}
// 监听BrushStore的变化更新brushManager
const size = BrushStore.state.size;
const color = BrushStore.state.color;
const type = BrushStore.state.type;
const opacity = BrushStore.state.opacity;
const textureEnabled = BrushStore.state.textureEnabled;
const texturePath = BrushStore.state.texturePath;
const textureScale = BrushStore.state.textureScale;
// 将所有更改一次性应用减少updateBrush调用次数
let needsUpdate = false;
if (
brushManager.brushSize &&
typeof brushManager.setBrushSize === "function" &&
brushManager.getBrushSize() !== size
) {
brushManager.brushSize.value = size; // 直接设置值避免触发updateBrush
needsUpdate = true;
}
if (
brushManager.brushColor &&
typeof brushManager.setBrushColor === "function" &&
brushManager.getBrushColor() !== color
) {
brushManager.brushColor.value = color; // 直接设置值避免触发updateBrush
needsUpdate = true;
}
if (
typeof brushManager.setBrushType === "function" &&
brushManager.getCurrentBrushType() !== type
) {
brushManager.setBrushType(type);
needsUpdate = true;
}
if (typeof brushManager.setBrushOpacity === "function") {
brushManager.setBrushOpacity(opacity);
needsUpdate = true;
}
// 同步材质相关设置
if (textureEnabled && texturePath) {
if (typeof brushManager.setTexturePath === "function") {
brushManager.setTexturePath(texturePath);
needsUpdate = true;
}
if (
typeof brushManager.setTextureScale === "function" &&
brushManager.getTextureScale() !== textureScale
) {
brushManager.textureScale.value = textureScale; // 直接设置值避免触发updateBrush
needsUpdate = true;
}
}
// 只在有变化时调用一次updateBrush减少重绘次数
if (needsUpdate && typeof brushManager.updateBrush === "function") {
brushManager.updateBrush();
}
}
// 点击外部时关闭笔刷面板
function handleClickOutside(event) {
if (
showBrushPanel.value &&
brushPanelRef.value &&
!brushPanelRef.value.contains(event.target) &&
!event.target.closest(".brush-selector")
) {
showBrushPanel.value = false;
}
}
onMounted(() => {
// 获取工具管理器和笔刷管理器
const brushManager = toolManager?.brushManager;
// 设置初始的可用笔刷类型
if (brushManager) {
const availableBrushes = brushManager.getBrushTypes();
BrushStore.setAvailableBrushes(availableBrushes);
// 初始化BrushStore与brushManager的数据同步
BrushStore.setBrushSize(brushManager.brushSize?.value || 5);
BrushStore.setBrushColor(brushManager.brushColor?.value || "#000000");
BrushStore.setBrushType(brushManager.getCurrentBrushType() || "pencil");
}
// 添加点击外部关闭面板的事件监听
document.addEventListener("mousedown", handleClickOutside);
// 监听BrushStore的变化同步到brushManager
const unwatch = watch(
() => [
BrushStore.state.size,
BrushStore.state.color,
BrushStore.state.type,
BrushStore.state.opacity,
BrushStore.state.textureEnabled,
BrushStore.state.texturePath,
BrushStore.state.textureScale,
],
syncBrushStoreToManager,
{ deep: true }
);
// 组件卸载时移除事件监听
onUnmounted(() => {
document.removeEventListener("mousedown", handleClickOutside);
unwatch();
});
});
</script>
<template>
<div class="canvas-header">
<span class="canvas-title">Canvas</span>
<!-- 默认设置 -->
<div
v-if="
!activeTool ||
activeTool === OperationType.SELECT ||
activeTool === OperationType.PAN
"
class="canvas-settings"
>
<div class="setting-group">
<span class="setting-label">Width</span>
<input
type="text"
:value="canvasWidth"
class="setting-input"
@input="$emit('update:canvasWidth', Number($event.target.value))"
@change="updateCanvasSize"
/>
</div>
<div class="setting-group">
<span class="setting-label">Height</span>
<input
type="text"
:value="canvasHeight"
class="setting-input"
@input="$emit('update:canvasHeight', Number($event.target.value))"
@change="updateCanvasSize"
/>
</div>
<div class="setting-group">
<span class="setting-label">Color</span>
<div class="color-picker-wrapper">
<input
type="color"
:value="canvasColor"
class="color-picker"
@input="$emit('update:canvasColor', $event.target.value)"
@change="updateCanvasColor"
/>
<span class="color-dropdown"></span>
</div>
</div>
</div>
<!-- 绘图工具设置 -->
<div v-if="shouldShowBrushSettings" class="canvas-settings">
<!-- 简化的笔刷控制UI -->
<!-- <div class="setting-group">
<span class="setting-label">大小:</span>
<input
type="range"
:value="BrushStore.state.size"
min="0.5"
max="100"
step="0.5"
class="size-slider"
@input="handleBrushSizeChange"
/>
<span class="size-value">{{ BrushStore.state.size }}px</span>
</div> -->
<div class="setting-group">
<span class="setting-label">笔刷:</span>
<div class="brush-selector" @click="toggleBrushPanel">
<div
class="brush-preview"
:style="{
backgroundColor: BrushStore.state.color,
height: BrushStore.state.type === 'marker' ? '4px' : '2px',
opacity: BrushStore.state.opacity,
}"
></div>
<span class="brush-dropdown"></span>
</div>
<!-- 笔刷面板 -->
<div
v-if="showBrushPanel"
class="brush-panel-container"
ref="brushPanelRef"
>
<BrushPanel />
</div>
</div>
<div class="setting-group">
<span class="setting-label">颜色:</span>
<div class="color-picker-wrapper">
<input
type="color"
:value="BrushStore.state.color"
class="color-picker"
@input="BrushStore.setBrushColor($event.target.value)"
/>
<span class="color-dropdown"></span>
</div>
</div>
</div>
<!-- 文本工具设置 -->
<div v-if="activeTool === OperationType.TEXT" class="canvas-settings">
<div class="setting-group">
<span class="setting-label">Font:</span>
<select class="font-select">
<option value="Arial">Arial</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Courier New">Courier New</option>
</select>
</div>
<div class="setting-group">
<span class="setting-label">Size:</span>
<input
type="number"
class="setting-input"
value="16"
min="8"
max="72"
/>
</div>
<div class="setting-group">
<span class="setting-label">Color:</span>
<div class="color-picker-wrapper">
<input type="color" class="color-picker" value="#000000" />
<span class="color-dropdown"></span>
</div>
</div>
</div>
<!-- 上传工具设置 -->
<div v-if="activeTool === OperationType.UPLOAD" class="canvas-settings">
<div class="setting-group">
<span class="setting-label">Upload Type:</span>
<select class="setting-select">
<option value="image">Image</option>
<option value="vector">Vector Graphics</option>
</select>
</div>
</div>
<!-- 导出设置 -->
<div class="setting-group export-group">
<span class="export-model-select">exportModel.select:</span>
<span class="export-model-dropdown"></span>
</div>
</div>
</template>
<style scoped>
.canvas-header {
display: flex;
align-items: center;
padding: 10px 20px;
border-bottom: 1px solid #e0e0e0;
user-select: none;
}
.canvas-title {
font-size: 16px;
font-weight: 500;
margin-right: 30px;
display: flex;
align-items: center;
}
.canvas-title::before {
content: "⟳";
margin-right: 5px;
font-size: 14px;
}
.canvas-settings {
display: flex;
align-items: center;
gap: 15px;
flex: 1;
}
.setting-group {
display: flex;
align-items: center;
gap: 5px;
position: relative;
}
.setting-label {
font-size: 14px;
color: #333;
}
.setting-input {
width: 60px;
padding: 4px 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.setting-select {
padding: 4px 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.font-select {
width: 150px;
padding: 4px 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.color-picker-wrapper {
position: relative;
display: flex;
align-items: center;
}
.color-picker {
width: 30px;
height: 30px;
border: none;
padding: 0;
background: none;
cursor: pointer;
}
.color-dropdown {
font-size: 10px;
margin-left: 5px;
color: #666;
}
.size-slider {
width: 100px;
cursor: pointer;
}
.size-value {
font-size: 12px;
color: #666;
min-width: 30px;
}
.brush-selector {
display: flex;
align-items: center;
border: 1px solid #ddd;
border-radius: 4px;
padding: 5px;
cursor: pointer;
background-color: white;
width: 80px;
justify-content: space-between;
}
.brush-preview {
width: 20px;
height: 2px;
background-color: #000;
border-radius: 1px;
}
.brush-dropdown,
.export-model-dropdown {
font-size: 10px;
margin-left: 5px;
color: #666;
}
.export-model-select {
font-size: 14px;
color: #333;
cursor: pointer;
}
.export-group {
margin-left: auto;
}
/* 笔刷面板 */
.brush-panel-container {
position: absolute;
top: calc(100% + 5px);
left: 0;
z-index: 1000;
width: 600px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
border-radius: 4px;
overflow: hidden;
}
</style>