接入画布
This commit is contained in:
525
src/component/Canvas/CanvasEditor/components/HeaderMenu.vue
Normal file
525
src/component/Canvas/CanvasEditor/components/HeaderMenu.vue
Normal file
@@ -0,0 +1,525 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user