feat: 修改选区面板样式 多语言完善
This commit is contained in:
@@ -10,24 +10,33 @@
|
|||||||
|
|
||||||
<div class="tool-types">
|
<div class="tool-types">
|
||||||
<div
|
<div
|
||||||
:class="['tool-btn', { active: selectionType === OperationType.LASSO }]"
|
:class="[
|
||||||
|
'tool-btn',
|
||||||
|
{ active: selectionType === OperationType.LASSO },
|
||||||
|
]"
|
||||||
@click="setSelectionType(OperationType.LASSO)"
|
@click="setSelectionType(OperationType.LASSO)"
|
||||||
>
|
>
|
||||||
<svg-icon name="CFree" size="26" />
|
<svg-icon name="CFree" size="20" />
|
||||||
<span>{{ $t("手绘") }}</span>
|
<span>{{ $t("手绘") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
:class="['tool-btn', { active: selectionType === OperationType.LASSO_RECTANGLE }]"
|
:class="[
|
||||||
|
'tool-btn',
|
||||||
|
{ active: selectionType === OperationType.LASSO_RECTANGLE },
|
||||||
|
]"
|
||||||
@click="setSelectionType(OperationType.LASSO_RECTANGLE)"
|
@click="setSelectionType(OperationType.LASSO_RECTANGLE)"
|
||||||
>
|
>
|
||||||
<svg-icon name="CRectangle" size="32" />
|
<svg-icon name="CRectangle" size="26" />
|
||||||
<span>{{ $t("矩形") }}</span>
|
<span>{{ $t("矩形") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
:class="['tool-btn', { active: selectionType === OperationType.LASSO_ELLIPSE }]"
|
:class="[
|
||||||
|
'tool-btn',
|
||||||
|
{ active: selectionType === OperationType.LASSO_ELLIPSE },
|
||||||
|
]"
|
||||||
@click="setSelectionType(OperationType.LASSO_ELLIPSE)"
|
@click="setSelectionType(OperationType.LASSO_ELLIPSE)"
|
||||||
>
|
>
|
||||||
<svg-icon name="CEllipse" size="30" />
|
<svg-icon name="CEllipse" size="24" />
|
||||||
<span>{{ $t("椭圆") }}</span>
|
<span>{{ $t("椭圆") }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,15 +47,15 @@
|
|||||||
<!-- 底部选区操作工具栏 -->
|
<!-- 底部选区操作工具栏 -->
|
||||||
<div class="tool-actions">
|
<div class="tool-actions">
|
||||||
<div class="action-btn" @click="copySelectionToNewLayer">
|
<div class="action-btn" @click="copySelectionToNewLayer">
|
||||||
<svg-icon name="CPaste" size="20" />
|
<svg-icon name="CPaste" size="16" />
|
||||||
<span class="btn-text">{{ $t("创建") }}</span>
|
<span class="btn-text">{{ $t("创建") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-btn" @click="cutSelectionToNewLayer">
|
<div class="action-btn" @click="cutSelectionToNewLayer">
|
||||||
<svg-icon name="CCut" size="30" />
|
<svg-icon name="CCut" size="26" />
|
||||||
<span class="btn-text">{{ $t("创建并拷贝") }}</span>
|
<span class="btn-text">{{ $t("创建并拷贝") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-btn" @click="clearSelectionContent">
|
<div class="action-btn" @click="clearSelectionContent">
|
||||||
<svg-icon name="CClear" size="22" />
|
<svg-icon name="CClear" size="18" />
|
||||||
<span class="btn-text">{{ $t("清除选择内容") }}</span>
|
<span class="btn-text">{{ $t("清除选择内容") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- <button
|
<!-- <button
|
||||||
@@ -150,7 +159,9 @@
|
|||||||
<div class="dialog-container">
|
<div class="dialog-container">
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
<h3>{{ $t("选择填充颜色") }}</h3>
|
<h3>{{ $t("选择填充颜色") }}</h3>
|
||||||
<button class="close-dialog-btn" @click="cancelColorPicker">×</button>
|
<button class="close-dialog-btn" @click="cancelColorPicker">
|
||||||
|
×
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<input type="color" v-model="fillColor" class="color-picker" />
|
<input type="color" v-model="fillColor" class="color-picker" />
|
||||||
@@ -311,7 +322,8 @@ function setSelectionType(type) {
|
|||||||
*/
|
*/
|
||||||
function checkSelectionStatus() {
|
function checkSelectionStatus() {
|
||||||
hasSelection.value =
|
hasSelection.value =
|
||||||
props.selectionManager && props.selectionManager.getSelectionObject() !== null;
|
props.selectionManager &&
|
||||||
|
props.selectionManager.getSelectionObject() !== null;
|
||||||
|
|
||||||
// 同步羽化值
|
// 同步羽化值
|
||||||
if (hasSelection.value) {
|
if (hasSelection.value) {
|
||||||
@@ -610,15 +622,15 @@ function confirmColorPicker() {
|
|||||||
background-color: rgba(0, 0, 0, 0.05);
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 10px 5px;
|
padding: 6px;
|
||||||
color: #333;
|
color: #333;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-btn span {
|
.tool-btn span {
|
||||||
margin-top: 6px;
|
margin-top: 0;
|
||||||
font-size: 14px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-btn svg {
|
.tool-btn svg {
|
||||||
@@ -638,7 +650,7 @@ function confirmColorPicker() {
|
|||||||
.toolbar-divider {
|
.toolbar-divider {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: rgba(0, 0, 0, 0.05);
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
margin: 0 10px 10px;
|
margin: 0 10px 5px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-actions {
|
.tool-actions {
|
||||||
@@ -674,13 +686,19 @@ function confirmColorPicker() {
|
|||||||
|
|
||||||
.action-btn {
|
.action-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
// flex-direction: column;
|
||||||
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: #333;
|
color: #333;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 8px 2px;
|
padding: 0;
|
||||||
|
gap: 4px;
|
||||||
|
.c-svg {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn svg {
|
.action-btn svg {
|
||||||
@@ -690,7 +708,8 @@ function confirmColorPicker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-text {
|
.btn-text {
|
||||||
font-size: 14px;
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,9 +844,7 @@ function confirmColorPicker() {
|
|||||||
|
|
||||||
.fade-enter-active,
|
.fade-enter-active,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition:
|
transition: opacity 0.3s, transform 0.3s;
|
||||||
opacity 0.3s,
|
|
||||||
transform 0.3s;
|
|
||||||
}
|
}
|
||||||
.fade-enter-from,
|
.fade-enter-from,
|
||||||
.fade-leave-to {
|
.fade-leave-to {
|
||||||
|
|||||||
@@ -34,12 +34,16 @@ import SelectionPanel from "./components/SelectionPanel.vue"; // 引入选区面
|
|||||||
import { LayerType, OperationType } from "./utils/layerHelper.js";
|
import { LayerType, OperationType } from "./utils/layerHelper.js";
|
||||||
import { ToolManager } from "./managers/toolManager.js";
|
import { ToolManager } from "./managers/toolManager.js";
|
||||||
// import { fabric } from "fabric-with-all";
|
// import { fabric } from "fabric-with-all";
|
||||||
import { uploadImageAndCreateLayer, loadImageUrlToLayer, loadImage } from "./utils/imageHelper.js";
|
import {
|
||||||
|
uploadImageAndCreateLayer,
|
||||||
|
loadImageUrlToLayer,
|
||||||
|
loadImage,
|
||||||
|
} from "./utils/imageHelper.js";
|
||||||
// import MinimapPanel from "./components/MinimapPanel.vue";
|
// import MinimapPanel from "./components/MinimapPanel.vue";
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from "vue-i18n";
|
||||||
const {t} = useI18n()
|
const { t } = useI18n();
|
||||||
const KeyboardShortcutHelp = defineAsyncComponent(
|
const KeyboardShortcutHelp = defineAsyncComponent(() =>
|
||||||
() => import("./components/KeyboardShortcutHelp.vue")
|
import("./components/KeyboardShortcutHelp.vue")
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
@@ -230,6 +234,7 @@ onMounted(async () => {
|
|||||||
activeLayerId,
|
activeLayerId,
|
||||||
canvasManager, // 添加对 canvasManager 的引用
|
canvasManager, // 添加对 canvasManager 的引用
|
||||||
commandManager, // 添加对命令管理器的引用
|
commandManager, // 添加对命令管理器的引用
|
||||||
|
t, // 国际化函数
|
||||||
});
|
});
|
||||||
|
|
||||||
// commandManager.setLayerManager(layerManager); // 设置命令管理器需要访问的图层数据
|
// commandManager.setLayerManager(layerManager); // 设置命令管理器需要访问的图层数据
|
||||||
@@ -321,7 +326,11 @@ onMounted(async () => {
|
|||||||
await layerManager.initializeLayers();
|
await layerManager.initializeLayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.enabledRedGreenMode && props.clothingImageUrl && props.redGreenImageUrl) {
|
if (
|
||||||
|
props.enabledRedGreenMode &&
|
||||||
|
props.clothingImageUrl &&
|
||||||
|
props.redGreenImageUrl
|
||||||
|
) {
|
||||||
canvasManager.canvas.fill = "#fff"; // 设置画布背景色为白色 // 初始化红绿图模式管理器
|
canvasManager.canvas.fill = "#fff"; // 设置画布背景色为白色 // 初始化红绿图模式管理器
|
||||||
redGreenModeManager = new RedGreenModeManager({
|
redGreenModeManager = new RedGreenModeManager({
|
||||||
canvas: canvasManager.canvas,
|
canvas: canvasManager.canvas,
|
||||||
@@ -368,7 +377,10 @@ onMounted(async () => {
|
|||||||
console.error("更换底图失败:", error);
|
console.error("更换底图失败:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
canvasManager?.centerBackgroundLayer?.(canvasManager.canvas.width, canvasManager.canvas.height);
|
canvasManager?.centerBackgroundLayer?.(
|
||||||
|
canvasManager.canvas.width,
|
||||||
|
canvasManager.canvas.height
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 设置固定图层是否可擦除
|
// // 设置固定图层是否可擦除
|
||||||
@@ -516,7 +528,7 @@ function updateCanvasColor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addLayer() {
|
async function addLayer() {
|
||||||
await layerManager.createLayer(t('Canvas.EmptyLayer'));
|
await layerManager.createLayer(t("Canvas.EmptyLayer"));
|
||||||
}
|
}
|
||||||
async function addTopLayer() {
|
async function addTopLayer() {
|
||||||
await layerManager.createLayer("空图层", LayerType.EMPTY, {
|
await layerManager.createLayer("空图层", LayerType.EMPTY, {
|
||||||
@@ -555,7 +567,9 @@ function moveLayerDown(layerId) {
|
|||||||
function removeLayer(layerId) {
|
function removeLayer(layerId) {
|
||||||
// Check if this is the last layer - prevent deletion
|
// Check if this is the last layer - prevent deletion
|
||||||
if (layers.value.length <= 2) {
|
if (layers.value.length <= 2) {
|
||||||
console.warn("Cannot delete the last layer. At least one layer must remain.");
|
console.warn(
|
||||||
|
"Cannot delete the last layer. At least one layer must remain."
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,7 +675,9 @@ function handleLayersReorder(reorderData) {
|
|||||||
const success = layerManager.reorderLayers(oldIndex, newIndex, layerId);
|
const success = layerManager.reorderLayers(oldIndex, newIndex, layerId);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
console.log(`图层 ${layerId} 已从位置 ${oldIndex} 移动到位置 ${newIndex}`);
|
console.log(
|
||||||
|
`图层 ${layerId} 已从位置 ${oldIndex} 移动到位置 ${newIndex}`
|
||||||
|
);
|
||||||
|
|
||||||
// 更新画布渲染顺序
|
// 更新画布渲染顺序
|
||||||
if (canvasManager) {
|
if (canvasManager) {
|
||||||
@@ -678,7 +694,12 @@ function handleChildLayersReorder(reorderData) {
|
|||||||
const { parentId, oldIndex, newIndex, layerId } = reorderData;
|
const { parentId, oldIndex, newIndex, layerId } = reorderData;
|
||||||
|
|
||||||
if (layerManager && layerManager.reorderChildLayers) {
|
if (layerManager && layerManager.reorderChildLayers) {
|
||||||
const success = layerManager.reorderChildLayers(parentId, oldIndex, newIndex, layerId);
|
const success = layerManager.reorderChildLayers(
|
||||||
|
parentId,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
layerId
|
||||||
|
);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -838,7 +859,8 @@ defineExpose({
|
|||||||
* @returns {Object} 优化结果统计
|
* @returns {Object} 优化结果统计
|
||||||
*/
|
*/
|
||||||
optimizeLayerStructure() {
|
optimizeLayerStructure() {
|
||||||
if (!layerManager) return { removedEmptyLayers: 0, mergedLayers: 0, reorderedLayers: 0 };
|
if (!layerManager)
|
||||||
|
return { removedEmptyLayers: 0, mergedLayers: 0, reorderedLayers: 0 };
|
||||||
return layerManager.optimizeLayerStructure();
|
return layerManager.optimizeLayerStructure();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -951,7 +973,10 @@ defineExpose({
|
|||||||
<!-- <MinimapPanel v-if="minimapEnabled" :minimapManager="minimapManager" /> -->
|
<!-- <MinimapPanel v-if="minimapEnabled" :minimapManager="minimapManager" /> -->
|
||||||
|
|
||||||
<!-- 笔刷控制面板 -->
|
<!-- 笔刷控制面板 -->
|
||||||
<BrushControlPanel v-if="canvasManagerLoaded" :activeTool="activeTool" />
|
<BrushControlPanel
|
||||||
|
v-if="canvasManagerLoaded"
|
||||||
|
:activeTool="activeTool"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- 文本编辑面板 -->
|
<!-- 文本编辑面板 -->
|
||||||
<TextEditorPanel
|
<TextEditorPanel
|
||||||
@@ -982,9 +1007,15 @@ defineExpose({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="zoom-info">
|
<div class="zoom-info">
|
||||||
{{ t('Canvas.Scale') }}: {{ currentZoom }}%
|
{{ t("Canvas.Scale") }}: {{ currentZoom }}%
|
||||||
<button class="reset-zoom" @click="resetZoom">{{ $t('Canvas.ResetLayer') }}</button>
|
<button class="reset-zoom" @click="resetZoom">
|
||||||
<button class="help-btn" @click="toggleShortcutHelp" :title="$t('Canvas.Help')">
|
{{ $t("Canvas.ResetLayer") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="help-btn"
|
||||||
|
@click="toggleShortcutHelp"
|
||||||
|
:title="$t('Canvas.Help')"
|
||||||
|
>
|
||||||
?
|
?
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1024,7 +1055,11 @@ defineExpose({
|
|||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<!-- 快捷键帮助模态框 -->
|
<!-- 快捷键帮助模态框 -->
|
||||||
<div v-if="showShortcutHelp" class="modal-overlay" @click="showShortcutHelp = false">
|
<div
|
||||||
|
v-if="showShortcutHelp"
|
||||||
|
class="modal-overlay"
|
||||||
|
@click="showShortcutHelp = false"
|
||||||
|
>
|
||||||
<div class="modal-content" @click.stop>
|
<div class="modal-content" @click.stop>
|
||||||
<button class="close-modal" @click="showShortcutHelp = false">×</button>
|
<button class="close-modal" @click="showShortcutHelp = false">×</button>
|
||||||
<KeyboardShortcutHelp />
|
<KeyboardShortcutHelp />
|
||||||
@@ -1101,20 +1136,30 @@ defineExpose({
|
|||||||
--offsetY: 0px;
|
--offsetY: 0px;
|
||||||
--size: 8px;
|
--size: 8px;
|
||||||
--color: #dedcdc;
|
--color: #dedcdc;
|
||||||
background-image:
|
background-image: -webkit-linear-gradient(
|
||||||
-webkit-linear-gradient(
|
|
||||||
45deg,
|
45deg,
|
||||||
var(--color) 25%,
|
var(--color) 25%,
|
||||||
transparent 0,
|
transparent 0,
|
||||||
transparent 75%,
|
transparent 75%,
|
||||||
var(--color) 0
|
var(--color) 0
|
||||||
),
|
),
|
||||||
-webkit-linear-gradient(45deg, var(--color) 25%, transparent 0, transparent 75%, var(--color) 0);
|
-webkit-linear-gradient(45deg, var(--color) 25%, transparent 0, transparent
|
||||||
background-image:
|
75%, var(--color) 0);
|
||||||
linear-gradient(45deg, var(--color) 25%, transparent 0, transparent 75%, var(--color) 0),
|
background-image: linear-gradient(
|
||||||
linear-gradient(45deg, var(--color) 25%, transparent 0, transparent 75%, var(--color) 0);
|
45deg,
|
||||||
background-position:
|
var(--color) 25%,
|
||||||
var(--offsetX) var(--offsetY),
|
transparent 0,
|
||||||
|
transparent 75%,
|
||||||
|
var(--color) 0
|
||||||
|
),
|
||||||
|
linear-gradient(
|
||||||
|
45deg,
|
||||||
|
var(--color) 25%,
|
||||||
|
transparent 0,
|
||||||
|
transparent 75%,
|
||||||
|
var(--color) 0
|
||||||
|
);
|
||||||
|
background-position: var(--offsetX) var(--offsetY),
|
||||||
calc(var(--size) + var(--offsetX)) calc(var(--size) + var(--offsetY));
|
calc(var(--size) + var(--offsetX)) calc(var(--size) + var(--offsetY));
|
||||||
background-size: calc(var(--size) * 2) calc(var(--size) * 2);
|
background-size: calc(var(--size) * 2) calc(var(--size) * 2);
|
||||||
}
|
}
|
||||||
@@ -1334,9 +1379,7 @@ button:hover {
|
|||||||
// 淡入淡出动画
|
// 淡入淡出动画
|
||||||
.fade-enter-active,
|
.fade-enter-active,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition:
|
transition: opacity 0.3s, transform 0.3s;
|
||||||
opacity 0.3s,
|
|
||||||
transform 0.3s;
|
|
||||||
}
|
}
|
||||||
.fade-enter-from,
|
.fade-enter-from,
|
||||||
.fade-leave-to {
|
.fade-leave-to {
|
||||||
|
|||||||
@@ -52,18 +52,27 @@ import {
|
|||||||
} from "../commands/RasterizeLayerCommand";
|
} from "../commands/RasterizeLayerCommand";
|
||||||
|
|
||||||
// 导入图层排序相关类和混入
|
// 导入图层排序相关类和混入
|
||||||
import { LayerSort, createLayerSort, LayerSortMixin, LayerSortUtils } from "../utils/LayerSort";
|
import {
|
||||||
|
LayerSort,
|
||||||
|
createLayerSort,
|
||||||
|
LayerSortMixin,
|
||||||
|
LayerSortUtils,
|
||||||
|
} from "../utils/LayerSort";
|
||||||
|
|
||||||
import CanvasConfig from "../config/canvasConfig";
|
import CanvasConfig from "../config/canvasConfig";
|
||||||
import { isBoolean, template } from "lodash-es";
|
import { isBoolean, template } from "lodash-es";
|
||||||
import { findObjectById, generateId, optimizeCanvasRendering } from "../utils/helper";
|
import {
|
||||||
|
findObjectById,
|
||||||
|
generateId,
|
||||||
|
optimizeCanvasRendering,
|
||||||
|
} from "../utils/helper";
|
||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import { fabric } from "fabric-with-all";
|
import { fabric } from "fabric-with-all";
|
||||||
import { getOriginObjectInfo } from "../utils/layerUtils";
|
import { getOriginObjectInfo } from "../utils/layerUtils";
|
||||||
import { restoreFabricObject } from "../utils/objectHelper";
|
import { restoreFabricObject } from "../utils/objectHelper";
|
||||||
import { UpdateGroupMaskPositionCommand } from "../commands/UpdateGroupMaskPositionCommand";
|
import { UpdateGroupMaskPositionCommand } from "../commands/UpdateGroupMaskPositionCommand";
|
||||||
import { useI18n } from 'vue-i18n'
|
// import { useI18n } from 'vue-i18n'
|
||||||
const {t} = useI18n()
|
// const {t} = useI18n()
|
||||||
/**
|
/**
|
||||||
* 图层管理器 - 负责管理画布上的所有图层
|
* 图层管理器 - 负责管理画布上的所有图层
|
||||||
* 包含图层的创建、删除、修改、排序等操作
|
* 包含图层的创建、删除、修改、排序等操作
|
||||||
@@ -83,6 +92,7 @@ export class LayerManager {
|
|||||||
* @param {Number} options.canvasWidth 画布宽度
|
* @param {Number} options.canvasWidth 画布宽度
|
||||||
* @param {Number} options.canvasHeight 画布高度
|
* @param {Number} options.canvasHeight 画布高度
|
||||||
* @param {String} options.backgroundColor 背景颜色
|
* @param {String} options.backgroundColor 背景颜色
|
||||||
|
* @param {Function} options.t 国际化函数
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.canvas = options.canvas;
|
this.canvas = options.canvas;
|
||||||
@@ -91,6 +101,7 @@ export class LayerManager {
|
|||||||
this.commandManager = options.commandManager;
|
this.commandManager = options.commandManager;
|
||||||
this.canvasManager = options.canvasManager || null;
|
this.canvasManager = options.canvasManager || null;
|
||||||
this.lastSelectLayerId = options.lastSelectLayerId || { value: null }; // 上次选中的图层ID
|
this.lastSelectLayerId = options.lastSelectLayerId || { value: null }; // 上次选中的图层ID
|
||||||
|
this.t = options.t || ((key) => key); // 国际化函数,默认为直接返回key
|
||||||
|
|
||||||
this.backgroundFillManager = new BackgroundFillManager({
|
this.backgroundFillManager = new BackgroundFillManager({
|
||||||
canvas: this.canvas,
|
canvas: this.canvas,
|
||||||
@@ -221,7 +232,11 @@ export class LayerManager {
|
|||||||
// 基于 fabric-with-erasing 库的 erasable 属性设置擦除权限
|
// 基于 fabric-with-erasing 库的 erasable 属性设置擦除权限
|
||||||
// 只有活动图层、可见、非锁定、非背景、非固定图层的对象才可擦除
|
// 只有活动图层、可见、非锁定、非背景、非固定图层的对象才可擦除
|
||||||
obj.erasable =
|
obj.erasable =
|
||||||
isInActiveLayer && layer.visible && !layer.locked && !layer.isBackground && !layer.isFixed;
|
isInActiveLayer &&
|
||||||
|
layer.visible &&
|
||||||
|
!layer.locked &&
|
||||||
|
!layer.isBackground &&
|
||||||
|
!layer.isFixed;
|
||||||
|
|
||||||
// 图层状态决定交互性
|
// 图层状态决定交互性
|
||||||
if (layer.isBackground || obj.isBackground || layer.isFixed) {
|
if (layer.isBackground || obj.isBackground || layer.isFixed) {
|
||||||
@@ -268,7 +283,11 @@ export class LayerManager {
|
|||||||
|
|
||||||
if (this.isRedGreenMode) {
|
if (this.isRedGreenMode) {
|
||||||
// 红绿图模式下 所有普通图层都可擦除
|
// 红绿图模式下 所有普通图层都可擦除
|
||||||
obj.erasable = layer.visible && !layer.locked && !layer.isBackground && !layer.isFixed;
|
obj.erasable =
|
||||||
|
layer.visible &&
|
||||||
|
!layer.locked &&
|
||||||
|
!layer.isBackground &&
|
||||||
|
!layer.isFixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 平移模式下,禁用多选和擦除
|
// 平移模式下,禁用多选和擦除
|
||||||
@@ -319,7 +338,9 @@ export class LayerManager {
|
|||||||
|
|
||||||
// 设置子图层对象的交互性
|
// 设置子图层对象的交互性
|
||||||
layer?.childLayer?.forEach(async (childLayer) => {
|
layer?.childLayer?.forEach(async (childLayer) => {
|
||||||
const childObj = this.canvas.getObjects().find((o) => o.layerId === childLayer.id);
|
const childObj = this.canvas
|
||||||
|
.getObjects()
|
||||||
|
.find((o) => o.layerId === childLayer.id);
|
||||||
if (childObj) {
|
if (childObj) {
|
||||||
await this._setObjectInteractivity(childObj, childLayer, editorMode);
|
await this._setObjectInteractivity(childObj, childLayer, editorMode);
|
||||||
}
|
}
|
||||||
@@ -331,7 +352,10 @@ export class LayerManager {
|
|||||||
let clippingMaskFabricObject = null;
|
let clippingMaskFabricObject = null;
|
||||||
if (layer.clippingMask) {
|
if (layer.clippingMask) {
|
||||||
// 反序列化 clippingMask
|
// 反序列化 clippingMask
|
||||||
clippingMaskFabricObject = await restoreFabricObject(layer.clippingMask, this.canvas);
|
clippingMaskFabricObject = await restoreFabricObject(
|
||||||
|
layer.clippingMask,
|
||||||
|
this.canvas
|
||||||
|
);
|
||||||
clippingMaskFabricObject.clipPath = null;
|
clippingMaskFabricObject.clipPath = null;
|
||||||
|
|
||||||
clippingMaskFabricObject.set({
|
clippingMaskFabricObject.set({
|
||||||
@@ -351,7 +375,9 @@ export class LayerManager {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
layer.children.forEach((childLayer) => {
|
layer.children.forEach((childLayer) => {
|
||||||
const childObj = this.canvas.getObjects().find((o) => o.layerId === childLayer.id);
|
const childObj = this.canvas
|
||||||
|
.getObjects()
|
||||||
|
.find((o) => o.layerId === childLayer.id);
|
||||||
if (childObj) {
|
if (childObj) {
|
||||||
childObj.clipPath = clippingMaskFabricObject;
|
childObj.clipPath = clippingMaskFabricObject;
|
||||||
childObj.dirty = true; // 标记为脏对象
|
childObj.dirty = true; // 标记为脏对象
|
||||||
@@ -369,7 +395,9 @@ export class LayerManager {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
layer.fabricObjects?.forEach((obj) => {
|
layer.fabricObjects?.forEach((obj) => {
|
||||||
const fabricObject = this.canvas.getObjects().find((o) => o.id === obj.id);
|
const fabricObject = this.canvas
|
||||||
|
.getObjects()
|
||||||
|
.find((o) => o.id === obj.id);
|
||||||
if (fabricObject) {
|
if (fabricObject) {
|
||||||
fabricObject.clipPath = clippingMaskFabricObject;
|
fabricObject.clipPath = clippingMaskFabricObject;
|
||||||
fabricObject.dirty = true; // 标记为脏对象
|
fabricObject.dirty = true; // 标记为脏对象
|
||||||
@@ -397,7 +425,11 @@ export class LayerManager {
|
|||||||
*/
|
*/
|
||||||
async fillLayerBackground(layerId, fillColor, undoable = true) {
|
async fillLayerBackground(layerId, fillColor, undoable = true) {
|
||||||
layerId = this.activeLayerId.value || layerId;
|
layerId = this.activeLayerId.value || layerId;
|
||||||
await this.backgroundFillManager.fillLayerBackground(layerId, fillColor, undoable);
|
await this.backgroundFillManager.fillLayerBackground(
|
||||||
|
layerId,
|
||||||
|
fillColor,
|
||||||
|
undoable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -470,7 +502,9 @@ export class LayerManager {
|
|||||||
*/
|
*/
|
||||||
createBackgroundLayer(name = "背景") {
|
createBackgroundLayer(name = "背景") {
|
||||||
// 检查是否已有背景图层
|
// 检查是否已有背景图层
|
||||||
const hasBackgroundLayer = this.layers.value.some((layer) => layer.isBackground);
|
const hasBackgroundLayer = this.layers.value.some(
|
||||||
|
(layer) => layer.isBackground
|
||||||
|
);
|
||||||
|
|
||||||
if (hasBackgroundLayer) {
|
if (hasBackgroundLayer) {
|
||||||
console.warn("已存在背景层,不再创建新的背景层");
|
console.warn("已存在背景层,不再创建新的背景层");
|
||||||
@@ -523,7 +557,9 @@ export class LayerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成唯一ID
|
// 生成唯一ID
|
||||||
const layerId = `fixed_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
const layerId = `fixed_layer_${Date.now()}_${Math.floor(
|
||||||
|
Math.random() * 1000
|
||||||
|
)}`;
|
||||||
|
|
||||||
// 创建固定图层
|
// 创建固定图层
|
||||||
const fixedLayer = createFixedLayer({
|
const fixedLayer = createFixedLayer({
|
||||||
@@ -574,19 +610,21 @@ export class LayerManager {
|
|||||||
// 如果没有任何图层,创建背景层、固定图层和一个空白图层
|
// 如果没有任何图层,创建背景层、固定图层和一个空白图层
|
||||||
if (this.layers.value.length === 0) {
|
if (this.layers.value.length === 0) {
|
||||||
// 创建背景图层
|
// 创建背景图层
|
||||||
this.createBackgroundLayer();
|
this.createBackgroundLayer(this.t("Canvas.Background"));
|
||||||
|
|
||||||
// 创建固定图层,位于背景图层之上
|
// 创建固定图层,位于背景图层之上
|
||||||
this.createFixedLayer();
|
this.createFixedLayer(this.t("Canvas.FixedLayer"));
|
||||||
|
|
||||||
// 创建一个空白图层(默认位于背景图层和固定图层之上)
|
// 创建一个空白图层(默认位于背景图层和固定图层之上)
|
||||||
await this.createLayer(t('Canvas.Layer1'));
|
await this.createLayer(this.t("Canvas.Layer1"));
|
||||||
} else {
|
} else {
|
||||||
// 检查是否已有背景层
|
// 检查是否已有背景层
|
||||||
const hasBackgroundLayer = this.layers.value.some((layer) => layer.isBackground);
|
const hasBackgroundLayer = this.layers.value.some(
|
||||||
|
(layer) => layer.isBackground
|
||||||
|
);
|
||||||
|
|
||||||
if (!hasBackgroundLayer) {
|
if (!hasBackgroundLayer) {
|
||||||
this.createBackgroundLayer();
|
this.createBackgroundLayer(this.t("Canvas.Background"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否已有固定图层
|
// 检查是否已有固定图层
|
||||||
@@ -602,7 +640,7 @@ export class LayerManager {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!hasNormalLayer) {
|
if (!hasNormalLayer) {
|
||||||
await this.createLayer(t('Canvas.Layer1'));
|
await this.createLayer(this.t("Canvas.Layer1"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,7 +667,10 @@ export class LayerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 验证目标图层是否存在
|
// 验证目标图层是否存在
|
||||||
const { layer: targetLayer } = findLayerRecursively(this.layers.value, targetLayerId);
|
const { layer: targetLayer } = findLayerRecursively(
|
||||||
|
this.layers.value,
|
||||||
|
targetLayerId
|
||||||
|
);
|
||||||
if (!targetLayer) {
|
if (!targetLayer) {
|
||||||
console.error(`目标图层 ${targetLayerId} 不存在`);
|
console.error(`目标图层 ${targetLayerId} 不存在`);
|
||||||
return null;
|
return null;
|
||||||
@@ -683,7 +724,8 @@ export class LayerManager {
|
|||||||
*/
|
*/
|
||||||
removeObjectFromLayer(objectOrId) {
|
removeObjectFromLayer(objectOrId) {
|
||||||
// 获取对象ID
|
// 获取对象ID
|
||||||
const objectId = typeof objectOrId === "string" ? objectOrId : objectOrId.id;
|
const objectId =
|
||||||
|
typeof objectOrId === "string" ? objectOrId : objectOrId.id;
|
||||||
|
|
||||||
if (!objectId) {
|
if (!objectId) {
|
||||||
console.error("无效的对象ID");
|
console.error("无效的对象ID");
|
||||||
@@ -743,7 +785,9 @@ export class LayerManager {
|
|||||||
*/
|
*/
|
||||||
getActiveLayer() {
|
getActiveLayer() {
|
||||||
if (!this.activeLayerId.value) {
|
if (!this.activeLayerId.value) {
|
||||||
console.warn("没有活动图层ID,无法获取活动图层 ==== 默认设置第一个图层为活动图层");
|
console.warn(
|
||||||
|
"没有活动图层ID,无法获取活动图层 ==== 默认设置第一个图层为活动图层"
|
||||||
|
);
|
||||||
this.activeLayerId.value = this.layers.value[0]?.id || null;
|
this.activeLayerId.value = this.layers.value[0]?.id || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,8 +892,13 @@ export class LayerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tempFabricObject =
|
const tempFabricObject =
|
||||||
fabricObject?.toObject?.(["id", "layerId", "layerName", "isBackgroud", "isFixed"]) ||
|
fabricObject?.toObject?.([
|
||||||
fabricObject;
|
"id",
|
||||||
|
"layerId",
|
||||||
|
"layerName",
|
||||||
|
"isBackgroud",
|
||||||
|
"isFixed",
|
||||||
|
]) || fabricObject;
|
||||||
if (layer.isFixed || layer.isBackground) {
|
if (layer.isFixed || layer.isBackground) {
|
||||||
layer.fabricObject = tempFabricObject;
|
layer.fabricObject = tempFabricObject;
|
||||||
} else {
|
} else {
|
||||||
@@ -875,7 +924,9 @@ export class LayerManager {
|
|||||||
|
|
||||||
// 如果是背景层或固定层,不允许移动
|
// 如果是背景层或固定层,不允许移动
|
||||||
if (layer && (layer.isBackground || layer.isFixed)) {
|
if (layer && (layer.isBackground || layer.isFixed)) {
|
||||||
console.warn(layer.isBackground ? $t("背景层不可移动") : $t("固定层不可移动"));
|
console.warn(
|
||||||
|
layer.isBackground ? this.t("背景层不可移动") : this.t("固定层不可移动")
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1300,7 +1351,11 @@ export class LayerManager {
|
|||||||
async ungroupLayers(groupId) {
|
async ungroupLayers(groupId) {
|
||||||
// 查找组图层
|
// 查找组图层
|
||||||
const groupLayer = this.layers.value.find((l) => l.id === groupId);
|
const groupLayer = this.layers.value.find((l) => l.id === groupId);
|
||||||
if (!groupLayer || !groupLayer.children || groupLayer.children.length === 0) {
|
if (
|
||||||
|
!groupLayer ||
|
||||||
|
!groupLayer.children ||
|
||||||
|
groupLayer.children.length === 0
|
||||||
|
) {
|
||||||
console.error(`${groupId} 不是有效的组图层或不包含子图层`);
|
console.error(`${groupId} 不是有效的组图层或不包含子图层`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1357,7 +1412,9 @@ export class LayerManager {
|
|||||||
*/
|
*/
|
||||||
resizeCanvasWithScale(width, height, options = {}) {
|
resizeCanvasWithScale(width, height, options = {}) {
|
||||||
// 检查是否有除背景层外的其他元素
|
// 检查是否有除背景层外的其他元素
|
||||||
const hasOtherElements = this.canvas.getObjects().some((obj) => !obj.isBackground);
|
const hasOtherElements = this.canvas
|
||||||
|
.getObjects()
|
||||||
|
.some((obj) => !obj.isBackground);
|
||||||
|
|
||||||
if (hasOtherElements) {
|
if (hasOtherElements) {
|
||||||
// 有其他元素时使用带缩放的命令
|
// 有其他元素时使用带缩放的命令
|
||||||
@@ -1426,7 +1483,9 @@ export class LayerManager {
|
|||||||
if (!this.canvas) return;
|
if (!this.canvas) return;
|
||||||
|
|
||||||
// 获取画布上的所有对象
|
// 获取画布上的所有对象
|
||||||
const canvasObjects = [...this.canvas.getObjects(["id", "layerId", "layerName"])];
|
const canvasObjects = [
|
||||||
|
...this.canvas.getObjects(["id", "layerId", "layerName"]),
|
||||||
|
];
|
||||||
|
|
||||||
// 清空画布
|
// 清空画布
|
||||||
this.canvas.clear();
|
this.canvas.clear();
|
||||||
@@ -1441,13 +1500,19 @@ export class LayerManager {
|
|||||||
|
|
||||||
if (layer.isBackground && layer.fabricObject) {
|
if (layer.isBackground && layer.fabricObject) {
|
||||||
// 背景图层
|
// 背景图层
|
||||||
const originalObj = canvasObjects.find((o) => o.id === layer.fabricObject.id);
|
const originalObj = canvasObjects.find(
|
||||||
|
(o) => o.id === layer.fabricObject.id
|
||||||
|
);
|
||||||
if (originalObj) {
|
if (originalObj) {
|
||||||
this.canvas.add(originalObj);
|
this.canvas.add(originalObj);
|
||||||
} else {
|
} else {
|
||||||
this.canvas.add(layer.fabricObject);
|
this.canvas.add(layer.fabricObject);
|
||||||
}
|
}
|
||||||
} else if (layer.isFixed && layer.fabricObjects && layer.fabricObjects.length > 0) {
|
} else if (
|
||||||
|
layer.isFixed &&
|
||||||
|
layer.fabricObjects &&
|
||||||
|
layer.fabricObjects.length > 0
|
||||||
|
) {
|
||||||
// 固定图层
|
// 固定图层
|
||||||
layer.fabricObjects.forEach((obj) => {
|
layer.fabricObjects.forEach((obj) => {
|
||||||
const originalObj = canvasObjects.find((o) => o.id === obj.id);
|
const originalObj = canvasObjects.find((o) => o.id === obj.id);
|
||||||
@@ -1457,7 +1522,10 @@ export class LayerManager {
|
|||||||
this.canvas.add(obj);
|
this.canvas.add(obj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (Array.isArray(layer.fabricObjects) && layer.fabricObjects.length > 0) {
|
} else if (
|
||||||
|
Array.isArray(layer.fabricObjects) &&
|
||||||
|
layer.fabricObjects.length > 0
|
||||||
|
) {
|
||||||
// 普通图层,添加所有fabricObjects
|
// 普通图层,添加所有fabricObjects
|
||||||
layer.fabricObjects.forEach((obj) => {
|
layer.fabricObjects.forEach((obj) => {
|
||||||
const originalObj = canvasObjects.find((o) => o.id === obj.id);
|
const originalObj = canvasObjects.find((o) => o.id === obj.id);
|
||||||
@@ -1486,7 +1554,9 @@ export class LayerManager {
|
|||||||
if (layer.isBackground) {
|
if (layer.isBackground) {
|
||||||
// 背景图层处理
|
// 背景图层处理
|
||||||
if (layer.fabricObject) {
|
if (layer.fabricObject) {
|
||||||
const existsOnCanvas = canvasObjects.some((obj) => obj.id === layer.fabricObject.id);
|
const existsOnCanvas = canvasObjects.some(
|
||||||
|
(obj) => obj.id === layer.fabricObject.id
|
||||||
|
);
|
||||||
if (!existsOnCanvas) {
|
if (!existsOnCanvas) {
|
||||||
this.canvas.add(layer.fabricObject);
|
this.canvas.add(layer.fabricObject);
|
||||||
}
|
}
|
||||||
@@ -1621,7 +1691,8 @@ export class LayerManager {
|
|||||||
layerCopy.children = layer.children.map((child) => {
|
layerCopy.children = layer.children.map((child) => {
|
||||||
const childCopy = JSON.parse(JSON.stringify(child));
|
const childCopy = JSON.parse(JSON.stringify(child));
|
||||||
if (child.fabricObjects && child.fabricObjects.length > 0) {
|
if (child.fabricObjects && child.fabricObjects.length > 0) {
|
||||||
childCopy.serializedObjects = this.getCurrLayerSerializedObjects(child);
|
childCopy.serializedObjects =
|
||||||
|
this.getCurrLayerSerializedObjects(child);
|
||||||
}
|
}
|
||||||
return childCopy;
|
return childCopy;
|
||||||
});
|
});
|
||||||
@@ -1667,7 +1738,9 @@ export class LayerManager {
|
|||||||
if (layer.fabricObjects && layer.fabricObjects.length > 0) {
|
if (layer.fabricObjects && layer.fabricObjects.length > 0) {
|
||||||
layerCopy.serializedObjects = layer.fabricObjects
|
layerCopy.serializedObjects = layer.fabricObjects
|
||||||
.map((obj) =>
|
.map((obj) =>
|
||||||
typeof obj.toObject === "function" ? obj.toObject(["id", "layerId", "layerName"]) : null
|
typeof obj.toObject === "function"
|
||||||
|
? obj.toObject(["id", "layerId", "layerName"])
|
||||||
|
: null
|
||||||
)
|
)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
}
|
}
|
||||||
@@ -1800,7 +1873,8 @@ export class LayerManager {
|
|||||||
// 查找第一个非背景、非锁定的图层,排除指定的图层
|
// 查找第一个非背景、非锁定的图层,排除指定的图层
|
||||||
return (
|
return (
|
||||||
this.layers.value.find(
|
this.layers.value.find(
|
||||||
(layer) => layer.id !== excludeLayerId && !layer.isBackground && !layer.locked
|
(layer) =>
|
||||||
|
layer.id !== excludeLayerId && !layer.isBackground && !layer.locked
|
||||||
) || null
|
) || null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1935,7 +2009,9 @@ export class LayerManager {
|
|||||||
* @param {string} backgroundColor 背景颜色
|
* @param {string} backgroundColor 背景颜色
|
||||||
*/
|
*/
|
||||||
updateBackgroundColor(backgroundColor, options = {}) {
|
updateBackgroundColor(backgroundColor, options = {}) {
|
||||||
const backgroundLayer = this.layers.value.find((layer) => layer.isBackground);
|
const backgroundLayer = this.layers.value.find(
|
||||||
|
(layer) => layer.isBackground
|
||||||
|
);
|
||||||
|
|
||||||
if (!backgroundLayer) {
|
if (!backgroundLayer) {
|
||||||
console.warn("没有找到背景图层");
|
console.warn("没有找到背景图层");
|
||||||
@@ -2135,7 +2211,8 @@ export class LayerManager {
|
|||||||
if (!this.canvas || !textObject) return null;
|
if (!this.canvas || !textObject) return null;
|
||||||
|
|
||||||
// 确保对象有ID
|
// 确保对象有ID
|
||||||
textObject.id = textObject.id || `text_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
textObject.id =
|
||||||
|
textObject.id || `text_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
||||||
|
|
||||||
// 创建文本图层
|
// 创建文本图层
|
||||||
const layerName = options.name || "文本图层";
|
const layerName = options.name || "文本图层";
|
||||||
@@ -2152,7 +2229,9 @@ export class LayerManager {
|
|||||||
overline: options.overline || textObject.overline || false,
|
overline: options.overline || textObject.overline || false,
|
||||||
fill: options.fill || textObject.fill || "#000000",
|
fill: options.fill || textObject.fill || "#000000",
|
||||||
textBackgroundColor:
|
textBackgroundColor:
|
||||||
options.textBackgroundColor || textObject.textBackgroundColor || "transparent",
|
options.textBackgroundColor ||
|
||||||
|
textObject.textBackgroundColor ||
|
||||||
|
"transparent",
|
||||||
lineHeight: options.lineHeight || textObject.lineHeight || 1.16,
|
lineHeight: options.lineHeight || textObject.lineHeight || 1.16,
|
||||||
charSpacing: options.charSpacing || textObject.charSpacing || 0,
|
charSpacing: options.charSpacing || textObject.charSpacing || 0,
|
||||||
},
|
},
|
||||||
@@ -2163,7 +2242,9 @@ export class LayerManager {
|
|||||||
textObject.layerName = layerName;
|
textObject.layerName = layerName;
|
||||||
|
|
||||||
// 添加到画布,如果还未添加
|
// 添加到画布,如果还未添加
|
||||||
const isOnCanvas = this.canvas.getObjects().some((obj) => obj.id === textObject.id);
|
const isOnCanvas = this.canvas
|
||||||
|
.getObjects()
|
||||||
|
.some((obj) => obj.id === textObject.id);
|
||||||
if (!isOnCanvas) {
|
if (!isOnCanvas) {
|
||||||
this.canvas.add(textObject);
|
this.canvas.add(textObject);
|
||||||
}
|
}
|
||||||
@@ -2172,7 +2253,9 @@ export class LayerManager {
|
|||||||
const layer = this.getLayerById(layerId);
|
const layer = this.getLayerById(layerId);
|
||||||
if (layer) {
|
if (layer) {
|
||||||
layer.fabricObjects = layer.fabricObjects || [];
|
layer.fabricObjects = layer.fabricObjects || [];
|
||||||
layer.fabricObjects.push(textObject.toObject(["id", "layerId", "layerName"]));
|
layer.fabricObjects.push(
|
||||||
|
textObject.toObject(["id", "layerId", "layerName"])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置此图层为活动图层
|
// 设置此图层为活动图层
|
||||||
@@ -2205,7 +2288,9 @@ export class LayerManager {
|
|||||||
|
|
||||||
// 检查普通图层
|
// 检查普通图层
|
||||||
if (layer.fabricObjects && Array.isArray(layer.fabricObjects)) {
|
if (layer.fabricObjects && Array.isArray(layer.fabricObjects)) {
|
||||||
const foundObject = layer.fabricObjects.find((obj) => obj.id === fabricObject.id);
|
const foundObject = layer.fabricObjects.find(
|
||||||
|
(obj) => obj.id === fabricObject.id
|
||||||
|
);
|
||||||
if (foundObject) {
|
if (foundObject) {
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
@@ -2286,7 +2371,12 @@ export class LayerManager {
|
|||||||
* @returns {boolean} 是否排序成功
|
* @returns {boolean} 是否排序成功
|
||||||
*/
|
*/
|
||||||
reorderChildLayers(parentId, oldIndex, newIndex, layerId) {
|
reorderChildLayers(parentId, oldIndex, newIndex, layerId) {
|
||||||
return this.layerSort?.reorderChildLayers(parentId, oldIndex, newIndex, layerId);
|
return this.layerSort?.reorderChildLayers(
|
||||||
|
parentId,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
layerId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2467,13 +2557,19 @@ export class LayerManager {
|
|||||||
const layersCount = this.layers?.value?.length || 0;
|
const layersCount = this.layers?.value?.length || 0;
|
||||||
|
|
||||||
const shouldUseAsync =
|
const shouldUseAsync =
|
||||||
async !== null ? async : LayerSortUtils.shouldUseAsyncProcessing(objectsCount, layersCount);
|
async !== null
|
||||||
|
? async
|
||||||
|
: LayerSortUtils.shouldUseAsyncProcessing(objectsCount, layersCount);
|
||||||
|
|
||||||
if (shouldUseAsync) {
|
if (shouldUseAsync) {
|
||||||
console.log(`使用异步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`);
|
console.log(
|
||||||
|
`使用异步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
||||||
|
);
|
||||||
return this.layerSort.rearrangeObjectsAsync();
|
return this.layerSort.rearrangeObjectsAsync();
|
||||||
} else {
|
} else {
|
||||||
console.log(`使用同步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`);
|
console.log(
|
||||||
|
`使用同步排序处理 ${objectsCount} 个对象, ${layersCount} 个图层`
|
||||||
|
);
|
||||||
this.layerSort.rearrangeObjects();
|
this.layerSort.rearrangeObjects();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2582,7 +2678,10 @@ export class LayerManager {
|
|||||||
moveLayerToIndex({ parentId, oldIndex, newIndex, layerId }) {
|
moveLayerToIndex({ parentId, oldIndex, newIndex, layerId }) {
|
||||||
if (!this.layerSort) {
|
if (!this.layerSort) {
|
||||||
console.warn("图层排序工具未初始化,使用基础移动方法");
|
console.warn("图层排序工具未初始化,使用基础移动方法");
|
||||||
return this.moveLayer(layerId, newIndex > this.getLayerIndex(layerId) ? "down" : "up");
|
return this.moveLayer(
|
||||||
|
layerId,
|
||||||
|
newIndex > this.getLayerIndex(layerId) ? "down" : "up"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = this.layerSort.moveLayerToIndex({
|
const result = this.layerSort.moveLayerToIndex({
|
||||||
@@ -2593,7 +2692,9 @@ export class LayerManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(`图层 ${layerId} - oldIndex: ${oldIndex} 已移动到位置 ${newIndex}`);
|
console.log(
|
||||||
|
`图层 ${layerId} - oldIndex: ${oldIndex} 已移动到位置 ${newIndex}`
|
||||||
|
);
|
||||||
// 更新对象交互性
|
// 更新对象交互性
|
||||||
// this.updateLayersObjectsInteractivity();
|
// this.updateLayersObjectsInteractivity();
|
||||||
}
|
}
|
||||||
@@ -2637,7 +2738,9 @@ export class LayerManager {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let layerIndex = this.layers.value.findIndex((layer) => layer.id === layerId);
|
let layerIndex = this.layers.value.findIndex(
|
||||||
|
(layer) => layer.id === layerId
|
||||||
|
);
|
||||||
if (layerIndex >= 0) return layerIndex;
|
if (layerIndex >= 0) return layerIndex;
|
||||||
// 如果未找到,尝试在子图层中查找
|
// 如果未找到,尝试在子图层中查找
|
||||||
const { parent } = findLayerRecursively(this.layers.value, layerId);
|
const { parent } = findLayerRecursively(this.layers.value, layerId);
|
||||||
@@ -2668,7 +2771,9 @@ export class LayerManager {
|
|||||||
const result = this.layerSort.reorderLayers(oldIndex, newIndex, layerId);
|
const result = this.layerSort.reorderLayers(oldIndex, newIndex, layerId);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(`高级排序完成: 图层 ${layerId} 从位置 ${oldIndex} 移动到 ${newIndex}`);
|
console.log(
|
||||||
|
`高级排序完成: 图层 ${layerId} 从位置 ${oldIndex} 移动到 ${newIndex}`
|
||||||
|
);
|
||||||
// 更新对象交互性
|
// 更新对象交互性
|
||||||
this.updateLayersObjectsInteractivity();
|
this.updateLayersObjectsInteractivity();
|
||||||
}
|
}
|
||||||
@@ -2685,7 +2790,12 @@ export class LayerManager {
|
|||||||
* @returns {boolean} 是否排序成功
|
* @returns {boolean} 是否排序成功
|
||||||
*/
|
*/
|
||||||
advancedReorderChildLayers(parentId, oldIndex, newIndex, layerId) {
|
advancedReorderChildLayers(parentId, oldIndex, newIndex, layerId) {
|
||||||
const result = this.reorderChildLayers(parentId, oldIndex, newIndex, layerId);
|
const result = this.reorderChildLayers(
|
||||||
|
parentId,
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
layerId
|
||||||
|
);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -2802,8 +2912,12 @@ export class LayerManager {
|
|||||||
async mergeGroupLayers(groupId) {
|
async mergeGroupLayers(groupId) {
|
||||||
// 查找组图层
|
// 查找组图层
|
||||||
const groupLayer = this.layers.value.find((l) => l.id === groupId);
|
const groupLayer = this.layers.value.find((l) => l.id === groupId);
|
||||||
if (!groupLayer || !groupLayer.children || groupLayer.children.length === 0) {
|
if (
|
||||||
console.warn($t("找不到有效的组图层或组图层为空"));
|
!groupLayer ||
|
||||||
|
!groupLayer.children ||
|
||||||
|
groupLayer.children.length === 0
|
||||||
|
) {
|
||||||
|
console.warn(this.t("找不到有效的组图层或组图层为空"));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2837,22 +2951,25 @@ export class LayerManager {
|
|||||||
const targetLayerId = layerId || this.activeLayerId.value;
|
const targetLayerId = layerId || this.activeLayerId.value;
|
||||||
|
|
||||||
if (!targetLayerId) {
|
if (!targetLayerId) {
|
||||||
console.warn($t("没有指定要栅格化的图层"));
|
console.warn(this.t("没有指定要栅格化的图层"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找目标图层
|
// 查找目标图层
|
||||||
// const targetLayer = this.getLayerById(targetLayerId);
|
// const targetLayer = this.getLayerById(targetLayerId);
|
||||||
const { layer: targetLayer } = findLayerRecursively(this.layers.value, targetLayerId);
|
const { layer: targetLayer } = findLayerRecursively(
|
||||||
|
this.layers.value,
|
||||||
|
targetLayerId
|
||||||
|
);
|
||||||
|
|
||||||
if (!targetLayer) {
|
if (!targetLayer) {
|
||||||
console.error($t("图层不存在", { layerId: targetLayerId }));
|
console.error(this.t("图层不存在", { layerId: targetLayerId }));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不允许栅格化背景图层和固定图层
|
// 不允许栅格化背景图层和固定图层
|
||||||
if (targetLayer.isBackground || targetLayer.isFixed) {
|
if (targetLayer.isBackground || targetLayer.isFixed) {
|
||||||
console.warn($t("背景图层和固定图层不能栅格化"));
|
console.warn(this.t("背景图层和固定图层不能栅格化"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2898,7 +3015,9 @@ export class LayerManager {
|
|||||||
// 检查图层是否有内容可以栅格化
|
// 检查图层是否有内容可以栅格化
|
||||||
if (layer.type === "group" || layer.children.length > 0) {
|
if (layer.type === "group" || layer.children.length > 0) {
|
||||||
// 组图层:检查是否有子图层且子图层有内容
|
// 组图层:检查是否有子图层且子图层有内容
|
||||||
return layer.children.some((child) => child.fabricObjects && child.fabricObjects.length > 0);
|
return layer.children.some(
|
||||||
|
(child) => child.fabricObjects && child.fabricObjects.length > 0
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// 普通图层:检查是否有对象
|
// 普通图层:检查是否有对象
|
||||||
return layer.fabricObjects && layer.fabricObjects.length > 0;
|
return layer.fabricObjects && layer.fabricObjects.length > 0;
|
||||||
@@ -2913,16 +3032,19 @@ export class LayerManager {
|
|||||||
const targetLayerId = layerId || this.activeLayerId.value;
|
const targetLayerId = layerId || this.activeLayerId.value;
|
||||||
|
|
||||||
if (!targetLayerId) {
|
if (!targetLayerId) {
|
||||||
console.warn($t("没有指定要栅格化的图层"));
|
console.warn(this.t("没有指定要栅格化的图层"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找目标图层
|
// 查找目标图层
|
||||||
// const targetLayer = this.getLayerById(targetLayerId);
|
// const targetLayer = this.getLayerById(targetLayerId);
|
||||||
const { layer: targetLayer } = findLayerRecursively(this.layers.value, targetLayerId);
|
const { layer: targetLayer } = findLayerRecursively(
|
||||||
|
this.layers.value,
|
||||||
|
targetLayerId
|
||||||
|
);
|
||||||
|
|
||||||
if (!targetLayer) {
|
if (!targetLayer) {
|
||||||
console.error($t("图层不存在", { layerId: targetLayerId }));
|
console.error(this.t("图层不存在", { layerId: targetLayerId }));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2079
src/lang/cn.ts
2079
src/lang/cn.ts
File diff suppressed because it is too large
Load Diff
2124
src/lang/en.ts
2124
src/lang/en.ts
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user