Files
aida_front/src/component/Canvas/CanvasEditor/components/HeaderMenu.vue
X1627315083 584f6a7db0 合并画布
2025-06-22 13:52:28 +08:00

552 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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 isShowLayerPanel = inject("isShowLayerPanel", ref(false));
const props = defineProps({
activeTool: String,
canvasWidth: Number,
canvasHeight: Number,
canvasColor: String,
brushSize: Number,
enabledRedGreenMode: Boolean,
});
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(
{ width, height } = { width: props.width, height: props.height }
) {
if (!layerManager) {
console.warn("LayerManager 未初始化,无法调整背景层尺寸");
return;
}
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);
// }
emit("canvas-size-change");
}
function updateCanvasColor() {
if (!layerManager) {
console.warn("LayerManager 未初始化,无法更改背景色");
return;
}
// 更新背景层颜色而不是画布颜色
layerManager.updateBackgroundColor(props.canvasColor);
emit("canvas-color-change");
}
// 切换笔刷面板显示状态
function toggleBrushPanel() {
// 如果笔刷没有激活 则激活笔刷工具
if (toolManager?.activeTool !== OperationType.DRAW) {
toolManager.setToolWithCommand(OperationType.DRAW);
}
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 (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;
}
}
}
function showLayerPanel() {
isShowLayerPanel.value = !isShowLayerPanel.value;
}
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">
<div class="canvas-header-wrapper">
<span class="canvas-title">Canvas</span>
<!-- 默认设置 -->
<!-- v-if="
!activeTool ||
activeTool === OperationType.SELECT ||
activeTool === OperationType.PAN
" -->
<div class="canvas-settings" v-if="!props.enabledRedGreenMode">
<div class="setting-group">
<span class="setting-label">{{ $t("宽度") }}</span>
<a-input-number
:value="canvasWidth"
class="setting-input"
:min="1"
:max="9999"
:step="1"
@change="
(value) => {
$emit('update:canvasWidth', value);
updateCanvasSize({ width: value, height: canvasHeight });
}
"
/>
</div>
<div class="setting-group">
<span class="setting-label">{{ $t("高度") }}</span>
<a-input-number
:value="canvasHeight"
class="setting-input"
:min="1"
:max="9999"
:step="1"
@change="
(value) => {
$emit('update:canvasHeight', value);
updateCanvasSize({ width: canvasWidth, height: value });
}
"
/>
</div>
<div class="setting-group">
<span class="setting-label">{{ $t("颜色") }}</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 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 class="setting-group export-group">
<span class="export-model-select">exportModel.select:</span>
<span class="export-model-dropdown"></span>
</div> -->
<!-- 绘图工具设置 -->
<div class="canvas-settings gap-20">
<div
class="btn"
:class="{ active: showBrushPanel }"
@click="toggleBrushPanel"
v-if="!props.enabledRedGreenMode"
>
<!-- <span class="setting-label">笔刷:</span>/ -->
<div class="brush-selector">
<SvgIcon name="CBrushTop" size="22"></SvgIcon>
<!-- <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"
>
<Teleport to="body">
<BrushPanel />
</Teleport>
</div>
</div>
<div
class="btn"
:class="{ active: isShowLayerPanel }"
@click="showLayerPanel"
>
<SvgIcon name="CLayout" size="26"></SvgIcon>
</div>
</div>
</div>
</template>
<style scoped lang="less">
.canvas-header {
display: flex;
align-items: center;
border-bottom: 1px solid #e0e0e0;
user-select: none;
height: 52px;
overflow: hidden;
width: 100%;
.canvas-header-wrapper {
display: flex;
align-items: center;
flex: auto;
}
}
.canvas-title {
font-size: 16px;
font-weight: 500;
margin-right: 30px;
display: flex;
align-items: center;
color: #333;
// &:before {
// // /* content: "⟳";
// // margin-right: 5px;
// // font-size: 14px; */
// }
}
.canvas-settings {
display: flex;
align-items: center;
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;
}
.setting-group {
display: flex;
align-items: center;
gap: 5px;
position: relative;
}
.setting-label {
font-size: 14px;
color: #333;
}
.setting-input {
width: 80px;
}
.setting-input :deep(.ant-input-number-input) {
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 {
display: block;
}
.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>