略略略略略略略略略略略略
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 154 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 166 KiB |
@@ -116,13 +116,15 @@ export class FillRepeatCommand extends Command {
|
|||||||
flipY: object.flipY,
|
flipY: object.flipY,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
let scaleX = bgObject.scaleX || 1;
|
||||||
|
let scaleY = bgObject.scaleY || 1;
|
||||||
rect.set({
|
rect.set({
|
||||||
width: bgObject.width,
|
width: bgObject.width,
|
||||||
height: bgObject.height,
|
height: bgObject.height,
|
||||||
top: bgObject.top,
|
top: bgObject.top - bgObject.height * scaleY / 2,
|
||||||
left: bgObject.left,
|
left: bgObject.left - bgObject.width * scaleX / 2,
|
||||||
originX: bgObject.originX,
|
scaleX,
|
||||||
originY: bgObject.originY,
|
scaleY,
|
||||||
});
|
});
|
||||||
layer.locked = true;
|
layer.locked = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ const canDeleteComputed = computed(() => {
|
|||||||
:is-child="isChild"
|
:is-child="isChild"
|
||||||
:is-active="layer.id === activeLayerId"
|
:is-active="layer.id === activeLayerId"
|
||||||
:is-selected="isLayerSelected(layer.id)"
|
:is-selected="isLayerSelected(layer.id)"
|
||||||
:is-multi-select-mode="isMultiSelectMode"
|
:is-multi-select-mode="isMultiSelectMode && !layer.specialType"
|
||||||
:is-editing="editingLayerId === layer.id"
|
:is-editing="editingLayerId === layer.id"
|
||||||
:editing-name="editingLayerName"
|
:editing-name="editingLayerName"
|
||||||
:can-delete="
|
:can-delete="
|
||||||
@@ -296,7 +296,7 @@ const canDeleteComputed = computed(() => {
|
|||||||
:expanded-group-ids="expandedGroupIds"
|
:expanded-group-ids="expandedGroupIds"
|
||||||
@click="(...args) => forwardEvent('layer-click', ...args)"
|
@click="(...args) => forwardEvent('layer-click', ...args)"
|
||||||
@double-click="(...args) => forwardEvent('layer-double-click', ...args)"
|
@double-click="(...args) => forwardEvent('layer-double-click', ...args)"
|
||||||
@context-menu="(...args) => forwardEvent('context-menu', ...args)"
|
@context-menu="(...args) => !layer.specialType && forwardEvent('context-menu', ...args)"
|
||||||
@checkbox-change="(...args) => forwardEvent('checkbox-change', ...args)"
|
@checkbox-change="(...args) => forwardEvent('checkbox-change', ...args)"
|
||||||
@toggle-visibility="(...args) => forwardEvent('toggle-visibility', ...args)"
|
@toggle-visibility="(...args) => forwardEvent('toggle-visibility', ...args)"
|
||||||
@toggle-lock="(...args) => forwardEvent('toggle-lock', ...args)"
|
@toggle-lock="(...args) => forwardEvent('toggle-lock', ...args)"
|
||||||
@@ -337,7 +337,7 @@ const canDeleteComputed = computed(() => {
|
|||||||
:expanded-group-ids="expandedGroupIds"
|
:expanded-group-ids="expandedGroupIds"
|
||||||
:isChild="true"
|
:isChild="true"
|
||||||
:parentLayerId="layer.id"
|
:parentLayerId="layer.id"
|
||||||
:group-name="groupName"
|
:group-name="layer.specialType || groupName"
|
||||||
@layer-click="(...args) => forwardEvent('layer-click', ...args)"
|
@layer-click="(...args) => forwardEvent('layer-click', ...args)"
|
||||||
@layer-double-click="(...args) => forwardEvent('layer-double-click', ...args)"
|
@layer-double-click="(...args) => forwardEvent('layer-double-click', ...args)"
|
||||||
@context-menu="(...args) => forwardEvent('context-menu', ...args)"
|
@context-menu="(...args) => forwardEvent('context-menu', ...args)"
|
||||||
@@ -385,17 +385,10 @@ const canDeleteComputed = computed(() => {
|
|||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
// 从父组件的样式文件中继承相关样式
|
// 从父组件的样式文件中继承相关样式
|
||||||
.layers-list {
|
.layers-list {
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.sortable-layers {
|
.sortable-layers {
|
||||||
min-height: 20px;
|
min-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// .layer-group {
|
|
||||||
// // margin-bottom: 1px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.child-layers {
|
.child-layers {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
|
|||||||
@@ -1240,6 +1240,12 @@ async function handleCrossLevelMove(moveData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const layer = findLayerRecursively(layers.value, layerId).layer;
|
||||||
|
const toLayer = findLayerRecursively(layers.value, toParentId).layer;
|
||||||
|
if(layer?.specialType || toLayer?.specialType) {
|
||||||
|
console.warn("当前图层不可移动到外部");
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 如果有命令管理器,使用命令模式
|
// 如果有命令管理器,使用命令模式
|
||||||
if (commandManager) {
|
if (commandManager) {
|
||||||
console.log("📝 使用命令模式执行跨层级移动");
|
console.log("📝 使用命令模式执行跨层级移动");
|
||||||
@@ -1593,46 +1599,48 @@ async function moveGroupToGroup(draggedLayer, fromParentId, toParentId, newIndex
|
|||||||
<small>{{ $t('Canvas.Hint') }}</small>
|
<small>{{ $t('Canvas.Hint') }}</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="layers-list-container">
|
||||||
<!-- 图层列表组件 -->
|
<!-- 图层列表组件 -->
|
||||||
<LayersList
|
<LayersList
|
||||||
:layers="layers"
|
:layers="layers"
|
||||||
:active-layer-id="activeLayerId"
|
:active-layer-id="activeLayerId"
|
||||||
:sortable-root-layers="sortableRootLayers"
|
:sortable-root-layers="sortableRootLayers"
|
||||||
:fixed-layers="fixedLayers"
|
:fixed-layers="fixedLayers"
|
||||||
:selected-layer-ids="selectedLayerIds"
|
:selected-layer-ids="selectedLayerIds"
|
||||||
:is-multi-select-mode="isMultiSelectMode"
|
:is-multi-select-mode="isMultiSelectMode"
|
||||||
:editing-layer-id="editingLayerId"
|
:editing-layer-id="editingLayerId"
|
||||||
:editing-layer-name="editingLayerName"
|
:editing-layer-name="editingLayerName"
|
||||||
:thumbnail-manager="thumbnailManager"
|
:thumbnail-manager="thumbnailManager"
|
||||||
:expanded-group-ids="expandedGroupIds"
|
:expanded-group-ids="expandedGroupIds"
|
||||||
:isChild="false"
|
:isChild="false"
|
||||||
group-name="layers-root"
|
group-name="layers-root"
|
||||||
@layer-click="handleLayerClick"
|
@layer-click="handleLayerClick"
|
||||||
@layer-double-click="handleLayerDoubleClick"
|
@layer-double-click="handleLayerDoubleClick"
|
||||||
@context-menu="showContextMenu"
|
@context-menu="showContextMenu"
|
||||||
@checkbox-change="handleCheckboxClick"
|
@checkbox-change="handleCheckboxClick"
|
||||||
@toggle-visibility="toggleLayerVisibility"
|
@toggle-visibility="toggleLayerVisibility"
|
||||||
@toggle-lock="toggleSelectedLayersLockByLayer"
|
@toggle-lock="toggleSelectedLayersLockByLayer"
|
||||||
@delete="removeLayer"
|
@delete="removeLayer"
|
||||||
@edit-confirm="confirmEdit"
|
@edit-confirm="confirmEdit"
|
||||||
@edit-cancel="cancelEdit"
|
@edit-cancel="cancelEdit"
|
||||||
@edit-keydown="handleEditKeydown"
|
@edit-keydown="handleEditKeydown"
|
||||||
@touch-start="handleTouchStart"
|
@touch-start="handleTouchStart"
|
||||||
@touch-move="handleTouchMove"
|
@touch-move="handleTouchMove"
|
||||||
@touch-end="handleTouchEnd"
|
@touch-end="handleTouchEnd"
|
||||||
@update:editing-name="editingLayerName = $event"
|
@update:editing-name="editingLayerName = $event"
|
||||||
@root-layers-sort="handleRootLayersSort"
|
@root-layers-sort="handleRootLayersSort"
|
||||||
@child-layers-sort="handleChildLayersSort"
|
@child-layers-sort="handleChildLayersSort"
|
||||||
@cross-level-move="handleCrossLevelMove"
|
@cross-level-move="handleCrossLevelMove"
|
||||||
@select-child-layer="selectChildLayer"
|
@select-child-layer="selectChildLayer"
|
||||||
@start-child-layer-edit="startChildLayerEdit"
|
@start-child-layer-edit="startChildLayerEdit"
|
||||||
@child-context-menu="showChildLayerContextMenu"
|
@child-context-menu="showChildLayerContextMenu"
|
||||||
@toggle-group-expanded="toggleGroupExpanded"
|
@toggle-group-expanded="toggleGroupExpanded"
|
||||||
@toggle-child-visibility="toggleChildLayerVisibility"
|
@toggle-child-visibility="toggleChildLayerVisibility"
|
||||||
@toggle-child-lock="toggleChildLayerLock"
|
@toggle-child-lock="toggleChildLayerLock"
|
||||||
@delete-child="deleteChildLayer"
|
@delete-child="deleteChildLayer"
|
||||||
@rename-child="renameChildLayer"
|
@rename-child="renameChildLayer"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<!-- 固定层(背景层和固定层) -->
|
<!-- 固定层(背景层和固定层) -->
|
||||||
<div v-if="fixedLayers.length > 0" class="fixed-layers">
|
<div v-if="fixedLayers.length > 0" class="fixed-layers">
|
||||||
<!-- 遍历固定层 -->
|
<!-- 遍历固定层 -->
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
overflow-y: auto;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
// max-height: 70vh;
|
// max-height: 70vh;
|
||||||
|
overflow: hidden;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,12 +161,12 @@
|
|||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.layers-list-container{
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
// 图层列表
|
// 图层列表
|
||||||
.layers-list {
|
.layers-list {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图层项样式
|
// 图层项样式
|
||||||
|
|||||||
@@ -179,7 +179,11 @@
|
|||||||
import { ref, onMounted, watch, onUnmounted, reactive } from "vue";
|
import { ref, onMounted, watch, onUnmounted, reactive } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
import { OperationType, SpecialLayerId } from "../../utils/layerHelper";
|
import {
|
||||||
|
OperationType,
|
||||||
|
SpecialLayerId,
|
||||||
|
SpecialType,
|
||||||
|
} from "../../utils/layerHelper";
|
||||||
import { loadImageUrlToLayer } from "../../utils/imageHelper";
|
import { loadImageUrlToLayer } from "../../utils/imageHelper";
|
||||||
import {
|
import {
|
||||||
calculateRotatedTopLeftDeg,
|
calculateRotatedTopLeftDeg,
|
||||||
@@ -280,6 +284,9 @@
|
|||||||
const getActiveObject = (e) => {
|
const getActiveObject = (e) => {
|
||||||
console.log("==========切换激活对象", e, activeObjects);
|
console.log("==========切换激活对象", e, activeObjects);
|
||||||
activeObjects.value = [...e.selected];
|
activeObjects.value = [...e.selected];
|
||||||
|
// .filter((v) =>
|
||||||
|
// v.specialType ? v.specialType === SpecialType.REPEAT_O : true
|
||||||
|
// );// 过滤出印花对象
|
||||||
activeObjects.value.forEach((v) => {
|
activeObjects.value.forEach((v) => {
|
||||||
v.layer = props.layerManager.getLayerById(v.layerId);
|
v.layer = props.layerManager.getLayerById(v.layerId);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
overflow-y: auto;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
.layers-header {
|
.layers-header {
|
||||||
@@ -132,10 +132,11 @@
|
|||||||
color: #666;
|
color: #666;
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
.layers-list-container {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
.layers-list {
|
.layers-list {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
.layer-item {
|
.layer-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="angle-tool">
|
<div class="angle-tool" :disabled="disabled">
|
||||||
<div
|
<div
|
||||||
ref="dishRef"
|
ref="dishRef"
|
||||||
class="dish"
|
class="dish"
|
||||||
@@ -11,7 +11,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<input type="number" v-model="angle" @input="onInput" @change="onChange" />
|
<input
|
||||||
|
type="number"
|
||||||
|
v-model="angle"
|
||||||
|
@input="onInput"
|
||||||
|
@change="onChange"
|
||||||
|
:disabled="disabled"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -25,14 +31,22 @@
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const emit = defineEmits(["change", "input"]);
|
const emit = defineEmits(["change", "input"]);
|
||||||
const angle = ref(props.angle);
|
const angle = ref(props.angle);
|
||||||
watch(() => props.angle, (value) => {
|
watch(
|
||||||
angle.value = value;
|
() => props.angle,
|
||||||
});
|
(value) => {
|
||||||
|
angle.value = value;
|
||||||
|
}
|
||||||
|
);
|
||||||
const dishRef = ref<HTMLDivElement>();
|
const dishRef = ref<HTMLDivElement>();
|
||||||
const mousedown = (e: MouseEvent | TouchEvent) => {
|
const mousedown = (e: MouseEvent | TouchEvent) => {
|
||||||
|
if (props.disabled) return;
|
||||||
const mousemove = (e: MouseEvent | TouchEvent) => {
|
const mousemove = (e: MouseEvent | TouchEvent) => {
|
||||||
if (!dishRef.value) return;
|
if (!dishRef.value) return;
|
||||||
const { left, top, width, height } =
|
const { left, top, width, height } =
|
||||||
@@ -56,9 +70,10 @@
|
|||||||
document.addEventListener("mouseup", mouseup);
|
document.addEventListener("mouseup", mouseup);
|
||||||
document.addEventListener("touchend", mouseup);
|
document.addEventListener("touchend", mouseup);
|
||||||
};
|
};
|
||||||
const onInput = () => emit("input", angle.value);
|
const onInput = () => !props.disabled && emit("input", angle.value);
|
||||||
var changeTime: any = null;
|
var changeTime: any = null;
|
||||||
const onChange = () => {
|
const onChange = () => {
|
||||||
|
if (props.disabled) return;
|
||||||
clearTimeout(changeTime);
|
clearTimeout(changeTime);
|
||||||
changeTime = setTimeout(() => emit("change", angle.value), 500);
|
changeTime = setTimeout(() => emit("change", angle.value), 500);
|
||||||
};
|
};
|
||||||
@@ -79,10 +94,17 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
--color: #000;
|
||||||
|
&[disabled="true"] {
|
||||||
|
--color: #b2b2b2;
|
||||||
|
> .dish {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
> .dish {
|
> .dish {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
border: 1px solid #000;
|
border: 1px solid var(--color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
> .pointer {
|
> .pointer {
|
||||||
@@ -98,7 +120,7 @@
|
|||||||
transform: translate(-50%, 0);
|
transform: translate(-50%, 0);
|
||||||
width: 35%;
|
width: 35%;
|
||||||
height: 35%;
|
height: 35%;
|
||||||
background-color: #000;
|
background-color: var(--color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +128,7 @@
|
|||||||
> .input {
|
> .input {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #000;
|
color: var(--color);
|
||||||
flex: 1;
|
flex: 1;
|
||||||
// min-width: 45px;
|
// min-width: 45px;
|
||||||
// max-width: 80px;
|
// max-width: 80px;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
@change="change"
|
@change="change"
|
||||||
:defaultValue="defaultValue"
|
:defaultValue="defaultValue"
|
||||||
@dropdownVisibleChange="dropdownVisibleChange"
|
@dropdownVisibleChange="dropdownVisibleChange"
|
||||||
|
:disabled="disabled"
|
||||||
>
|
>
|
||||||
<a-select-option
|
<a-select-option
|
||||||
v-for="v in list"
|
v-for="v in list"
|
||||||
@@ -21,6 +22,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
defaultValue: {
|
defaultValue: {
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="slider">
|
<div class="slider" :disabled="disabled">
|
||||||
<div class="input-range">
|
<div class="input-range">
|
||||||
<span
|
<span
|
||||||
class="tip"
|
class="tip"
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
:step="props.step"
|
:step="props.step"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
|
:disabled="disabled"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="input" v-show="isInput">
|
<div class="input" v-show="isInput">
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
:step="props.step"
|
:step="props.step"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
|
:disabled="disabled"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,6 +37,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, defineProps, defineEmits, watch } from "vue";
|
import { ref, defineProps, defineEmits, watch } from "vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
value: {
|
value: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
@@ -66,9 +72,10 @@
|
|||||||
() => props.value,
|
() => props.value,
|
||||||
(v) => (value.value = v)
|
(v) => (value.value = v)
|
||||||
);
|
);
|
||||||
const onInput = () => emit("input", Number(value.value));
|
const onInput = () => !props.disabled && emit("input", Number(value.value));
|
||||||
var changeTime: any = null;
|
var changeTime: any = null;
|
||||||
const onChange = () => {
|
const onChange = () => {
|
||||||
|
if (props.disabled) return;
|
||||||
clearTimeout(changeTime);
|
clearTimeout(changeTime);
|
||||||
changeTime = setTimeout(() => emit("change", Number(value.value)), 500);
|
changeTime = setTimeout(() => emit("change", Number(value.value)), 500);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ const canvasManagerLoaded = ref(false); // 画布是否加载完成
|
|||||||
// 红绿图模式状态
|
// 红绿图模式状态
|
||||||
const isRedGreenMode = ref(false);
|
const isRedGreenMode = ref(false);
|
||||||
|
|
||||||
const isShowLayerPanel = ref(false); // 是否显示图层面板
|
const isShowLayerPanel = ref(true); // 是否显示图层面板
|
||||||
|
|
||||||
provide("isShowLayerPanel", isShowLayerPanel); // 提供红绿图模式状态给子组件
|
provide("isShowLayerPanel", isShowLayerPanel); // 提供红绿图模式状态给子组件
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
createLayer,
|
createLayer,
|
||||||
LayerType,
|
LayerType,
|
||||||
SpecialLayerId,
|
SpecialLayerId,
|
||||||
|
SpecialType,
|
||||||
BlendMode,
|
BlendMode,
|
||||||
} from "../utils/layerHelper";
|
} from "../utils/layerHelper";
|
||||||
import { ObjectMoveCommand } from "../commands/ObjectCommands";
|
import { ObjectMoveCommand } from "../commands/ObjectCommands";
|
||||||
@@ -175,6 +176,7 @@ export class CanvasManager {
|
|||||||
// 返回false表示使用默认行为(直接添加到画布)
|
// 返回false表示使用默认行为(直接添加到画布)
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
this.eraserStateManager = new EraserStateManager(
|
this.eraserStateManager = new EraserStateManager(
|
||||||
this.canvas,
|
this.canvas,
|
||||||
@@ -568,10 +570,10 @@ export class CanvasManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新颜色层信息
|
// 更新颜色层信息
|
||||||
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
// const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
||||||
if(colorObject){
|
// if(colorObject){
|
||||||
await this.setObjecCliptInfo(colorObject);
|
// await this.setObjecCliptInfo(colorObject);
|
||||||
}
|
// }
|
||||||
const groupLayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
|
const groupLayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
|
||||||
if(groupLayer){
|
if(groupLayer){
|
||||||
const groupRect = new fabric.Rect({});
|
const groupRect = new fabric.Rect({});
|
||||||
@@ -945,7 +947,7 @@ export class CanvasManager {
|
|||||||
options.restoreOpacityInRedGreen !== undefined
|
options.restoreOpacityInRedGreen !== undefined
|
||||||
? options.restoreOpacityInRedGreen
|
? options.restoreOpacityInRedGreen
|
||||||
: false, // 默认在红绿图模式下恢复透明度
|
: false, // 默认在红绿图模式下恢复透明度
|
||||||
excludedLayers: [SpecialLayerId.SPECIAL_GROUP],
|
// excludedLayers: [SpecialLayerId.SPECIAL_GROUP], // 导出时排除的图层ID数组
|
||||||
};
|
};
|
||||||
|
|
||||||
// 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层
|
// 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层
|
||||||
@@ -1040,10 +1042,10 @@ export class CanvasManager {
|
|||||||
* 导出印花和元素图层
|
* 导出印花和元素图层
|
||||||
*/
|
*/
|
||||||
async exportPrintTrimsLayers() {
|
async exportPrintTrimsLayers() {
|
||||||
const object = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
|
const glayer = this.layerManager.getLayerById(SpecialLayerId.SPECIAL_GROUP);
|
||||||
if(!object) return Promise.reject("印花和元素图层组不存在");
|
if(!glayer) return Promise.reject("印花和元素图层组不存在");
|
||||||
const ids = object.children.map((v) => v.id);
|
const ids = glayer.children.map((v) => v.id);
|
||||||
const objects = this.getObjectsByIds(ids).filter((v) => !!v.sourceData);
|
const objects = this.getObjectsByIds(ids);
|
||||||
const fixedLayerObj = this.getFixedLayerObject();
|
const fixedLayerObj = this.getFixedLayerObject();
|
||||||
if(!fixedLayerObj) return Promise.reject("固定图层不存在");
|
if(!fixedLayerObj) return Promise.reject("固定图层不存在");
|
||||||
const flWidth = fixedLayerObj.width
|
const flWidth = fixedLayerObj.width
|
||||||
@@ -1055,33 +1057,55 @@ export class CanvasManager {
|
|||||||
const prints = [];
|
const prints = [];
|
||||||
const trims = [];
|
const trims = [];
|
||||||
objects.forEach((v) => {
|
objects.forEach((v) => {
|
||||||
|
const sourceData = glayer.children.find((v_) => v_.id === v.id)?.metadata?.sourceData;
|
||||||
|
if(!sourceData) return;
|
||||||
const obj = {
|
const obj = {
|
||||||
ifSingle: v.sourceData.ifSingle,
|
ifSingle: typeof v.fill === "string",
|
||||||
level2Type: v.sourceData.level2Type,
|
level2Type: sourceData.level2Type,
|
||||||
designType: v.sourceData.designType,
|
designType: sourceData.designType,
|
||||||
path: v.sourceData.path,
|
path: sourceData.path,
|
||||||
minIOPath: v.sourceData.minIOPath,
|
minIOPath: sourceData.minIOPath,
|
||||||
location: [0, 0],
|
location: [0, 0],
|
||||||
scale: [0, 0],
|
scale: [0, 0],
|
||||||
angle: v.angle,
|
angle: v.angle,
|
||||||
name: v.sourceData.name,
|
name: sourceData.name,
|
||||||
priority: v.sourceData.priority,
|
priority: sourceData.priority,
|
||||||
gap: [0, 0],
|
object:{
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
scaleX: 0,//对象的缩放比例
|
||||||
|
scaleY: 0,//对象的缩放比例
|
||||||
|
opacity: v.opacity,
|
||||||
|
angle: v.angle,
|
||||||
|
flipX: v.flipX,//是否水平翻转
|
||||||
|
flipY: v.flipY,//是否垂直翻转
|
||||||
|
blendMode: v.globalCompositeOperation,// 混合模式
|
||||||
|
gapX: 0,// 平铺模式下的间距
|
||||||
|
gapY: 0,// 平铺模式下的间距
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let left = (v.left - (flLeft - flWidth * flScaleX / 2));
|
||||||
|
let top = (v.top - (flTop - flHeight * flScaleY / 2));
|
||||||
|
let width = (v.width * v.scaleX);
|
||||||
|
let height = (v.height * v.scaleY);
|
||||||
|
let {x:cx, y:cy} = calculateCenterPoint(width, height, left, top, v.angle);
|
||||||
|
let oX = (cx-width/2) / flScaleX;
|
||||||
|
let oY = (cy-height/2) / flScaleY;
|
||||||
|
let oScaleX = (v.width * v.scaleX) / (flWidth * flScaleX);
|
||||||
|
let oScaleY = (v.height * v.scaleY) / (flHeight * flScaleY);
|
||||||
|
// obj.object.width = width;
|
||||||
|
// obj.object.height = height;
|
||||||
|
obj.object.top = oY;
|
||||||
|
obj.object.left = oX;
|
||||||
|
obj.object.scaleX = oScaleX;
|
||||||
|
obj.object.scaleY = oScaleY;
|
||||||
if(obj.ifSingle){
|
if(obj.ifSingle){
|
||||||
let left = (v.left - (flLeft - flWidth * flScaleX / 2));
|
obj.location = [oX, oY];
|
||||||
let top = (v.top - (flTop - flHeight * flScaleY / 2));
|
obj.scale = [oScaleX, oScaleY];
|
||||||
let width = (v.width * v.scaleX);
|
|
||||||
let height = (v.height * v.scaleY);
|
|
||||||
let {x:cx, y:cy} = calculateCenterPoint(width, height, left, top, v.angle);
|
|
||||||
let x = (cx-width/2) / flScaleX;
|
|
||||||
let y = (cy-height/2) / flScaleY;
|
|
||||||
obj.location = [x, y];
|
|
||||||
obj.scale = [(v.width * v.scaleX) / (flWidth * flScaleX), (v.height * v.scaleY) / (flHeight * flScaleY)];
|
|
||||||
}else{
|
}else{
|
||||||
let fill = v.fill;
|
let fill = v.fill;
|
||||||
let fill_ = v.fill_;
|
let fill_ = v.fill_;
|
||||||
if(!fill || !fill_) return;
|
if(!fill || !fill_) return console.warn("印花元素不存在fill或fill_属性");
|
||||||
let {scale, angle} = getTransformScaleAngle(fill.patternTransform);
|
let {scale, angle} = getTransformScaleAngle(fill.patternTransform);
|
||||||
let scaleX = scale * 5 * v.fill_.width / flWidth;
|
let scaleX = scale * 5 * v.fill_.width / flWidth;
|
||||||
let scaleY = scale * 5 * v.fill_.height / flHeight;
|
let scaleY = scale * 5 * v.fill_.height / flHeight;
|
||||||
@@ -1339,7 +1363,6 @@ export class CanvasManager {
|
|||||||
await this.setCanvasSize(this.canvas.width, this.canvas.height);
|
await this.setCanvasSize(this.canvas.width, this.canvas.height);
|
||||||
await this.centerBackgroundLayer(this.canvas.width, this.canvas.height);
|
await this.centerBackgroundLayer(this.canvas.width, this.canvas.height);
|
||||||
await this.createOtherLayers(this.props.otherData);
|
await this.createOtherLayers(this.props.otherData);
|
||||||
|
|
||||||
// 重新构建对象关系
|
// 重新构建对象关系
|
||||||
// restoreObjectLayerAssociations(this.layers.value, this.canvas.getObjects());
|
// restoreObjectLayerAssociations(this.layers.value, this.canvas.getObjects());
|
||||||
// 验证图层关联关系 - 稳定后可以注释
|
// 验证图层关联关系 - 稳定后可以注释
|
||||||
@@ -1490,7 +1513,7 @@ export class CanvasManager {
|
|||||||
globalCompositeOperation: BlendMode.MULTIPLY,
|
globalCompositeOperation: BlendMode.MULTIPLY,
|
||||||
originColor: color,
|
originColor: color,
|
||||||
});
|
});
|
||||||
await this.setObjecCliptInfo(colorRect);
|
// await this.setObjecCliptInfo(colorRect);
|
||||||
const gradientObj = palletToFill(color);
|
const gradientObj = palletToFill(color);
|
||||||
const gradient = new fabric.Gradient({
|
const gradient = new fabric.Gradient({
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
@@ -1561,7 +1584,8 @@ export class CanvasManager {
|
|||||||
selectable: true,
|
selectable: true,
|
||||||
hasControls: true,
|
hasControls: true,
|
||||||
hasBorders: true,
|
hasBorders: true,
|
||||||
sourceData: item,
|
specialType: SpecialType.PRINT_TRIMS_O,
|
||||||
|
globalCompositeOperation: BlendMode.MULTIPLY,
|
||||||
});
|
});
|
||||||
resolve(fabricImage);
|
resolve(fabricImage);
|
||||||
}, { crossOrigin: "anonymous" });
|
}, { crossOrigin: "anonymous" });
|
||||||
@@ -1574,7 +1598,10 @@ export class CanvasManager {
|
|||||||
visible: true,
|
visible: true,
|
||||||
locked: false,
|
locked: false,
|
||||||
opacity: 1.0,
|
opacity: 1.0,
|
||||||
|
specialType: SpecialType.PRINT_TRIMS_L,
|
||||||
|
blendMode: BlendMode.MULTIPLY,
|
||||||
fabricObjects: [image.toObject(["id", "layerId", "layerName"])],
|
fabricObjects: [image.toObject(["id", "layerId", "layerName"])],
|
||||||
|
metadata: {sourceData: item},
|
||||||
})
|
})
|
||||||
children.push(layer);
|
children.push(layer);
|
||||||
};
|
};
|
||||||
@@ -1606,13 +1633,11 @@ export class CanvasManager {
|
|||||||
layerName: name,
|
layerName: name,
|
||||||
width: fixedLayerObj.width,
|
width: fixedLayerObj.width,
|
||||||
height: fixedLayerObj.height,
|
height: fixedLayerObj.height,
|
||||||
top: fixedLayerObj.top,
|
top: fixedLayerObj.top - fixedLayerObj.height * fixedLayerObj.scaleY / 2,
|
||||||
left: fixedLayerObj.left,
|
left: fixedLayerObj.left - fixedLayerObj.width * fixedLayerObj.scaleX / 2,
|
||||||
scaleX: fixedLayerObj.scaleX,
|
scaleX: fixedLayerObj.scaleX,
|
||||||
scaleY: fixedLayerObj.scaleY,
|
scaleY: fixedLayerObj.scaleY,
|
||||||
originX: fixedLayerObj.originX,
|
globalCompositeOperation: BlendMode.MULTIPLY,
|
||||||
originY: fixedLayerObj.originY,
|
|
||||||
sourceData: item,
|
|
||||||
fill: new fabric.Pattern({
|
fill: new fabric.Pattern({
|
||||||
source: image,
|
source: image,
|
||||||
repeat: "repeat",
|
repeat: "repeat",
|
||||||
@@ -1626,7 +1651,8 @@ export class CanvasManager {
|
|||||||
gapY: 0,
|
gapY: 0,
|
||||||
width: image.width,
|
width: image.width,
|
||||||
height: image.height,
|
height: image.height,
|
||||||
}
|
},
|
||||||
|
specialType: SpecialType.REPEAT_O,
|
||||||
});
|
});
|
||||||
this.canvas.add(rect);
|
this.canvas.add(rect);
|
||||||
let layer = createLayer({
|
let layer = createLayer({
|
||||||
@@ -1636,7 +1662,10 @@ export class CanvasManager {
|
|||||||
visible: true,
|
visible: true,
|
||||||
locked: true,
|
locked: true,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
|
specialType: SpecialType.REPEAT_L,
|
||||||
|
blendMode: BlendMode.MULTIPLY,
|
||||||
fabricObjects: [rect.toObject(["id", "layerId", "layerName"])],
|
fabricObjects: [rect.toObject(["id", "layerId", "layerName"])],
|
||||||
|
metadata: {sourceData: item},
|
||||||
})
|
})
|
||||||
children.push(layer);
|
children.push(layer);
|
||||||
};
|
};
|
||||||
@@ -1667,6 +1696,7 @@ export class CanvasManager {
|
|||||||
children: children,
|
children: children,
|
||||||
clippingMask: groupRect.toObject(),
|
clippingMask: groupRect.toObject(),
|
||||||
isFixedClipMask: true,
|
isFixedClipMask: true,
|
||||||
|
specialType: SpecialType.PRINT_TRIMS_G,
|
||||||
});
|
});
|
||||||
this.layers.value.splice(groupIndex, 0, groupLayer);
|
this.layers.value.splice(groupIndex, 0, groupLayer);
|
||||||
}
|
}
|
||||||
@@ -1675,29 +1705,29 @@ export class CanvasManager {
|
|||||||
* 画布事件变更后
|
* 画布事件变更后
|
||||||
*/
|
*/
|
||||||
async changeCanvas(){
|
async changeCanvas(){
|
||||||
// const fixedLayerObj = this.getFixedLayerObject();
|
const fixedLayerObj = this.getFixedLayerObject();
|
||||||
// if(!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj)
|
if(!fixedLayerObj) return console.warn("固定图层对象不存在", fixedLayerObj)
|
||||||
// const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
const colorObject = this.getLayerObjectById(SpecialLayerId.COLOR);
|
||||||
// if(colorObject){
|
if(colorObject){
|
||||||
// const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP);
|
const ids = this.layerManager.getBlendModeLayerIds(SpecialLayerId.SPECIAL_GROUP);
|
||||||
// if(ids.length === 0){
|
if(ids.length === 0){
|
||||||
// ids.unshift(SpecialLayerId.SPECIAL_GROUP);
|
ids.unshift(SpecialLayerId.SPECIAL_GROUP);
|
||||||
// await this.setObjecCliptInfo(colorObject);
|
await this.setObjecCliptInfo(colorObject);
|
||||||
// this.canvas.renderAll();
|
this.canvas.renderAll();
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
// const base64 = await this.exportManager.exportImage({layerIdArray2: ids, isEnhanceImg: true});
|
const base64 = await this.exportManager.exportImage({layerIdArray2: ids, isEnhanceImg: true});
|
||||||
// if(!base64) return console.warn("导出图片失败", base64)
|
if(!base64) return console.warn("导出图片失败", base64)
|
||||||
// const canvas = await base64ToCanvas(base64, fixedLayerObj.scaleX * 2, true);
|
const canvas = await base64ToCanvas(base64, fixedLayerObj.scaleX * 2, true);
|
||||||
// const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
// const width = fixedLayerObj.width;
|
const width = fixedLayerObj.width;
|
||||||
// const height = fixedLayerObj.height;
|
const height = fixedLayerObj.height;
|
||||||
// const x = (canvas.width - width) / 2;
|
const x = (canvas.width - width) / 2;
|
||||||
// const y = (canvas.height - height) / 2;
|
const y = (canvas.height - height) / 2;
|
||||||
// const data = ctx.getImageData(x, y, width, height);
|
const data = ctx.getImageData(x, y, width, height);
|
||||||
// await this.setObjecCliptInfo(colorObject, data);
|
await this.setObjecCliptInfo(colorObject, data);
|
||||||
// this.canvas.renderAll();
|
this.canvas.renderAll();
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -45,12 +45,6 @@ export class ExportManager {
|
|||||||
excludedLayers = [], // 排除的图层ID数组
|
excludedLayers = [], // 排除的图层ID数组
|
||||||
} = options;
|
} = options;
|
||||||
try {
|
try {
|
||||||
// 查找颜色图层并隐藏
|
|
||||||
// const colorLayer = this.layerManager.getLayerById(SpecialLayerId.COLOR);
|
|
||||||
// if (colorLayer && colorLayer.visible) {
|
|
||||||
// colorLayer.visible = false;
|
|
||||||
// await this.layerManager?.updateLayersObjectsInteractivity();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 检查是否为红绿图模式
|
// 检查是否为红绿图模式
|
||||||
const isRedGreenMode = this.layerManager?.isInRedGreenMode?.() || false;
|
const isRedGreenMode = this.layerManager?.isInRedGreenMode?.() || false;
|
||||||
|
|||||||
@@ -767,16 +767,13 @@ export function getLayerObjectsZIndex(canvas, layerId) {
|
|||||||
* @param {number} y1 第一个点的y坐标
|
* @param {number} y1 第一个点的y坐标
|
||||||
* @param {number} x2 第二个点的x坐标
|
* @param {number} x2 第二个点的x坐标
|
||||||
* @param {number} y2 第二个点的y坐标
|
* @param {number} y2 第二个点的y坐标
|
||||||
* @returns {number} 角度值(-90 - 270度)
|
* @returns {number} 角度值(-180 - 180度)
|
||||||
*/
|
*/
|
||||||
export function calculateAngle(x1, y1, x2, y2, int = false) {
|
export function calculateAngle(x1, y1, x2, y2, int = false) {
|
||||||
// 计算两点之间的差值
|
|
||||||
const deltaX = x2 - x1;
|
const deltaX = x2 - x1;
|
||||||
const deltaY = y2 - y1;
|
const deltaY = y1 - y2;
|
||||||
|
let angle = Math.atan2(deltaX, deltaY) * (180 / Math.PI);
|
||||||
// 使用Math.atan2计算弧度,然后转换为角度
|
// if(angle < 0) angle += 360;// 0 - 360度
|
||||||
let angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI + 90;
|
|
||||||
|
|
||||||
return int ? Math.round(angle) : angle;
|
return int ? Math.round(angle) : angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,17 @@ export const SpecialLayerId = {
|
|||||||
SPECIAL_GROUP: "group_special", // 特殊组
|
SPECIAL_GROUP: "group_special", // 特殊组
|
||||||
COLOR: "special_color", // 颜色图层
|
COLOR: "special_color", // 颜色图层
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 特殊类型
|
||||||
|
*/
|
||||||
|
export const SpecialType = {
|
||||||
|
PRINT_TRIMS_G: "print_trims_group", // 印花和元素图层组
|
||||||
|
PRINT_TRIMS_L: "print_trims_layer", // 印花和元素图层
|
||||||
|
PRINT_TRIMS_O: "print_trims_object", // 印花和元素图层对象
|
||||||
|
REPEAT_L: "repeat_layer",// 平铺图层
|
||||||
|
REPEAT_O: "repeat_object",// 平铺图层对象
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ const editorConfig = {
|
|||||||
const exportImage = async () => {
|
const exportImage = async () => {
|
||||||
if (canvasEditor.value) {
|
if (canvasEditor.value) {
|
||||||
const base64 = await canvasEditor.value.exportImage({
|
const base64 = await canvasEditor.value.exportImage({
|
||||||
isContainFixed: false, // 是否导出底图
|
isContainFixed: true, // 是否导出底图
|
||||||
isContainFixedOther: false, // 是否导出其他固定图层
|
isContainFixedOther: true, // 是否导出其他固定图层
|
||||||
isContainBg: false, // 是否导出背景
|
isContainBg: false, // 是否导出背景
|
||||||
isEnhanceImg: false, // 是否导出增强图片
|
isEnhanceImg: false, // 是否导出增强图片
|
||||||
});
|
});
|
||||||
@@ -301,7 +301,7 @@ const otherData = {
|
|||||||
designType: "Library",
|
designType: "Library",
|
||||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||||
location: [250, 780],
|
location: [250, 780],
|
||||||
scale: [0.5 * 0.7, 0.272541 * 0.7],
|
scale: [0.3, 0.4],
|
||||||
angle: 0,
|
angle: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -309,8 +309,8 @@ const otherData = {
|
|||||||
level2Type: "Pattern",
|
level2Type: "Pattern",
|
||||||
designType: "Library",
|
designType: "Library",
|
||||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||||
location: [250, 780],
|
location: [650, 650],
|
||||||
scale: [0.5 * 0.7, 0.272541 * 0.7],
|
scale: [0.15, 0.2],
|
||||||
angle: 0,
|
angle: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -318,8 +318,8 @@ const otherData = {
|
|||||||
level2Type: "Pattern",
|
level2Type: "Pattern",
|
||||||
designType: "Library",
|
designType: "Library",
|
||||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||||
location: [300, 500],
|
location: [700, 400],
|
||||||
scale: [0.5 * 0.4, 0.272541 * 0.4],
|
scale: [0.1, 0.133],
|
||||||
angle: 0,
|
angle: 0,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user