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

552 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");
2025-06-18 11:05:23 +08:00
const isShowLayerPanel = inject("isShowLayerPanel", ref(false));
2025-06-09 10:25:54 +08:00
const props = defineProps({
activeTool: String,
canvasWidth: Number,
canvasHeight: Number,
canvasColor: String,
brushSize: Number,
2025-06-18 11:05:23 +08:00
enabledRedGreenMode: Boolean,
2025-06-09 10:25:54 +08:00
});
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);
// 计算属性
2025-06-18 11:05:23 +08:00
// const shouldShowBrushSettings = computed(() => {
// return props.activeTool === OperationType.DRAW;
// });
2025-06-09 10:25:54 +08:00
2025-06-22 13:52:28 +08:00
function updateCanvasSize(
{ width, height } = { width: props.width, height: props.height }
) {
2025-06-09 10:25:54 +08:00
if (!layerManager) {
console.warn("LayerManager 未初始化,无法调整背景层尺寸");
return;
}
2025-06-22 13:52:28 +08:00
layerManager.resizeCanvas(width, height);
// // 检查画布上是否有除了背景层的其他元素
// 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(width, height);
// } else {
// // 只有背景层时使用普通调整命令
// layerManager.resizeCanvas(width, height);
// }
2025-06-09 10:25:54 +08:00
emit("canvas-size-change");
}
function updateCanvasColor() {
if (!layerManager) {
console.warn("LayerManager 未初始化,无法更改背景色");
return;
}
// 更新背景层颜色而不是画布颜色
layerManager.updateBackgroundColor(props.canvasColor);
emit("canvas-color-change");
}
// 切换笔刷面板显示状态
function toggleBrushPanel() {
2025-06-18 11:05:23 +08:00
// 如果笔刷没有激活 则激活笔刷工具
if (toolManager?.activeTool !== OperationType.DRAW) {
toolManager.setToolWithCommand(OperationType.DRAW);
}
2025-06-09 10:25:54 +08:00
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) {
2025-06-18 11:05:23 +08:00
// if (isShowLayerPanel.value) {
// // 如果点击的是图层面板或其内部元素,则不关闭
// if (event.target.closest(".layers-panel")) {
// return;
// }
// // 关闭图层面板
// isShowLayerPanel.value = false;
// }
if (showBrushPanel.value) {
// 检查是否点击了笔刷选择器按钮
if (event.target.closest(".brush-selector")) {
return;
}
// 检查是否点击了笔刷面板或其内部元素
if (event.target.closest(".brush-panel")) {
return;
}
// 如果都不是,则关闭面板
if (showBrushPanel.value) {
showBrushPanel.value = false;
}
2025-06-09 10:25:54 +08:00
}
}
2025-06-18 11:05:23 +08:00
function showLayerPanel() {
isShowLayerPanel.value = !isShowLayerPanel.value;
}
2025-06-09 10:25:54 +08:00
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">
2025-06-18 11:05:23 +08:00
<div class="canvas-header-wrapper">
<span class="canvas-title">Canvas</span>
<!-- 默认设置 -->
<!-- v-if="
2025-06-09 10:25:54 +08:00
!activeTool ||
activeTool === OperationType.SELECT ||
activeTool === OperationType.PAN
2025-06-18 11:05:23 +08:00
" -->
<div class="canvas-settings" v-if="!props.enabledRedGreenMode">
<div class="setting-group">
2025-06-22 13:52:28 +08:00
<span class="setting-label">{{ $t("宽度") }}</span>
<a-input-number
2025-06-18 11:05:23 +08:00
:value="canvasWidth"
class="setting-input"
2025-06-22 13:52:28 +08:00
:min="1"
:max="9999"
:step="1"
@change="
(value) => {
$emit('update:canvasWidth', value);
updateCanvasSize({ width: value, height: canvasHeight });
}
"
2025-06-18 11:05:23 +08:00
/>
</div>
<div class="setting-group">
2025-06-22 13:52:28 +08:00
<span class="setting-label">{{ $t("高度") }}</span>
<a-input-number
2025-06-18 11:05:23 +08:00
:value="canvasHeight"
class="setting-input"
2025-06-22 13:52:28 +08:00
:min="1"
:max="9999"
:step="1"
@change="
(value) => {
$emit('update:canvasHeight', value);
updateCanvasSize({ width: canvasWidth, height: value });
}
"
2025-06-18 11:05:23 +08:00
/>
</div>
<div class="setting-group">
2025-06-22 13:52:28 +08:00
<span class="setting-label">{{ $t("颜色") }}</span>
2025-06-18 11:05:23 +08:00
<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>
2025-06-09 10:25:54 +08:00
</div>
2025-06-18 11:05:23 +08:00
<!-- <div class="setting-group">
<span class="setting-label">颜色:</span>
2025-06-09 10:25:54 +08:00
<div class="color-picker-wrapper">
<input
type="color"
2025-06-18 11:05:23 +08:00
:value="BrushStore.state.color"
2025-06-09 10:25:54 +08:00
class="color-picker"
2025-06-18 11:05:23 +08:00
@input="BrushStore.setBrushColor($event.target.value)"
2025-06-09 10:25:54 +08:00
/>
<span class="color-dropdown"></span>
</div>
2025-06-18 11:05:23 +08:00
</div> -->
2025-06-09 10:25:54 +08:00
</div>
2025-06-18 11:05:23 +08:00
<!-- 导出设置 -->
<!-- <div class="setting-group export-group">
<span class="export-model-select">exportModel.select:</span>
<span class="export-model-dropdown"></span>
</div> -->
2025-06-09 10:25:54 +08:00
2025-06-18 11:05:23 +08:00
<!-- 绘图工具设置 -->
2025-06-22 13:52:28 +08:00
<div class="canvas-settings gap-20">
2025-06-18 11:05:23 +08:00
<div
class="btn"
:class="{ active: showBrushPanel }"
@click="toggleBrushPanel"
2025-06-22 13:52:28 +08:00
v-if="!props.enabledRedGreenMode"
2025-06-18 11:05:23 +08:00
>
<!-- <span class="setting-label">笔刷:</span>/ -->
<div class="brush-selector">
<SvgIcon name="CBrushTop" size="22"></SvgIcon>
<!-- <div
2025-06-09 10:25:54 +08:00
class="brush-preview"
:style="{
backgroundColor: BrushStore.state.color,
height: BrushStore.state.type === 'marker' ? '4px' : '2px',
opacity: BrushStore.state.opacity,
}"
2025-06-18 11:05:23 +08:00
></div> -->
<!-- <span class="brush-dropdown"></span> -->
2025-06-09 10:25:54 +08:00
</div>
<!-- 笔刷面板 -->
<div
v-if="showBrushPanel"
class="brush-panel-container"
ref="brushPanelRef"
>
2025-06-18 11:05:23 +08:00
<Teleport to="body">
<BrushPanel />
</Teleport>
2025-06-09 10:25:54 +08:00
</div>
</div>
2025-06-18 11:05:23 +08:00
<div
class="btn"
:class="{ active: isShowLayerPanel }"
@click="showLayerPanel"
>
<SvgIcon name="CLayout" size="26"></SvgIcon>
2025-06-09 10:25:54 +08:00
</div>
</div>
</div>
</template>
2025-06-18 11:05:23 +08:00
<style scoped lang="less">
2025-06-09 10:25:54 +08:00
.canvas-header {
display: flex;
align-items: center;
border-bottom: 1px solid #e0e0e0;
user-select: none;
2025-06-18 11:05:23 +08:00
height: 52px;
overflow: hidden;
width: 100%;
.canvas-header-wrapper {
display: flex;
align-items: center;
flex: auto;
}
2025-06-09 10:25:54 +08:00
}
.canvas-title {
font-size: 16px;
font-weight: 500;
margin-right: 30px;
display: flex;
align-items: center;
2025-06-18 11:05:23 +08:00
color: #333;
// &:before {
// // /* content: "⟳";
// // margin-right: 5px;
// // font-size: 14px; */
// }
2025-06-09 10:25:54 +08:00
}
.canvas-settings {
display: flex;
align-items: center;
2025-06-18 11:05:23 +08:00
gap: 8px;
color: #213547;
.btn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
background-color: #f0f0f0;
&.active,
&:active {
background-color: #e6f7ff;
color: #1890ff;
}
&:hover {
background-color: #e6f7ff;
// color: #1890ff;
}
}
}
.gap-20 {
gap: 20px;
2025-06-09 10:25:54 +08:00
}
.setting-group {
display: flex;
align-items: center;
gap: 5px;
position: relative;
}
.setting-label {
font-size: 14px;
color: #333;
}
.setting-input {
2025-06-22 13:52:28 +08:00
width: 80px;
}
.setting-input :deep(.ant-input-number-input) {
2025-06-09 10:25:54 +08:00
padding: 4px 8px;
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 {
2025-06-22 13:52:28 +08:00
display: block;
2025-06-09 10:25:54 +08:00
}
.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>