@zhangqingcq/vgce 0.0.25 → 0.0.27
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +63 -63
- package/dist/style.css +1 -1
- package/dist/vgce.js +3463 -3371
- package/dist/vgce.umd.cjs +45 -45
- package/package.json +7 -10
- package/src/components/config/index.ts +5 -0
- package/src/components/svg-editor/center-panel/index.vue +43 -14
- package/src/components/svg-editor/connection-line/index.vue +1 -1
- package/src/components/svg-editor/connection-panel/index.vue +40 -5
- package/src/components/svg-editor/right-panel/index.vue +22 -2
- package/src/components/svg-editor/top-panel/index.vue +27 -3
- package/src/config/types.ts +1 -1
- package/src/stores/global/index.ts +4 -2
- package/src/utils/index.ts +30 -3
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@zhangqingcq/vgce",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.27",
|
4
4
|
"description": "Vector graphics configure editor. svg组态编辑器。基于vue3.3+ts+element-plus+vite",
|
5
5
|
"publishConfig": {
|
6
6
|
"access": "public"
|
@@ -23,14 +23,6 @@
|
|
23
23
|
"./*": "./*"
|
24
24
|
},
|
25
25
|
"types": "types/index.d.ts",
|
26
|
-
"scripts": {
|
27
|
-
"dev": "vite",
|
28
|
-
"build": "run-p type-check build-only",
|
29
|
-
"preview": "vite preview",
|
30
|
-
"build-only": "vite build",
|
31
|
-
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
32
|
-
"lib": "vue-tsc --noEmit --skipLibCheck && vite build --mode lib"
|
33
|
-
},
|
34
26
|
"repository": {
|
35
27
|
"type": "git",
|
36
28
|
"url": "git+https://github.com/RickyHeaven/VGCE.git"
|
@@ -91,5 +83,10 @@
|
|
91
83
|
"node": ">=18.16.0",
|
92
84
|
"npm": ">=9.5.1",
|
93
85
|
"pnpm": ">=8.6.0"
|
86
|
+
},
|
87
|
+
"scripts": {
|
88
|
+
"dev": "vite",
|
89
|
+
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
90
|
+
"lib": "vue-tsc --noEmit --skipLibCheck && vite build --mode lib"
|
94
91
|
}
|
95
|
-
}
|
92
|
+
}
|
@@ -65,6 +65,11 @@ export const connection_line_system: ISystemStraightLine = {
|
|
65
65
|
type: EConfigItemPropsType.InputNumber,
|
66
66
|
val: 2
|
67
67
|
},
|
68
|
+
point_r: {
|
69
|
+
title: '节点半径',
|
70
|
+
type: EConfigItemPropsType.InputNumber,
|
71
|
+
val: 4
|
72
|
+
},
|
68
73
|
point_position: {
|
69
74
|
title: '点坐标',
|
70
75
|
type: EConfigItemPropsType.JsonEdit,
|
@@ -57,7 +57,8 @@
|
|
57
57
|
const ct: Record<string, any> = {
|
58
58
|
MoveCanvas: 'grab',
|
59
59
|
Rotate: "url('/src/assets/icons/rotate.svg'), auto",
|
60
|
-
Connection: 'crosshair'
|
60
|
+
Connection: 'crosshair',
|
61
|
+
SetConnectionLineNode: 'crosshair'
|
61
62
|
}
|
62
63
|
const cursor_style = computed(() => {
|
63
64
|
if (Object.keys(ct).indexOf(globalStore.intention) > -1) {
|
@@ -119,6 +120,7 @@
|
|
119
120
|
})
|
120
121
|
|
121
122
|
let groupMoved = false
|
123
|
+
let lineNodeMoved = false
|
122
124
|
|
123
125
|
const dropEvent = (e: Record<string, any>) => {
|
124
126
|
if (globalStore.intention == EGlobalStoreIntention.None) {
|
@@ -342,6 +344,7 @@
|
|
342
344
|
(globalStore.intention === EGlobalStoreIntention.Select || globalStore.intention === EGlobalStoreIntention.Move)
|
343
345
|
) {
|
344
346
|
//有选中组件 移动单个组件
|
347
|
+
//todo 对齐功能开发
|
345
348
|
globalStore.handle_svg_info.info.x = x
|
346
349
|
|
347
350
|
globalStore.handle_svg_info.info.y = y
|
@@ -559,8 +562,9 @@
|
|
559
562
|
const _y =
|
560
563
|
globalStore.mouse_info.new_position_y - globalStore.mouse_info.position_y - svgEditLayoutStore.center_offset.y
|
561
564
|
const brotherPoint = globalStore.handle_svg_info.info.props.point_position.val[l - 2]
|
562
|
-
|
563
|
-
let
|
565
|
+
const pr = globalStore.handle_svg_info?.info.props.point_r?.val + 2 || 4
|
566
|
+
let ox = brotherPoint.x < _x ? -pr : brotherPoint.x > _x ? pr : 0
|
567
|
+
let oy = brotherPoint.y < _y ? -pr : brotherPoint.y > _y ? pr : 0
|
564
568
|
if (e.ctrlKey) {
|
565
569
|
//画竖线
|
566
570
|
globalStore.handle_svg_info.info.props.point_position.val[l - 1] = {
|
@@ -580,17 +584,32 @@
|
|
580
584
|
}
|
581
585
|
}
|
582
586
|
} else if (globalStore.intention === EGlobalStoreIntention.SetConnectionLineNode && globalStore.handle_svg_info) {
|
587
|
+
if (
|
588
|
+
!lineNodeMoved &&
|
589
|
+
(globalStore.connection_line_node_info.point_index ===
|
590
|
+
globalStore.handle_svg_info.info.props.point_position.val.length - 1 ||
|
591
|
+
globalStore.connection_line_node_info.point_index === 0)
|
592
|
+
) {
|
593
|
+
lineNodeMoved = true
|
594
|
+
}
|
595
|
+
const l = globalStore.handle_svg_info?.info.props.point_position.val.length
|
596
|
+
const _x = getSvgNowPosition(
|
597
|
+
globalStore.mouse_info.position_x,
|
598
|
+
globalStore.mouse_info.new_position_x,
|
599
|
+
globalStore.connection_line_node_info.init_pos.x
|
600
|
+
)
|
601
|
+
const _y = getSvgNowPosition(
|
602
|
+
globalStore.mouse_info.position_y,
|
603
|
+
globalStore.mouse_info.new_position_y,
|
604
|
+
globalStore.connection_line_node_info.init_pos.y
|
605
|
+
)
|
606
|
+
const brotherPoint = globalStore.handle_svg_info.info.props.point_position.val[l - 2]
|
607
|
+
const pr = globalStore.handle_svg_info?.info.props.point_r?.val + 2 || 4
|
608
|
+
let ox = brotherPoint.x < _x ? -pr : brotherPoint.x > _x ? pr : 0
|
609
|
+
let oy = brotherPoint.y < _y ? -pr : brotherPoint.y > _y ? pr : 0
|
583
610
|
globalStore.handle_svg_info.info.props.point_position.val[globalStore.connection_line_node_info.point_index] = {
|
584
|
-
x:
|
585
|
-
|
586
|
-
globalStore.mouse_info.new_position_x,
|
587
|
-
globalStore.connection_line_node_info.init_pos.x
|
588
|
-
),
|
589
|
-
y: getSvgNowPosition(
|
590
|
-
globalStore.mouse_info.position_y,
|
591
|
-
globalStore.mouse_info.new_position_y,
|
592
|
-
globalStore.connection_line_node_info.init_pos.y
|
593
|
-
)
|
611
|
+
x: _x + ox,
|
612
|
+
y: _y + oy
|
594
613
|
}
|
595
614
|
}
|
596
615
|
}
|
@@ -631,6 +650,17 @@
|
|
631
650
|
globalStore.intention = EGlobalStoreIntention.None
|
632
651
|
} else if (globalStore.intention === EGlobalStoreIntention.Connection) {
|
633
652
|
return
|
653
|
+
} else if (globalStore.intention === EGlobalStoreIntention.SetConnectionLineNode) {
|
654
|
+
//解绑锚点
|
655
|
+
if (lineNodeMoved && globalStore.handle_svg_info?.info.bind_anchors) {
|
656
|
+
if (globalStore.connection_line_node_info.point_index === 0) {
|
657
|
+
globalStore.handle_svg_info.info.bind_anchors.start = null
|
658
|
+
} else {
|
659
|
+
globalStore.handle_svg_info.info.bind_anchors.end = null
|
660
|
+
}
|
661
|
+
}
|
662
|
+
globalStore.intention = EGlobalStoreIntention.None
|
663
|
+
lineNodeMoved = false
|
634
664
|
} else if (globalStore.intention === EGlobalStoreIntention.SelectArea) {
|
635
665
|
//框选
|
636
666
|
globalStore.setHandleSvgInfo(null)
|
@@ -654,7 +684,6 @@
|
|
654
684
|
contextMenuStore.display = false
|
655
685
|
}
|
656
686
|
const onCanvasMouseDown = (e: MouseEvent) => {
|
657
|
-
//todo 画横线或垂线
|
658
687
|
const { clientX, clientY } = e
|
659
688
|
if (globalStore.intention === EGlobalStoreIntention.Connection) {
|
660
689
|
if (!globalStore.handle_svg_info) {
|
@@ -73,11 +73,40 @@
|
|
73
73
|
getCoordinateOffset(props.itemInfo.actual_bound.height, props.itemInfo.scale_y) +
|
74
74
|
radius.value
|
75
75
|
)
|
76
|
+
|
77
|
+
const anchorUp = (e: any, type: ELineBindAnchors) => {
|
78
|
+
if (globalStore.intention === EGlobalStoreIntention.SetConnectionLineNode) {
|
79
|
+
//移动连线首尾节点,绑定锚点
|
80
|
+
e.stopPropagation()
|
81
|
+
if (globalStore.handle_svg_info?.info.bind_anchors && globalStore.connection_line_node_info) {
|
82
|
+
if (globalStore.connection_line_node_info.point_index === 0) {
|
83
|
+
globalStore.handle_svg_info.info.bind_anchors.start = {
|
84
|
+
type: type,
|
85
|
+
target_id: props.itemInfo.id
|
86
|
+
}
|
87
|
+
} else if (
|
88
|
+
globalStore.connection_line_node_info.point_index ===
|
89
|
+
globalStore.handle_svg_info.info.props.point_position.val.length - 1
|
90
|
+
) {
|
91
|
+
globalStore.handle_svg_info.info.bind_anchors.end = {
|
92
|
+
type: type,
|
93
|
+
target_id: props.itemInfo.id
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
globalStore.intention = EGlobalStoreIntention.None
|
98
|
+
globalStore.setHandleSvgInfo(null)
|
99
|
+
nextTick(function () {
|
100
|
+
moveAnchors(props.itemInfo)
|
101
|
+
})
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
76
105
|
const bindAnchor = (e: any, type: ELineBindAnchors) => {
|
77
106
|
if (globalStore.intention === EGlobalStoreIntention.None) {
|
78
107
|
createLine(e, type, props.itemInfo)
|
79
108
|
} else if (globalStore.intention === EGlobalStoreIntention.Connection) {
|
80
|
-
|
109
|
+
//在连线的情况下,点击锚点,结束连线并绑定锚点
|
81
110
|
e.stopPropagation()
|
82
111
|
if (globalStore.handle_svg_info?.info.bind_anchors) {
|
83
112
|
globalStore.handle_svg_info.info.bind_anchors.end = {
|
@@ -102,19 +131,25 @@
|
|
102
131
|
stroke-width="2"
|
103
132
|
stroke="rgba(0,0,0,0)"
|
104
133
|
>
|
105
|
-
<g
|
134
|
+
<g
|
135
|
+
@mousedown="bindAnchor($event, ELineBindAnchors.TopCenter)"
|
136
|
+
@mouseup="anchorUp($event, ELineBindAnchors.TopCenter)"
|
137
|
+
>
|
106
138
|
<circle class="out-circle" :cx="cxT" :cy="cyT" :r="outRadius" fill-opacity=".3" />
|
107
139
|
<circle id="connection_tc" :cx="cxT" :cy="cyT" :r="radius" pointer-events="all" />
|
108
140
|
</g>
|
109
|
-
<g @mousedown="bindAnchor($event, ELineBindAnchors.Right)">
|
141
|
+
<g @mousedown="bindAnchor($event, ELineBindAnchors.Right)" @mouseup="anchorUp($event, ELineBindAnchors.Right)">
|
110
142
|
<circle class="out-circle" :cx="cxR" :cy="cyR" :r="outRadius" fill-opacity=".3" />
|
111
143
|
<circle id="connection_r" :cx="cxR" :cy="cyR" :r="radius" pointer-events="all" />
|
112
144
|
</g>
|
113
|
-
<g
|
145
|
+
<g
|
146
|
+
@mousedown="bindAnchor($event, ELineBindAnchors.BottomCenter)"
|
147
|
+
@mouseup="anchorUp($event, ELineBindAnchors.BottomCenter)"
|
148
|
+
>
|
114
149
|
<circle class="out-circle" :cx="cxB" :cy="cyB" :r="outRadius" fill-opacity=".3" />
|
115
150
|
<circle id="connection_bc" :cx="cxB" :cy="cyB" :r="radius" pointer-events="all" />
|
116
151
|
</g>
|
117
|
-
<g @mousedown="bindAnchor($event, ELineBindAnchors.Left)">
|
152
|
+
<g @mousedown="bindAnchor($event, ELineBindAnchors.Left)" @mouseup="anchorUp($event, ELineBindAnchors.Left)">
|
118
153
|
<circle class="out-circle" :cx="cxL" :cy="cyL" :r="outRadius" fill-opacity=".3" />
|
119
154
|
<circle id="connection_l" :cx="cxL" :cy="cyL" :r="radius" :style="{ cursor: 'crosshair' }" pointer-events="all" />
|
120
155
|
</g>
|
@@ -219,11 +219,11 @@
|
|
219
219
|
<el-input v-model="globalStore.handle_svg_info!.info.id" />
|
220
220
|
</el-form-item>
|
221
221
|
<div
|
222
|
-
v-for="(e, k)
|
222
|
+
v-for="(e, k) of globalStore.handle_svg_info!.info.state"
|
223
223
|
:key="'state' + String(k)"
|
224
224
|
v-if="globalStore.handle_svg_info!.info.state"
|
225
225
|
>
|
226
|
-
<el-form-item class="props-row" :label="String(k)" size="small"> {{ e
|
226
|
+
<el-form-item class="props-row" :label="String(k)" size="small"> {{ e!.default }}</el-form-item>
|
227
227
|
|
228
228
|
<el-form-item
|
229
229
|
v-if="k === 'OnOff'"
|
@@ -243,6 +243,26 @@
|
|
243
243
|
</el-form-item>
|
244
244
|
</div>
|
245
245
|
<dynamic-el-form-item :obj-info="globalStore.handle_svg_info!.info.props" code />
|
246
|
+
<el-form-item
|
247
|
+
:label="globalStore.handle_svg_info!.info.animations.type.title"
|
248
|
+
size="small"
|
249
|
+
v-if="globalStore.handle_svg_info!.info.animations"
|
250
|
+
>
|
251
|
+
<el-select v-model="globalStore.handle_svg_info!.info.animations.type.val" placeholder="Select" size="small">
|
252
|
+
<el-option
|
253
|
+
v-for="item in globalStore.handle_svg_info!.info.animations.type.options"
|
254
|
+
:key="item.value"
|
255
|
+
:label="item.label"
|
256
|
+
:value="item.value"
|
257
|
+
/>
|
258
|
+
</el-select>
|
259
|
+
</el-form-item>
|
260
|
+
|
261
|
+
<dynamic-el-form-item
|
262
|
+
:obj-info="globalStore.handle_svg_info!.info.animations"
|
263
|
+
v-if="globalStore.handle_svg_info!.info.animations?.type.val !== 'None'"
|
264
|
+
:hide="['type', 'repeatCount']"
|
265
|
+
/>
|
246
266
|
<bind-anchor
|
247
267
|
v-if="globalStore.handle_svg_info?.info.type === EDoneJsonType.ConnectionLine"
|
248
268
|
v-model="globalStore.handle_svg_info!.info!.bind_anchors!.start"
|
@@ -173,12 +173,30 @@
|
|
173
173
|
</div>
|
174
174
|
<div class="el-divider el-divider--horizontal" role="separator" style="--el-border-style: solid"></div>
|
175
175
|
<div class="font-bold mb-10px text-15px guide-title">横线和竖线</div>
|
176
|
-
<div
|
176
|
+
<div class="guide-text"
|
177
|
+
><p>
|
178
|
+
点击编辑器顶部连线按钮会进入连线状态,该状态下在画板点击鼠标左键即可开始画线,移动并再次点击左键创建新的节点,右键结束画线并退出连线状态;</p
|
179
|
+
>
|
180
|
+
<p>画线的时候按住ctrl即可画竖线,按住shift即可画横线</p></div
|
181
|
+
>
|
182
|
+
<div class="el-divider el-divider--horizontal" role="separator" style="--el-border-style: solid"></div>
|
183
|
+
<div class="font-bold mb-10px text-15px guide-title">绑定锚点</div>
|
184
|
+
<div class="guide-text">
|
185
|
+
<p
|
186
|
+
>组件在未选中时,鼠标经过组件时会出现锚点,鼠标悬浮在锚点上时,会有交互样式,点击左键即可以锚点为起点创建连线;</p
|
187
|
+
>
|
188
|
+
<p>同样,在连线时鼠标经过锚点时,会出现锚点交互样式,点击左键即可以锚点为连线终点结束连线;</p>
|
189
|
+
<p>这两种情况下的连线会绑定组件上的锚点,如需解绑,参考下面的‘锚点解绑’;</p>
|
190
|
+
<p>同样,也可先画线,再选中连线绑定想要绑定的组件锚点(连线只有首尾节点可以绑定锚点)</p>
|
191
|
+
</div>
|
177
192
|
<div class="el-divider el-divider--horizontal" role="separator" style="--el-border-style: solid"></div>
|
178
193
|
<div class="font-bold mb-10px text-15px guide-title">线段选中</div>
|
179
|
-
<div
|
180
|
-
|
194
|
+
<div>
|
195
|
+
若线段绑定了锚点,移动线段,绑定的锚点不会移动。若是想将线段整体移动,需要先解绑,参考下面的‘锚点解绑’
|
181
196
|
</div>
|
197
|
+
<div class="el-divider el-divider--horizontal" role="separator" style="--el-border-style: solid"></div>
|
198
|
+
<div class="font-bold mb-10px text-15px guide-title">锚点解绑</div>
|
199
|
+
<div style="padding-bottom: 14px"> 选中线段,在右侧‘数据’栏里解除绑定;或者鼠标左键拖拽连线上需要解绑的节点</div>
|
182
200
|
</el-scrollbar>
|
183
201
|
</el-dialog>
|
184
202
|
</template>
|
@@ -197,6 +215,12 @@
|
|
197
215
|
.el-divider {
|
198
216
|
border-color: #f1f1f5;
|
199
217
|
}
|
218
|
+
|
219
|
+
.guide-text {
|
220
|
+
p {
|
221
|
+
line-height: 1.9;
|
222
|
+
}
|
223
|
+
}
|
200
224
|
</style>
|
201
225
|
<style lang="less">
|
202
226
|
.guide-dialog {
|
package/src/config/types.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { config } from '@/config'
|
2
2
|
import type { IConfigItem } from '@/config/types'
|
3
|
-
import { isOfType, objectDeepClone, setSvgActualInfo } from '@/utils'
|
3
|
+
import { isOfType, objectDeepClone, setSvgActualInfo, unbindAnchors } from '@/utils'
|
4
4
|
import { EGlobalStoreIntention, EMouseInfoState, EScaleInfoType } from './types'
|
5
5
|
import type { IDoneJson, IGlobalStore, IMouseInfo, IScaleInfo } from './types'
|
6
6
|
import { useHistoryRecord } from '@/hooks'
|
@@ -90,7 +90,9 @@ export const useGlobalStore = defineStore('global-store', {
|
|
90
90
|
},
|
91
91
|
spliceDoneJson(index: number) {
|
92
92
|
const globalStore = useGlobalStore()
|
93
|
-
globalStore.done_json.splice(index, 1)
|
93
|
+
const t = globalStore.done_json.splice(index, 1)
|
94
|
+
const item = t[0]
|
95
|
+
unbindAnchors(item.id)
|
94
96
|
useHistoryRecord(globalStore.done_json)
|
95
97
|
}
|
96
98
|
}
|
package/src/utils/index.ts
CHANGED
@@ -286,15 +286,39 @@ export const moveAnchors = (done_json: IDoneJson) => {
|
|
286
286
|
if (d.type === EDoneJsonType.ConnectionLine) {
|
287
287
|
if (d.bind_anchors?.start?.target_id === done_json.id) {
|
288
288
|
const a = getAnchorPosByAnchorType(d.bind_anchors.start.type, done_json)
|
289
|
-
d.props.point_position.val[0] = {
|
289
|
+
d.props.point_position.val[0] = {
|
290
|
+
x: a.x - d.x,
|
291
|
+
y: a.y - d.y
|
292
|
+
}
|
290
293
|
}
|
291
294
|
if (d.bind_anchors?.end?.target_id === done_json.id) {
|
292
295
|
const a = getAnchorPosByAnchorType(d.bind_anchors.end.type, done_json)
|
293
|
-
d.props.point_position.val[d.props.point_position.val.length - 1] = {
|
296
|
+
d.props.point_position.val[d.props.point_position.val.length - 1] = {
|
297
|
+
x: a.x - d.x,
|
298
|
+
y: a.y - d.y
|
299
|
+
}
|
294
300
|
}
|
295
301
|
}
|
296
302
|
}
|
297
303
|
}
|
304
|
+
/**
|
305
|
+
* 解绑连线的锚点
|
306
|
+
* @param id 被绑定的组件的id
|
307
|
+
*/
|
308
|
+
export const unbindAnchors = (id: string) => {
|
309
|
+
const globalStore = useGlobalStore(pinia)
|
310
|
+
for (let d of globalStore.done_json) {
|
311
|
+
if (d.type === EDoneJsonType.ConnectionLine) {
|
312
|
+
if (d.bind_anchors?.start?.target_id === id) {
|
313
|
+
d.bind_anchors.start = null
|
314
|
+
}
|
315
|
+
if (d.bind_anchors?.end?.target_id === id) {
|
316
|
+
d.bind_anchors.end = null
|
317
|
+
}
|
318
|
+
}
|
319
|
+
}
|
320
|
+
}
|
321
|
+
|
298
322
|
/**
|
299
323
|
* 根据锚点类型获取锚点坐标
|
300
324
|
* @param anchor_type
|
@@ -464,7 +488,10 @@ export const createLine = (e: MouseEvent, type?: ELineBindAnchors, itemInfo?: ID
|
|
464
488
|
width: 0,
|
465
489
|
height: 0
|
466
490
|
},
|
467
|
-
center_position: {
|
491
|
+
center_position: {
|
492
|
+
x: 0,
|
493
|
+
y: 0
|
494
|
+
},
|
468
495
|
point_coordinate: {
|
469
496
|
tl: {
|
470
497
|
x: 0,
|