@zhangqingcq/vgce 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhangqingcq/vgce",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Vector graphics configure editor. svg组态编辑器。基于vue3.3+ts+element-plus+vite",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -109,7 +109,39 @@ body {
109
109
 
110
110
  .tree-v {
111
111
  --el-color-primary-light-9: @listActiveColor;
112
+
112
113
  &.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
113
114
  color: #fff;
114
115
  }
115
116
  }
117
+
118
+ .coverLayer {
119
+ position: absolute;
120
+ left: 0;
121
+ top: 0;
122
+ bottom: 0;
123
+ right: 0;
124
+ }
125
+
126
+ .can-not-select {
127
+ user-select: none;
128
+ }
129
+
130
+ .canvasInfoTxt {
131
+ color: #bbb;
132
+ position: absolute;
133
+ left: 20px;
134
+ bottom: 10px;
135
+ font-size: 12px;
136
+
137
+ .icoP {
138
+ margin-left: 10px;
139
+ position: relative;
140
+ top: 2px;
141
+ cursor: pointer;
142
+
143
+ &:hover {
144
+ color: @activeIconOut;
145
+ }
146
+ }
147
+ }
@@ -18,7 +18,8 @@
18
18
  randomString,
19
19
  resetHandlePointOld,
20
20
  setSvgActualInfo,
21
- stopEvent
21
+ getZoomPosition,
22
+ myFixed
22
23
  } from '@/utils'
23
24
  import {
24
25
  calculateBottom,
@@ -494,10 +495,10 @@
494
495
  //算出缩放倍数
495
496
  if (globalStore.handle_svg_info && new_length.width > 0 && new_length.height > 0) {
496
497
  const scale_x = !new_length.is_old_width
497
- ? parseFloat((new_length.width / globalStore.handle_svg_info.info.actual_bound.width).toFixed(3))
498
+ ? myFixed(new_length.width / globalStore.handle_svg_info.info.actual_bound.width, 3)
498
499
  : 1
499
500
  const scale_y = !new_length.is_old_height
500
- ? parseFloat((new_length.height / globalStore.handle_svg_info.info.actual_bound.height).toFixed(3))
501
+ ? myFixed(new_length.height / globalStore.handle_svg_info.info.actual_bound.height, 3)
501
502
  : 1
502
503
  const newCenterPoint = getCenterPoint(curPosition, globalStore.scale_info.symmetric_point)
503
504
  if (
@@ -549,8 +550,9 @@
549
550
  globalStore.handle_svg_info.info.x
550
551
  ) /
551
552
  (Math.PI / 180)
552
- globalStore.handle_svg_info.info.rotate = parseFloat(
553
- (globalStore.rotate_info.angle + rotateDegreeAfter - rotateDegreeBefore).toFixed(2)
553
+ globalStore.handle_svg_info.info.rotate = myFixed(
554
+ globalStore.rotate_info.angle + rotateDegreeAfter - rotateDegreeBefore,
555
+ 2
554
556
  )
555
557
  } else if (globalStore.intention === EGlobalStoreIntention.Connection && globalStore.handle_svg_info) {
556
558
  //鼠标移动的实时位置(相对于连线起始点,只在创建第一个点时记录了鼠标原始位置)
@@ -766,9 +768,11 @@
766
768
  function onMousewheel(e: any) {
767
769
  if (e?.wheelDelta) {
768
770
  if (e.wheelDelta > 0) {
769
- configStore.svg.scale = parseFloat((configStore.svg.scale + 0.1).toFixed(1))
770
- } else {
771
- configStore.svg.scale = parseFloat((configStore.svg.scale - 0.1).toFixed(1))
771
+ configStore.svg.scale = myFixed(configStore.svg.scale + 0.1, 1)
772
+ getZoomPosition(e, configStore.svg.scale, svgEditLayoutStore.center_offset, true)
773
+ } else if (configStore.svg.scale > 0.1) {
774
+ configStore.svg.scale = myFixed(configStore.svg.scale - 0.1, 1)
775
+ getZoomPosition(e, configStore.svg.scale, svgEditLayoutStore.center_offset, false)
772
776
  }
773
777
  }
774
778
  }
@@ -817,10 +821,10 @@
817
821
  scale_y: number
818
822
  ) => {
819
823
  return {
820
- x: parseFloat((actual_bound.x - (actual_bound.width / 2) * scale_x + actual_bound.width / 2).toFixed(1)),
821
- y: parseFloat((actual_bound.y - (actual_bound.height / 2) * scale_y + actual_bound.height / 2).toFixed(1)),
822
- width: parseFloat((actual_bound.width * scale_x).toFixed(1)),
823
- height: parseFloat((actual_bound.height * scale_y).toFixed(1))
824
+ x: myFixed(actual_bound.x - (actual_bound.width / 2) * scale_x + actual_bound.width / 2, 1),
825
+ y: myFixed(actual_bound.y - (actual_bound.height / 2) * scale_y + actual_bound.height / 2, 1),
826
+ width: myFixed(actual_bound.width * scale_x, 1),
827
+ height: myFixed(actual_bound.height * scale_y, 1)
824
828
  }
825
829
  }
826
830
  const onHandleKeyDown = (e: KeyboardEvent) => {
@@ -959,7 +963,7 @@
959
963
  @mousewheel="onMousewheel"
960
964
  >
961
965
  <slot name="background" />
962
- <div style="position: absolute; left: 0; top: 0; bottom: 0; right: 0">
966
+ <div class="coverLayer">
963
967
  <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
964
968
  <defs>
965
969
  <pattern id="pattern_grid" patternUnits="userSpaceOnUse" x="0" y="0" width="20" height="20">
@@ -980,7 +984,6 @@
980
984
  :key="item.id"
981
985
  :transform="`translate(${item.x},${item.y})rotate(0)scale(1)`"
982
986
  v-show="item.display"
983
- @mousewheel="stopEvent"
984
987
  >
985
988
  <g :class="`${getCommonClass(item)}`">
986
989
  <g
@@ -313,7 +313,8 @@
313
313
  <el-form-item label="行为" size="small">
314
314
  <el-select v-model="item.action" @change="addEventList($event, item)">
315
315
  <el-option value="ChangeAttr" label="改变属性" />
316
- <el-option value="JavaScript" label="执行JavaScript" />
316
+ <el-option value="JavaScript" label="执行 JavaScript" />
317
+ <el-option value="Emit" label="emit 事件" />
317
318
  </el-select>
318
319
  </el-form-item>
319
320
 
@@ -1,11 +1,15 @@
1
1
  <script setup lang="ts">
2
2
  import { pinia } from '@/hooks'
3
+ import { ElIcon } from 'element-plus'
4
+ import { Aim } from '@element-plus/icons-vue'
3
5
  import { useGlobalStore } from '@/stores/global'
4
6
  import { EGlobalStoreIntention, EMouseInfoState } from '@/stores/global/types'
5
7
  import type { IDoneJson } from '@/stores/global/types'
6
8
  import {
7
9
  componentsRegister,
8
10
  getCommonClass,
11
+ getZoomPosition,
12
+ myFixed,
9
13
  preventDefault,
10
14
  prosToVBind,
11
15
  setArrItemByID,
@@ -25,11 +29,12 @@
25
29
 
26
30
  setEditorLoadTime()
27
31
 
28
- const emit = defineEmits(['onMessage'])
32
+ const emit = defineEmits(['onMessage', 'onEvent'])
29
33
  const props = withDefaults(
30
- defineProps<{ vueComp?: Record<string, any>; data?: IDataModel; canvasDrag?: boolean }>(),
34
+ defineProps<{ vueComp?: Record<string, any>; data?: IDataModel; canvasDrag?: boolean; showCanvasInfo?: boolean }>(),
31
35
  {
32
- canvasDrag: true
36
+ canvasDrag: true,
37
+ showCanvasInfo: true
33
38
  }
34
39
  )
35
40
  const globalStore = useGlobalStore(pinia)
@@ -72,14 +77,18 @@
72
77
  return
73
78
  }
74
79
  const { clientX, clientY } = e
75
- globalStore.mouse_info.new_position_x =
76
- globalStore.mouse_info.now_position_x + clientX - globalStore.mouse_info.position_x
77
- globalStore.mouse_info.new_position_y =
78
- globalStore.mouse_info.now_position_y + clientY - globalStore.mouse_info.position_y
80
+ globalStore.mouse_info.new_position_x = Math.round(clientX / preview_data.config.svg.scale)
81
+ globalStore.mouse_info.new_position_y = Math.round(clientY / preview_data.config.svg.scale)
82
+ const x = Math.round(
83
+ globalStore.mouse_info.new_position_x - globalStore.mouse_info.position_x + globalStore.mouse_info.now_position_x
84
+ )
85
+ const y = Math.round(
86
+ globalStore.mouse_info.new_position_y - globalStore.mouse_info.position_y + globalStore.mouse_info.now_position_y
87
+ )
79
88
  if (globalStore.intention == EGlobalStoreIntention.MoveCanvas) {
80
89
  //移动画布
81
- preview_data.layout_center.x = globalStore.mouse_info.new_position_x
82
- preview_data.layout_center.y = globalStore.mouse_info.new_position_y
90
+ preview_data.layout_center.x = x
91
+ preview_data.layout_center.y = y
83
92
  }
84
93
  }
85
94
  const onCanvasMouseUp = () => {
@@ -106,21 +115,23 @@
106
115
  globalStore.intention = EGlobalStoreIntention.MoveCanvas
107
116
  globalStore.mouse_info = {
108
117
  state: EMouseInfoState.Down,
109
- position_x: clientX,
110
- position_y: clientY,
118
+ position_x: Math.round(clientX / preview_data.config.svg.scale),
119
+ position_y: Math.round(clientY / preview_data.config.svg.scale),
111
120
  now_position_x: preview_data.layout_center.x,
112
121
  now_position_y: preview_data.layout_center.y,
113
- new_position_x: preview_data.layout_center.x,
114
- new_position_y: preview_data.layout_center.y
122
+ new_position_x: 0,
123
+ new_position_y: 0
115
124
  }
116
125
  }
117
126
 
118
127
  function onMousewheel(e: any) {
119
128
  if (e?.wheelDelta) {
120
129
  if (e.wheelDelta > 0) {
121
- preview_data.config.svg.scale = parseFloat((preview_data.config.svg.scale + 0.1).toFixed(1))
130
+ preview_data.config.svg.scale = myFixed(preview_data.config.svg.scale + 0.1, 1)
131
+ getZoomPosition(e, preview_data.config.svg.scale, preview_data.layout_center, true)
122
132
  } else {
123
- preview_data.config.svg.scale = parseFloat((preview_data.config.svg.scale - 0.1).toFixed(1))
133
+ preview_data.config.svg.scale = myFixed(preview_data.config.svg.scale - 0.1, 1)
134
+ getZoomPosition(e, preview_data.config.svg.scale, preview_data.layout_center, false)
124
135
  }
125
136
  }
126
137
  }
@@ -156,7 +167,6 @@
156
167
  return { cursor: t ? 'pointer' : 'default' }
157
168
  }
158
169
  const eventHandle = (root: IDoneJson, type: EEventType) => {
159
- console.log(root.events, type)
160
170
  if (root.events?.length > 0) {
161
171
  for (let e of root.events) {
162
172
  if (e.type === type) {
@@ -204,6 +214,28 @@
204
214
  } else if (e.action === EEventAction.JavaScript) {
205
215
  const t = new Function(e.scripts)
206
216
  t()
217
+ } else if (e.action === EEventAction.Emit) {
218
+ const _k: Array<keyof IDoneJson> = [
219
+ 'id',
220
+ 'name',
221
+ 'common_animations',
222
+ 'display',
223
+ 'props',
224
+ 'title',
225
+ 'type',
226
+ 'x',
227
+ 'y'
228
+ ]
229
+ const _r: Record<string, any> = {}
230
+ _k.forEach((x) => {
231
+ if (root?.hasOwnProperty?.(x)) {
232
+ _r[x] = root[x]
233
+ }
234
+ })
235
+ emit('onEvent', {
236
+ event: e,
237
+ target: _r
238
+ })
207
239
  }
208
240
  }
209
241
  }
@@ -230,6 +262,13 @@
230
262
  }
231
263
  }
232
264
 
265
+ const resetCanvas = () => {
266
+ preview_data.layout_center = {
267
+ x: 0,
268
+ y: 0
269
+ }
270
+ }
271
+
233
272
  const connectNet = () => {
234
273
  const m = preview_data.config.net.mqtt
235
274
  if (m && m.url && m.user && m.pwd && m.topics) {
@@ -240,12 +279,12 @@
240
279
  //用户拿到消息后可以配合setNodeAttrByID方法更新界面
241
280
  //setNodeAttrByID的参数id可以在传给本组件的props.data(用户传进来的,自然知道值是多少)里done_json找到
242
281
  /*如何找到指定组件的两种方案:
243
- 1.用你的项目里前后端约定的svg组件唯一标识符替换掉编辑器生成的id(必须保证唯一),然后调用setNodeAttrByID改变组件属性。
244
- 2.如果不想改动id(避免因不能保证手动改过的id唯一性导致编辑器功能异常),可以在config里给想要改变的组件的配置文件的props里增加一个字段,
245
- 如deviceCode(svg-text的配置文件里有被注释的例子),然后在编辑组态时,给对应组件填上对应的deviceCode(这样deviceCode就和组件id实现
246
- 了映射关系),并保存,后台给前台推MQTT消息时带上指定的deviceCode,前台预览时,在收到MQTT消息后,凭借消息里的deviceCode在done_json
247
- 找到组件的id(可以用vue的computed计算一份deviceCode和id的映射关系存到一个对象里,这样在需要id时可直接在计算出的对象凭借deviceCode
248
- 直接取到),即可用setNodeAttrByID改变组件属性*/
282
+ 1.用你的项目里前后端约定的svg组件唯一标识符替换掉编辑器生成的id(必须保证唯一),然后调用setNodeAttrByID改变组件属性。
283
+ 2.如果不想改动id(避免因不能保证手动改过的id唯一性导致编辑器功能异常),可以在config里给想要改变的组件的配置文件的props里增加一个字段,
284
+ 如deviceCode(svg-text的配置文件里有被注释的例子),然后在编辑组态时,给对应组件填上对应的deviceCode(这样deviceCode就和组件id实现
285
+ 了映射关系),并保存,后台给前台推MQTT消息时带上指定的deviceCode,前台预览时,在收到MQTT消息后,凭借消息里的deviceCode在done_json
286
+ 找到组件的id(可以用vue的computed计算一份deviceCode和id的映射关系存到一个对象里,这样在需要id时可直接在计算出的对象凭借deviceCode
287
+ 直接取到),即可用setNodeAttrByID改变组件属性*/
249
288
  emit('onMessage', {
250
289
  topics,
251
290
  message
@@ -287,12 +326,12 @@
287
326
  @contextmenu="preventDefault"
288
327
  >
289
328
  <slot name="background" />
290
- <div style="position: absolute; left: 0; top: 0; bottom: 0; right: 0">
329
+ <div class="coverLayer">
291
330
  <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
292
331
  <g
293
- :transform="`translate(${preview_data.layout_center.x},${preview_data.layout_center.y})rotate(${0})scale(${
294
- preview_data.config.svg.scale
295
- })`"
332
+ :transform="`translate(${preview_data.layout_center.x * preview_data.config.svg.scale},${
333
+ preview_data.layout_center.y * preview_data.config.svg.scale
334
+ })rotate(${0})scale(${preview_data.config.svg.scale})`"
296
335
  >
297
336
  <g
298
337
  v-for="item in preview_data.done_json"
@@ -304,7 +343,6 @@
304
343
  @mousedown="stopEvent"
305
344
  @mousemove="stopEvent"
306
345
  @mouseup="stopEvent"
307
- @mousewheel="stopEvent"
308
346
  >
309
347
  <g :class="`${getCommonClass(item)}`">
310
348
  <g
@@ -371,6 +409,15 @@
371
409
  </g>
372
410
  </svg>
373
411
  </div>
412
+
413
+ <div class="can-not-select canvasInfoTxt" v-show="showCanvasInfo"
414
+ >缩放倍数:{{ preview_data.config.svg.scale }}倍,画布位置:{{ myFixed(preview_data.layout_center.x, 2) }},{{
415
+ myFixed(preview_data.layout_center.y, 2)
416
+ }}
417
+ <el-icon class="icoP" :size="14" @click="resetCanvas" title="重置位置">
418
+ <Aim />
419
+ </el-icon>
420
+ </div>
374
421
  </div>
375
422
  </template>
376
423
 
@@ -2,7 +2,7 @@
2
2
  import { pinia } from '@/hooks'
3
3
  import { useSvgEditLayoutStore } from '@/stores/svg-edit-layout'
4
4
  import { useConfigStore } from '@/stores/config'
5
- import { preventDefault, stopEvent } from '@/utils'
5
+ import { myFixed, preventDefault } from '@/utils'
6
6
 
7
7
  const emit = defineEmits(['onLineMouseUp'])
8
8
  const props = withDefaults(
@@ -144,12 +144,12 @@
144
144
  if (array.length === 0) {
145
145
  for (let i = 0; i < length; i += props.stepLength) {
146
146
  if (i % props.stepLength === 0) {
147
- array.push({ id: Number((i / configStore.svg.scale - start).toFixed(0)) })
147
+ array.push({ id: myFixed(i / configStore.svg.scale - start, 0) })
148
148
  }
149
149
  }
150
150
  } else {
151
151
  array.forEach((e, i) => {
152
- e.id = Number(((i * props.stepLength) / configStore.svg.scale - start).toFixed(0))
152
+ e.id = myFixed((i * props.stepLength) / configStore.svg.scale - start, 0)
153
153
  })
154
154
  }
155
155
  }
@@ -311,10 +311,14 @@
311
311
  <span v-for="(item, index) in yScale" :style="{ top: index * stepLength + 'px' }" class="n">{{ item.id }}</span>
312
312
  </div>
313
313
  <div :style="{ top: verticalDottedTop + 'px' }" class="vue-ruler-ref-dot-h"
314
- ><span>{{ Math.round((verticalDottedTop - rulerHeight) / configStore.svg.scale) }}</span></div
314
+ ><span>{{
315
+ Math.round((verticalDottedTop - rulerHeight) / configStore.svg.scale - svgEditLayoutStore.center_offset.y)
316
+ }}</span></div
315
317
  >
316
318
  <div :style="{ left: horizontalDottedLeft + 'px' }" class="vue-ruler-ref-dot-v"
317
- ><span>{{ Math.round((horizontalDottedLeft - rulerWidth) / configStore.svg.scale) }}</span></div
319
+ ><span>{{
320
+ Math.round((horizontalDottedLeft - rulerWidth) / configStore.svg.scale - svgEditLayoutStore.center_offset.x)
321
+ }}</span></div
318
322
  >
319
323
  <div
320
324
  v-for="item in lineList"
@@ -80,6 +80,7 @@ export enum EEventType {
80
80
  export enum EEventAction {
81
81
  ChangeAttr = 'ChangeAttr',
82
82
  JavaScript = 'JavaScript',
83
+ Emit = 'Emit',
83
84
  Null = ''
84
85
  }
85
86
 
@@ -18,6 +18,10 @@ export const preventDefault = (e: any) => {
18
18
  e.preventDefault()
19
19
  }
20
20
 
21
+ export const myFixed = (d: number, n: number) => {
22
+ return Number(d.toFixed(n))
23
+ }
24
+
21
25
  export function componentsRegister(data?: Record<string, any>) {
22
26
  //注册所有组件
23
27
  const instance = getCurrentInstance()
@@ -95,19 +99,17 @@ export const calculateRotatedPointCoordinate = (
95
99
  */
96
100
 
97
101
  return {
98
- x: parseFloat(
99
- (
100
- (point.x - center.x) * Math.cos(angleToRadian(rotate)) -
102
+ x: myFixed(
103
+ (point.x - center.x) * Math.cos(angleToRadian(rotate)) -
101
104
  (point.y - center.y) * Math.sin(angleToRadian(rotate)) +
102
- center.x
103
- ).toFixed(1)
105
+ center.x,
106
+ 1
104
107
  ),
105
- y: parseFloat(
106
- (
107
- (point.x - center.x) * Math.sin(angleToRadian(rotate)) +
108
+ y: myFixed(
109
+ (point.x - center.x) * Math.sin(angleToRadian(rotate)) +
108
110
  (point.y - center.y) * Math.cos(angleToRadian(rotate)) +
109
- center.y
110
- ).toFixed(1)
111
+ center.y,
112
+ 1
111
113
  )
112
114
  }
113
115
  }
@@ -190,10 +192,10 @@ export const setSvgActualInfo = (done_json: IDoneJson, resize?: boolean) => {
190
192
  }
191
193
  } else {
192
194
  const BBox = (queryBbox as SVGGraphicsElement).getBBox()
193
- x = parseFloat(BBox.x.toFixed(0))
194
- y = parseFloat(BBox.y.toFixed(0))
195
- width = parseFloat(BBox.width.toFixed(0))
196
- height = parseFloat(BBox.height.toFixed(0))
195
+ x = myFixed(BBox.x, 0)
196
+ y = myFixed(BBox.y, 0)
197
+ width = myFixed(BBox.width, 0)
198
+ height = myFixed(BBox.height, 0)
197
199
  }
198
200
  if (
199
201
  rectBBox &&
@@ -568,3 +570,19 @@ export const createLine = (e: MouseEvent, type?: ELineBindAnchors, itemInfo?: ID
568
570
  new_position_y: 0
569
571
  }
570
572
  }
573
+
574
+ export const getZoomPosition = (e: Record<string, any>, scale: any, center: Record<string, any>, isAdd: boolean) => {
575
+ /*鼠标位置*/
576
+ const offsetX = e.layerX
577
+ const offsetY = e.layerY
578
+ /*上次的画布位置*/
579
+ const tx = center.x
580
+ const ty = center.y
581
+ /*上次的scale*/
582
+ const ts = myFixed(isAdd ? scale - 0.1 : scale + 0.1, 1)
583
+ /*画布在没有缩放的时候移动位置*/
584
+ const lx = myFixed(tx + (offsetX * (ts - 1)) / ts, 1)
585
+ const ly = myFixed(ty + (offsetY * (ts - 1)) / ts, 1)
586
+ center.x = myFixed(lx / scale - ((offsetX - lx) * (scale - 1)) / scale, 2)
587
+ center.y = myFixed(ly / scale - ((offsetY - ly) * (scale - 1)) / scale, 2)
588
+ }
@@ -15,7 +15,7 @@
15
15
  <template>
16
16
  <div class="previewPage">
17
17
  <svg-viewer :data="store.data" />
18
- <el-button @click="back" class="backBtn" :icon="ArrowLeftBold">返回</el-button>
18
+ <el-button type="text" @click="back" class="backBtn" :icon="ArrowLeftBold">返回</el-button>
19
19
  </div>
20
20
  </template>
21
21