223 lines
6.5 KiB
Vue
223 lines
6.5 KiB
Vue
<template>
|
|
<div class="depth-canvas">
|
|
<div class="canvas-container" ref="canvasContainerRef">
|
|
<canvas ref="canvasRef"></canvas>
|
|
</div>
|
|
<template v-if="isReady">
|
|
<layer-panel />
|
|
<details-panel />
|
|
<brush-control-panel :currentTool="toolManager.currentTool.value" />
|
|
<ai-selectbox-panel :currentTool="toolManager.currentTool.value" />
|
|
<depth-header-tools @export="exportCanvas" @workbench="onWorkbench" />
|
|
|
|
<zoom
|
|
:zoom="canvasManager.currentZoom.value / 100"
|
|
is-home
|
|
@home="() => canvasManager.resetZoom()"
|
|
@add="() => canvasManager.zoomIn()"
|
|
@sub="() => canvasManager.zoomOut()"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { fabric } from 'fabric-with-all'
|
|
import { computed, ref, markRaw, onMounted, nextTick, provide, onBeforeUnmount } from 'vue'
|
|
import { OperationType } from './tools/layerHelper'
|
|
import { exportCanvasToImage } from './tools/exportMethod'
|
|
// 组件
|
|
import layerPanel from './components/layer-panel/index.vue'
|
|
import detailsPanel from './components/details-panel/index.vue'
|
|
import depthHeaderTools from './components/depth-header-tools.vue'
|
|
import zoom from '../components/zoom.vue'
|
|
import brushControlPanel from './components/brush-control-panel.vue'
|
|
import aiSelectboxPanel from './components/ai-selectbox-panel.vue'
|
|
|
|
// 管理器
|
|
import { StateManager } from './manager/StateManager'
|
|
import { LayerManager } from './manager/LayerManager'
|
|
import { CanvasManager } from './manager/CanvasManager'
|
|
import { ToolManager } from './manager/ToolManager'
|
|
import { KeyEventManager } from './manager/events/KeyEventManager'
|
|
import { ObjectManager } from './manager/ObjectManager'
|
|
|
|
import { useGlobalStore } from '@/stores'
|
|
const globalStore = useGlobalStore()
|
|
|
|
const emit = defineEmits(['workbench', 'close'])
|
|
const props = defineProps({
|
|
config: {
|
|
type: Object,
|
|
default: () => ({})
|
|
}
|
|
})
|
|
const onWorkbench = async () => {
|
|
exportCanvasToImage(canvasManager.canvas).then((url) => {
|
|
const { canvas, images } = canvasManager.getCanvasDisUrlJSON()
|
|
emit('workbench', { url, canvas, images })
|
|
})
|
|
}
|
|
|
|
// 准备就绪
|
|
const isReady = ref(false)
|
|
const canvasContainerRef = ref(null)
|
|
const canvasRef = ref(null)
|
|
|
|
// 状态管理器
|
|
const stateManager = new StateManager({})
|
|
provide('stateManager', stateManager)
|
|
|
|
// 画布管理器
|
|
const canvasManager = new CanvasManager({ stateManager, props })
|
|
stateManager.setManager({ canvasManager })
|
|
provide('canvasManager', canvasManager)
|
|
|
|
// 图层管理器
|
|
const layerManager = new LayerManager({ stateManager, canvasManager })
|
|
stateManager.setManager({ layerManager })
|
|
provide('layerManager', layerManager)
|
|
|
|
// 工具管理器
|
|
const toolManager = new ToolManager({ stateManager, canvasManager })
|
|
stateManager.setManager({ toolManager })
|
|
provide('toolManager', toolManager)
|
|
|
|
//键盘事件管理器
|
|
const keyEventManager = new KeyEventManager({ stateManager, onWorkbench })
|
|
stateManager.setManager({ keyEventManager })
|
|
provide('keyEventManager', keyEventManager)
|
|
|
|
// 对象管理器
|
|
const objectManager = new ObjectManager({ stateManager, canvasManager, layerManager })
|
|
stateManager.setManager({ objectManager })
|
|
provide('objectManager', objectManager)
|
|
|
|
const observer = ref(null)
|
|
onMounted(async () => {
|
|
globalStore.setLoading(true)
|
|
keyEventManager.registerEvents()
|
|
const url = props.config.url || ''
|
|
const canvasData = props.config.canvasData || ''
|
|
await canvasManager.initCanvas({
|
|
canvasRef,
|
|
canvasViewWidth: canvasContainerRef.value.clientWidth,
|
|
canvasViewHeight: canvasContainerRef.value.clientHeight,
|
|
canvasWidth: props.config.width || 750,
|
|
canvasHeight: props.config.height || 600,
|
|
url: canvasData ? '' : url
|
|
})
|
|
if (canvasData) {
|
|
const json = canvasManager.processCanvasDisUrlJSON(canvasData)
|
|
await canvasManager.loadJSON(json)
|
|
}
|
|
layerManager.setActiveFirstLayer()
|
|
globalStore.setLoading(false)
|
|
|
|
stateManager.onMounted()
|
|
canvasManager.onMounted()
|
|
layerManager.onMounted()
|
|
toolManager.onMounted()
|
|
keyEventManager.onMounted()
|
|
objectManager.onMounted()
|
|
|
|
const trailingTimeout = ref(null)
|
|
observer.value = new ResizeObserver((entries) => {
|
|
clearTimeout(trailingTimeout.value)
|
|
trailingTimeout.value = setTimeout(async () => {
|
|
handleWindowResize()
|
|
}, 100)
|
|
})
|
|
observer.value.observe(canvasContainerRef.value)
|
|
|
|
isReady.value = true // 准备就绪
|
|
})
|
|
onBeforeUnmount(() => {
|
|
observer.value.disconnect()
|
|
canvasManager.dispose()
|
|
stateManager.dispose()
|
|
layerManager.dispose()
|
|
toolManager.dispose()
|
|
keyEventManager.dispose()
|
|
objectManager.dispose()
|
|
})
|
|
async function handleWindowResize() {
|
|
console.log('==========画布窗口大小变化==========')
|
|
canvasManager.setCanvasViewSize({
|
|
canvasViewWidth: canvasContainerRef.value.clientWidth,
|
|
canvasViewHeight: canvasContainerRef.value.clientHeight
|
|
})
|
|
canvasManager.resetZoom(true, true)
|
|
}
|
|
/** 导入本地图片 */
|
|
const importLocalImage = (isRecord = true) => {
|
|
return new Promise((resolve, reject) => {
|
|
const input = document.createElement('input')
|
|
input.type = 'file'
|
|
input.accept = 'image/*'
|
|
input.click()
|
|
input.addEventListener('change', (e: any) => {
|
|
const file = e.target.files[0]
|
|
if (!file) return
|
|
const reader = new FileReader()
|
|
reader.readAsDataURL(file)
|
|
reader.onload = () => {
|
|
toolManager.setTool(OperationType.SELECT)
|
|
const url = reader.result as string
|
|
layerManager
|
|
.createImageLayer(url, { info: { name: file.name } }, isRecord)
|
|
.then((v) => resolve(v))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
provide('importLocalImage', importLocalImage)
|
|
|
|
const exportCanvas = () => {
|
|
// 导出图片
|
|
exportCanvasToImage(canvasManager.canvas).then((url) => {
|
|
const a = document.createElement('a')
|
|
a.href = url
|
|
a.download = 'canvas.png'
|
|
a.click()
|
|
})
|
|
|
|
// console.log(canvasManager.getCanvasJSON())
|
|
|
|
// const object = canvasManager.getCanvasDisUrlJSON()
|
|
// console.log(object)
|
|
// const canvas = canvasManager.processCanvasDisUrlJSON(object)
|
|
// console.log(canvas)
|
|
}
|
|
|
|
defineExpose({
|
|
onWorkbench
|
|
})
|
|
</script>
|
|
<style lang="less">
|
|
@import '@vue-flow/core/dist/style.css';
|
|
@import '@vue-flow/core/dist/theme-default.css';
|
|
</style>
|
|
<style lang="less" scoped>
|
|
.depth-canvas {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
user-select: none;
|
|
> .canvas-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
> canvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
&:deep(.canvas-container) {
|
|
filter: drop-shadow(0 0 5px rgba(0, 0, 0, 0.3));
|
|
}
|
|
}
|
|
}
|
|
</style>
|