深度画布
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fill-repeat">
|
<div class="fill-repeat h">
|
||||||
<div>
|
<div>
|
||||||
<div class="title">Image</div>
|
<div class="title">Image</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -99,6 +99,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, inject, computed, nextTick, onBeforeUnmount } from 'vue'
|
import { ref, inject, computed, nextTick, onBeforeUnmount } from 'vue'
|
||||||
import DepthOffsetTool from '../tools/depth-offset-tool.vue'
|
import DepthOffsetTool from '../tools/depth-offset-tool.vue'
|
||||||
|
import DepthSlider from '../tools/depth-slider.vue'
|
||||||
|
import DepthInput from '../tools/depth-input.vue'
|
||||||
import { getTransformScaleAngle } from '../../manager/ObjectManager'
|
import { getTransformScaleAngle } from '../../manager/ObjectManager'
|
||||||
const objectManager = inject('objectManager') as any
|
const objectManager = inject('objectManager') as any
|
||||||
const stateManager = inject('stateManager') as any
|
const stateManager = inject('stateManager') as any
|
||||||
@@ -159,7 +161,7 @@
|
|||||||
const options = {
|
const options = {
|
||||||
opacity: opacity.value / 100
|
opacity: opacity.value / 100
|
||||||
}
|
}
|
||||||
objectManager.updateOpacity(id.value, options, isRecord)
|
objectManager.updateProperty(id.value, options, isRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateManager.event.add('canvas:undo', updateData)
|
stateManager.event.add('canvas:undo', updateData)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<div class="content" v-if="isShow" v-show="show">
|
<div class="content" v-if="isShow" v-show="show">
|
||||||
<!-- <basic-info :object="activeObject" /> -->
|
<!-- <basic-info :object="activeObject" /> -->
|
||||||
<fill-repeat :object="activeObject" v-if="isRepeat" />
|
<fill-repeat :object="activeObject" v-if="isRepeat" />
|
||||||
|
<shape-setting :object="activeObject" v-if="isShape && !isRepeat" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, inject, computed, watch, onMounted } from 'vue'
|
import { ref, inject, computed, watch, onMounted } from 'vue'
|
||||||
import FillRepeat from './fill-repeat.vue'
|
import FillRepeat from './fill-repeat.vue'
|
||||||
|
import ShapeSetting from './shape-setting.vue'
|
||||||
const props = defineProps({})
|
const props = defineProps({})
|
||||||
const layerManager = inject('layerManager') as any
|
const layerManager = inject('layerManager') as any
|
||||||
const canvasManager = inject('canvasManager') as any
|
const canvasManager = inject('canvasManager') as any
|
||||||
@@ -25,8 +27,10 @@
|
|||||||
const layers = computed(() => layerManager.layers.value)
|
const layers = computed(() => layerManager.layers.value)
|
||||||
const activeObject = ref(null)
|
const activeObject = ref(null)
|
||||||
|
|
||||||
|
const shapes = ['rect', 'line', 'path', 'triangle', 'polygon', 'ellipse']
|
||||||
|
const isShape = computed(() => shapes.includes(activeObject.value?.type))
|
||||||
const isRepeat = computed(() => activeObject.value?.fill?.repeat === 'repeat')
|
const isRepeat = computed(() => activeObject.value?.fill?.repeat === 'repeat')
|
||||||
const isShow = computed(() => isRepeat.value)
|
const isShow = computed(() => isRepeat.value || isShape.value)
|
||||||
|
|
||||||
const updateActiveObject = () => {
|
const updateActiveObject = () => {
|
||||||
const obj = layers.value.find((v: any) => v.info.id === activeID.value)
|
const obj = layers.value.find((v: any) => v.info.id === activeID.value)
|
||||||
@@ -85,7 +89,12 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
&:deep(> div) {
|
&:deep(> div) {
|
||||||
> div {
|
> div {
|
||||||
margin-bottom: 1.6rem;
|
margin-bottom: var(--details-item-margin-bottom, 1.6rem);
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.h > div {
|
||||||
> .title {
|
> .title {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
color: #000;
|
color: #000;
|
||||||
@@ -97,6 +106,20 @@
|
|||||||
padding: 0 1.4rem;
|
padding: 0 1.4rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.v > div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
> .label {
|
||||||
|
min-width: 6rem;
|
||||||
|
margin-right: 0.8rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
> .value {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<div class="shape-setting v">
|
||||||
|
<!-- <div>
|
||||||
|
<div class="label">填充颜色</div>
|
||||||
|
<div class="value">
|
||||||
|
<el-color-picker
|
||||||
|
v-model="data.fill"
|
||||||
|
show-alpha
|
||||||
|
:predefine="['transparent', '#000', '#f00', '#0f0', '#00f']"
|
||||||
|
@change="onChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, inject, computed, nextTick, onBeforeUnmount, reactive } from 'vue'
|
||||||
|
import { ElColorPicker } from 'element-plus'
|
||||||
|
import { getTransformScaleAngle } from '../../manager/ObjectManager'
|
||||||
|
import DepthOffsetTool from '../tools/depth-offset-tool.vue'
|
||||||
|
import DepthSlider from '../tools/depth-slider.vue'
|
||||||
|
import DepthInput from '../tools/depth-input.vue'
|
||||||
|
const objectManager = inject('objectManager') as any
|
||||||
|
const stateManager = inject('stateManager') as any
|
||||||
|
const props = defineProps({
|
||||||
|
object: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const id = computed(() => props.object.info.id)
|
||||||
|
|
||||||
|
const data = reactive({
|
||||||
|
fill: '',
|
||||||
|
stroke: '',
|
||||||
|
strokeWidth: 0
|
||||||
|
})
|
||||||
|
const updateData = async () => {
|
||||||
|
await nextTick()
|
||||||
|
data.fill = props.object.fill
|
||||||
|
data.stroke = props.object.stroke
|
||||||
|
data.strokeWidth = props.object.strokeWidth
|
||||||
|
}
|
||||||
|
updateData()
|
||||||
|
|
||||||
|
const onInpot = () => setPriority(false)
|
||||||
|
const onChange = () => setPriority(true)
|
||||||
|
const setPriority = (isRecord: boolean) => {
|
||||||
|
const options = { ...data }
|
||||||
|
objectManager.updateProperty(id.value, options, isRecord)
|
||||||
|
}
|
||||||
|
|
||||||
|
stateManager.event.add('canvas:undo', updateData)
|
||||||
|
stateManager.event.add('canvas:redo', updateData)
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
stateManager.event.remove('canvas:undo', updateData)
|
||||||
|
stateManager.event.remove('canvas:redo', updateData)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.shape-setting {
|
||||||
|
--details-item-margin-bottom: 1rem;
|
||||||
|
> div {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -158,12 +158,16 @@
|
|||||||
|
|
||||||
const exportCanvas = () => {
|
const exportCanvas = () => {
|
||||||
// 导出图片
|
// 导出图片
|
||||||
exportCanvasToImage(canvasManager.canvas).then((url) => {
|
// exportCanvasToImage(canvasManager.canvas).then((url) => {
|
||||||
const a = document.createElement('a')
|
// const a = document.createElement('a')
|
||||||
a.href = url
|
// a.href = url
|
||||||
a.download = 'canvas.png'
|
// a.download = 'canvas.png'
|
||||||
a.click()
|
// a.click()
|
||||||
})
|
// })
|
||||||
|
const object = canvasManager.getCanvasDisUrlJSON()
|
||||||
|
console.log(object)
|
||||||
|
const canvas = canvasManager.processCanvasDisUrlJSON(object)
|
||||||
|
console.log(canvas)
|
||||||
}
|
}
|
||||||
// 导出到本地存储
|
// 导出到本地存储
|
||||||
const exportCanvasToLocalStorage = () => {
|
const exportCanvasToLocalStorage = () => {
|
||||||
|
|||||||
@@ -121,21 +121,12 @@ export class CanvasManager {
|
|||||||
this.setupBrushEvents()
|
this.setupBrushEvents()
|
||||||
|
|
||||||
/** 测试-开始 */
|
/** 测试-开始 */
|
||||||
// this.stateManager.setIsRecord(false)
|
this.stateManager.setIsRecord(false)
|
||||||
// // 创建矩形
|
const rect = await this.layerManager.createRectLayer({ left: 200 })
|
||||||
// const rect = await this.layerManager.createRectLayer({
|
await this.layerManager.createStarLayer({ left: 400 })
|
||||||
// left: 400,
|
await this.layerManager.createArrowLayer({ left: 600 })
|
||||||
// top: 100,
|
this.layerManager.setActiveID(rect.info.id)
|
||||||
// })
|
this.stateManager.setIsRecord(true)
|
||||||
// //创建圆形
|
|
||||||
// const circle = await this.layerManager.createCircleLayer({
|
|
||||||
// left: 200,
|
|
||||||
// top: 200,
|
|
||||||
// })
|
|
||||||
// // 文字
|
|
||||||
// const text = await this.layerManager.createTextLayer('Hello World');
|
|
||||||
// this.layerManager.setActiveID(text.info.id)
|
|
||||||
// this.stateManager.setIsRecord(true)
|
|
||||||
/** 测试-结束 */
|
/** 测试-结束 */
|
||||||
|
|
||||||
this.resetZoom(false, true)// 画布居中
|
this.resetZoom(false, true)// 画布居中
|
||||||
@@ -289,6 +280,39 @@ export class CanvasManager {
|
|||||||
callback?.(true)
|
callback?.(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/** 导出画布为处理图片的JSON */
|
||||||
|
getCanvasDisUrlJSON() {
|
||||||
|
const canvas = this.canvas.toJSON()
|
||||||
|
const images = [];
|
||||||
|
var i = 0;
|
||||||
|
const create = (url) => {
|
||||||
|
const logo = `xxxxxxxx_${i++}_xxxxxxxx`;
|
||||||
|
images.push({ index: logo, url })
|
||||||
|
return logo
|
||||||
|
}
|
||||||
|
canvas.objects.forEach((object: any) => {
|
||||||
|
if (object.thumbnail) {
|
||||||
|
object.thumbnail = create(object.thumbnail)
|
||||||
|
}
|
||||||
|
if (object.src) {
|
||||||
|
object.src = create(object.src)
|
||||||
|
}
|
||||||
|
if (object.fill?.source) {
|
||||||
|
object.fill.source = create(object.fill.source)
|
||||||
|
}
|
||||||
|
if (object.info?.fill?.source) {
|
||||||
|
object.info.fill.source = create(object.info.fill.source)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return { canvas: JSON.stringify(canvas), images }
|
||||||
|
}
|
||||||
|
/** 处理JSON为正常画布 */
|
||||||
|
processCanvasDisUrlJSON(obj: { canvas: string, images: Object[] }) {
|
||||||
|
var json = obj.canvas;
|
||||||
|
const images = obj.images || []
|
||||||
|
images.forEach((v: any) => json = json.replace(new RegExp(v.index), v.url))
|
||||||
|
return JSON.parse(json)
|
||||||
|
}
|
||||||
dispose() {
|
dispose() {
|
||||||
this.animationManager?.dispose()
|
this.animationManager?.dispose()
|
||||||
this.eventManager?.dispose()
|
this.eventManager?.dispose()
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ export class LayerManager {
|
|||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
fill: '#000',
|
fill: '#000',
|
||||||
|
strokeWidth: 0,
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
info: {
|
info: {
|
||||||
id: createId("rect"),
|
id: createId("rect"),
|
||||||
@@ -212,6 +213,7 @@ export class LayerManager {
|
|||||||
const ellipseObject = new fabric.Ellipse({
|
const ellipseObject = new fabric.Ellipse({
|
||||||
radius: 50,
|
radius: 50,
|
||||||
fill: '#000',
|
fill: '#000',
|
||||||
|
strokeWidth: 0,
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
info: {
|
info: {
|
||||||
id: createId("ellipse"),
|
id: createId("ellipse"),
|
||||||
@@ -230,6 +232,7 @@ export class LayerManager {
|
|||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
fill: '#000',
|
fill: '#000',
|
||||||
|
strokeWidth: 0,
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
info: {
|
info: {
|
||||||
id: createId("triangle"),
|
id: createId("triangle"),
|
||||||
@@ -249,6 +252,7 @@ export class LayerManager {
|
|||||||
delete options.points
|
delete options.points
|
||||||
const starObject = new fabric.Polygon(getStarArr(width, height), {
|
const starObject = new fabric.Polygon(getStarArr(width, height), {
|
||||||
fill: '#000',
|
fill: '#000',
|
||||||
|
strokeWidth: 0,
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
info: {
|
info: {
|
||||||
id: createId("star"),
|
id: createId("star"),
|
||||||
|
|||||||
@@ -207,18 +207,15 @@ export class ObjectManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 修改属性
|
||||||
/** 修改透明度
|
|
||||||
* @param id 目标对象ID
|
* @param id 目标对象ID
|
||||||
* @param options 透明度参数
|
* @param options 参数
|
||||||
* @param options.opacity 透明度
|
|
||||||
* @param isRecord 是否记录
|
* @param isRecord 是否记录
|
||||||
*/
|
*/
|
||||||
async updateOpacity(id: string, options: any, isRecord: boolean) {
|
async updateProperty(id: string, options: any, isRecord: boolean) {
|
||||||
const object = this.getFillRepeatObject(id)
|
const object = this.canvasManager.getObjectById(id)
|
||||||
if (!object) return null
|
if (!object) return null
|
||||||
const opacity = options.opacity
|
object.set(options);
|
||||||
object.set("opacity", opacity);
|
|
||||||
this.canvasManager.renderAll()
|
this.canvasManager.renderAll()
|
||||||
if (isRecord) {
|
if (isRecord) {
|
||||||
this.stateManager.recordState()
|
this.stateManager.recordState()
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import { OperationType, OperationTypes } from "../tools/layerHelper";
|
|
||||||
import { fabric } from 'fabric-with-all'
|
|
||||||
/** 矩形工具管理器 */
|
|
||||||
export class RectToolManager {
|
|
||||||
// 管理器
|
|
||||||
canvasManager: any
|
|
||||||
stateManager: any
|
|
||||||
layerManager: any
|
|
||||||
|
|
||||||
isDragging: boolean = false
|
|
||||||
startX: number = 0
|
|
||||||
startY: number = 0
|
|
||||||
demoObject: any
|
|
||||||
tools = [
|
|
||||||
OperationType.RECTANGLE
|
|
||||||
]
|
|
||||||
constructor(options) {
|
|
||||||
this.canvasManager = options.canvasManager
|
|
||||||
this.stateManager = options.stateManager
|
|
||||||
this.layerManager = options.layerManager
|
|
||||||
}
|
|
||||||
mouseDownEvent(e) {
|
|
||||||
this.isDragging = true
|
|
||||||
this.startX = e.absolutePointer.x
|
|
||||||
this.startY = e.absolutePointer.y
|
|
||||||
const rect = new fabric.Rect({
|
|
||||||
left: this.startX,
|
|
||||||
top: this.startY,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
fill: '#000',
|
|
||||||
evented: false,
|
|
||||||
})
|
|
||||||
this.demoObject = rect
|
|
||||||
this.canvasManager.canvas.add(rect)
|
|
||||||
this.canvasManager.canvas.renderAll()
|
|
||||||
}
|
|
||||||
mouseMoveEvent(e) {
|
|
||||||
if (!this.isDragging) return;
|
|
||||||
var width = e.absolutePointer.x - this.startX
|
|
||||||
var height = e.absolutePointer.y - this.startY
|
|
||||||
var left = this.startX
|
|
||||||
var top = this.startY
|
|
||||||
if (width < 0) {
|
|
||||||
left += width
|
|
||||||
width = -width
|
|
||||||
}
|
|
||||||
if (height < 0) {
|
|
||||||
top += height
|
|
||||||
height = -height
|
|
||||||
}
|
|
||||||
this.demoObject.set({ width, height, left, top })
|
|
||||||
this.canvasManager.canvas.renderAll()
|
|
||||||
}
|
|
||||||
mouseUpEvent(e) {
|
|
||||||
if (!this.isDragging) return;
|
|
||||||
this.isDragging = false;
|
|
||||||
const object = this.demoObject.toJSON("evented")
|
|
||||||
if (object.width === 0) object.width = 100
|
|
||||||
if (object.height === 0) object.height = 100
|
|
||||||
this.layerManager.createRectLayer(object, true)
|
|
||||||
this.canvasManager.canvas.remove(this.demoObject)
|
|
||||||
this.canvasManager.canvas.renderAll()
|
|
||||||
|
|
||||||
}
|
|
||||||
dispose() { }
|
|
||||||
}
|
|
||||||
@@ -120,6 +120,7 @@ export class ShapeToolManager {
|
|||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
fill: '#000',
|
fill: '#000',
|
||||||
|
strokeWidth: 0,
|
||||||
})
|
})
|
||||||
return rect
|
return rect
|
||||||
}
|
}
|
||||||
@@ -159,6 +160,7 @@ export class ShapeToolManager {
|
|||||||
left: this.startX,
|
left: this.startX,
|
||||||
top: this.startY,
|
top: this.startY,
|
||||||
fill: '#000',
|
fill: '#000',
|
||||||
|
strokeWidth: 0,
|
||||||
})
|
})
|
||||||
return circle
|
return circle
|
||||||
}
|
}
|
||||||
@@ -180,6 +182,7 @@ export class ShapeToolManager {
|
|||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
fill: '#000',
|
fill: '#000',
|
||||||
|
strokeWidth: 0,
|
||||||
})
|
})
|
||||||
return triangle
|
return triangle
|
||||||
}
|
}
|
||||||
@@ -203,6 +206,7 @@ export class ShapeToolManager {
|
|||||||
fill: '#000',
|
fill: '#000',
|
||||||
strokeLineJoin: 'round', // 圆角连接
|
strokeLineJoin: 'round', // 圆角连接
|
||||||
strokeLineCap: 'round', // 圆角端点
|
strokeLineCap: 'round', // 圆角端点
|
||||||
|
strokeWidth: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
return star
|
return star
|
||||||
|
|||||||
Reference in New Issue
Block a user