traffic-diagram 1.0.9

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.
Files changed (51) hide show
  1. package/.trae/documents/Update README.md for Traffic Lane Component.md +37 -0
  2. package/.trae/documents/npm Publish Plan.md +27 -0
  3. package/.trae/documents/plan_20251222_071731.md +64 -0
  4. package/.trae/documents/plan_20251222_074833.md +23 -0
  5. package/.trae/documents/plan_20251222_081357.md +23 -0
  6. package/README.md +311 -0
  7. package/dist/demo.html +10 -0
  8. package/dist/static/css/chunk-src.895fe3a6.css +1 -0
  9. package/dist/static/img/demo.160f90e8.png +0 -0
  10. package/dist/static/img/demo.18a57edc.gif +0 -0
  11. package/dist/static/img/demo.c7910627.gif +0 -0
  12. package/dist/traffic-diagram.common.chunk-lodash.js +17212 -0
  13. package/dist/traffic-diagram.common.chunk-src.js +2015 -0
  14. package/dist/traffic-diagram.common.js +33788 -0
  15. package/dist/traffic-diagram.umd.chunk-lodash.js +17212 -0
  16. package/dist/traffic-diagram.umd.chunk-src.js +2015 -0
  17. package/dist/traffic-diagram.umd.js +33798 -0
  18. package/dist/traffic-diagram.umd.min.chunk-lodash.js +9 -0
  19. package/dist/traffic-diagram.umd.min.chunk-src.js +1 -0
  20. package/dist/traffic-diagram.umd.min.js +12 -0
  21. package/package.json +46 -0
  22. package/src/App.vue +269 -0
  23. package/src/assets/images/green-wave/demo.gif +0 -0
  24. package/src/assets/images/traffic-lane/demo.gif +0 -0
  25. package/src/assets/images/traffic-lane/demo.png +0 -0
  26. package/src/assets/images/traffic-lane/directon.png +0 -0
  27. package/src/assets/images/traffic-lane/left-ui.png +0 -0
  28. package/src/assets/images/traffic-lane/left.png +0 -0
  29. package/src/assets/images/traffic-lane/leftRight-ui.png +0 -0
  30. package/src/assets/images/traffic-lane/leftRight.png +0 -0
  31. package/src/assets/images/traffic-lane/otherPic-ui.png +0 -0
  32. package/src/assets/images/traffic-lane/otherPic.png +0 -0
  33. package/src/assets/images/traffic-lane/right-ui.png +0 -0
  34. package/src/assets/images/traffic-lane/right.png +0 -0
  35. package/src/assets/images/traffic-lane/straight-ui.png +0 -0
  36. package/src/assets/images/traffic-lane/straight.png +0 -0
  37. package/src/assets/images/traffic-lane/straightLeft-ui.png +0 -0
  38. package/src/assets/images/traffic-lane/straightLeft.png +0 -0
  39. package/src/assets/images/traffic-lane/straightLeftRight-ui.png +0 -0
  40. package/src/assets/images/traffic-lane/straightLeftRight.png +0 -0
  41. package/src/assets/images/traffic-lane/straightRight-ui.png +0 -0
  42. package/src/assets/images/traffic-lane/straightRight.png +0 -0
  43. package/src/assets/images/traffic-lane/turnAround-ui.png +0 -0
  44. package/src/assets/images/traffic-lane/turnAround.png +0 -0
  45. package/src/components/green-wave/index.vue +671 -0
  46. package/src/components/traffic-lane/index.vue +1020 -0
  47. package/src/index.js +32 -0
  48. package/src/libs/bus.js +24 -0
  49. package/src/libs/pics-load-tool.js +23 -0
  50. package/src/main.js +14 -0
  51. package/vue.config.js +279 -0
@@ -0,0 +1,671 @@
1
+ <template>
2
+ <div class="canvas-wrapper">
3
+ <canvas ref="canvas_l" :style="{ width: yAxisWidth + 'px' }" />
4
+ <canvas ref="canvas" style="flex: 1" />
5
+ </div>
6
+ </template>
7
+ <script>
8
+ import _ from 'lodash';
9
+ import { fabric } from 'fabric';
10
+ export default {
11
+ name: 'green-wave',
12
+ props: {
13
+ /**
14
+ * 绿波路口数据
15
+ * - Possible values:
16
+ * - Array
17
+ * 字段描述:
18
+ * - crossId: string - 路口ID
19
+ * - crossRoadName: string - 路口名称
20
+ * - crossDistance: number - 路口距离
21
+ * - orderNumber: number - 排序编号
22
+ * - forwardGreen: number - 路口正向车道绿灯时长(A路口 -> B路口)
23
+ * - reverseGreen: number - 路口反向车道绿灯时长(B路口 -> A路口)
24
+ * - forwardSpeed: number - 路口正向车道速度(A路口 -> B路口)
25
+ * - reverseSpeed: number - 路口反向车道速度(B路口 -> A路口)
26
+ * - forwardPhaseDiff: number - 路口正向车道相位差时长(A路口 -> B路口)
27
+ * - reversePhaseDiff: number - 路口反向车道相位差时长(B路口 -> A路口)
28
+ * - greenFlag: boolean - 绿波标志
29
+ * - phaseDiffFlag: boolean - 相位标志
30
+ */
31
+ crossData: {
32
+ type: Array,
33
+ default: () => [],
34
+ },
35
+ /**
36
+ * 画布高度
37
+ */
38
+ canvasHeight: {
39
+ type: Number,
40
+ default: 500,
41
+ },
42
+ /**
43
+ * 是否正向(从上到下, 从左到右为正方向)
44
+ */
45
+ forwardDirection: {
46
+ type: Boolean,
47
+ default: true,
48
+ },
49
+ /**
50
+ * 绿波周期间隔(单位:秒)
51
+ */
52
+ interval: {
53
+ type: Number,
54
+ default: 70,
55
+ },
56
+ /**
57
+ * 正向绿波颜色
58
+ */
59
+ forwardColor: {
60
+ type: String,
61
+ default: '#058213',
62
+ },
63
+ /**
64
+ * 反向绿波颜色
65
+ */
66
+ backwardColor: {
67
+ type: String,
68
+ default: '#0eed28',
69
+ },
70
+ /**
71
+ * 拖拽线宽
72
+ */
73
+ dragStrokeWidth: {
74
+ type: Number,
75
+ default: 5,
76
+ },
77
+ /**
78
+ * 单绿波宽度
79
+ * 值越小宽度越小,值越大宽度越大
80
+ */
81
+ singleWaveWidth: {
82
+ type: Number,
83
+ default: 2,
84
+ },
85
+ /**
86
+ * X轴底部间距
87
+ */
88
+ xAxisBottom: {
89
+ type: Number,
90
+ default: 30,
91
+ },
92
+ /**
93
+ * Y轴顶部间距
94
+ */
95
+ yAxisTop: {
96
+ type: Number,
97
+ default: 30,
98
+ },
99
+ /**
100
+ * Y轴文本刻度宽度
101
+ */
102
+ yAxisWidth: {
103
+ type: Number,
104
+ default: 200,
105
+ },
106
+
107
+ /**
108
+ * 绿波循环次数
109
+ * - Possible values:
110
+ * - Number
111
+ * - 0 - 默认值,自动计算
112
+ * - 1 - 循环一次
113
+ */
114
+ loopTime: {
115
+ type: Number,
116
+ default: 0,
117
+ }
118
+ },
119
+ data() {
120
+ return {
121
+ groups_forward: [],
122
+ groups_back: [],
123
+ // 初始值,默认按照容器宽度
124
+ canvasWidth: 500,
125
+ canvas: null,
126
+ canvas_l: null,
127
+ initialPositions: {},
128
+ calculateLoopTime: 0,
129
+ resizeHandler: null,
130
+ };
131
+ },
132
+ computed: {
133
+ mulWatchData() {
134
+ return {
135
+ interval: this.interval,
136
+ crossData: this.crossData,
137
+ forwardDirection: this.forwardDirection,
138
+ };
139
+ }
140
+ },
141
+ watch: {
142
+ mulWatchData: {
143
+ handler(val) {
144
+ this.drawRoad();
145
+ },
146
+ deep: true,
147
+ },
148
+ },
149
+ mounted() {
150
+ this.init();
151
+ },
152
+ created() {
153
+ this.resizeHandler = _.debounce(() => {
154
+ if (this.canvas) {
155
+ this.canvas.dispose();
156
+ this.canvas_l.dispose();
157
+ this.canvas = null;
158
+ this.canvas_l = null;
159
+ this.init();
160
+ }
161
+ }, 200);
162
+
163
+ window.addEventListener('resize', this.resizeHandler);
164
+ },
165
+ beforeDestroy() {
166
+ window.removeEventListener('resize', this.resizeHandler);
167
+ this.resizeHandler.cancel();
168
+
169
+ if (this.canvas) {
170
+ this.canvas.dispose();
171
+ this.canvas_l.dispose();
172
+ }
173
+ },
174
+ methods: {
175
+ /**
176
+ * 初始化
177
+ */
178
+ init() {
179
+ // Get container width dynamically
180
+ const containerWidth = this.$el.offsetWidth - this.yAxisWidth;
181
+
182
+ // Initialize main canvas
183
+ if (this.canvas) {
184
+ this.canvas.dispose();
185
+ }
186
+ this.canvas = new fabric.Canvas(this.$refs.canvas, {
187
+ backgroundColor: '#fff',
188
+ selection: false,
189
+ width: containerWidth,
190
+ height: this.canvasHeight,
191
+ });
192
+
193
+ // Initialize y-axis canvas
194
+ if (this.canvas_l) {
195
+ this.canvas_l.dispose();
196
+ }
197
+ this.canvas_l = new fabric.Canvas(this.$refs.canvas_l, {
198
+ backgroundColor: '#fff',
199
+ selection: false,
200
+ width: this.yAxisWidth,
201
+ height: this.canvasHeight,
202
+ });
203
+
204
+ this.drawRoad();
205
+ },
206
+ // 绘制路口
207
+ drawBaseElement() {
208
+ // 横纵坐标,周期线
209
+ this.canvas.add(
210
+ new fabric.Line([0, this.canvas.height - this.xAxisBottom, this.canvas.width, this.canvas.height - this.xAxisBottom], {
211
+ stroke: '#333',
212
+ strokeWidth: 1,
213
+ }),
214
+ );
215
+ // 纵轴线
216
+ this.canvas.add(
217
+ new fabric.Line([0, this.canvas.height - this.xAxisBottom, 0, this.yAxisTop], {
218
+ stroke: '#333',
219
+ strokeWidth: 1,
220
+ }),
221
+ );
222
+ // // 画周期
223
+ const contentWidth = this.canvas.width;
224
+
225
+ this.calculateLoopTime = this.loopTime || Math.ceil(contentWidth / this.interval / this.singleWaveWidth);
226
+
227
+ for (let i = 1; i < this.calculateLoopTime; i++) {
228
+ this.canvas.add(
229
+ new fabric.Line(
230
+ [this.interval * this.singleWaveWidth * i, this.canvas.height - this.xAxisBottom, this.interval * this.singleWaveWidth * i, this.yAxisTop],
231
+ {
232
+ stroke: 'lightgrey',
233
+ strokeWidth: 1,
234
+ strokeDashArray: [5, 2],
235
+ },
236
+ ),
237
+ );
238
+ this.canvas.add(
239
+ new fabric.Textbox(`${this.interval * i}s`, {
240
+ fontSize: 14,
241
+ left: this.interval * this.singleWaveWidth * i,
242
+ top: this.canvas.height - this.xAxisBottom + 5,
243
+ textAlign: 'center',
244
+ backgroundColor: '#fff',
245
+ originX: 'center',
246
+ originY: 'top',
247
+ }),
248
+ );
249
+ }
250
+ },
251
+
252
+ // 获取时距拖拽方向
253
+ getGroupDirection({ crossId, left }) {
254
+ const initialPos = this.initialPositions[crossId];
255
+ let direction = 'none';
256
+
257
+ if (initialPos) {
258
+ direction = left > initialPos ? 'forward' : 'backward';
259
+ }
260
+
261
+ return direction;
262
+ },
263
+
264
+ drawRoad() {
265
+ // this.canvas_l && this.canvas_l.clear();
266
+ // this.canvas && this.canvas.clear();
267
+ this.groups_forward = [];
268
+ this.groups_back = [];
269
+ this.drawBaseElement();
270
+ this.canvas_l.add(
271
+ new fabric.Line([this.yAxisWidth / 2, this.canvas.height - this.xAxisBottom, this.yAxisWidth / 2, this.yAxisTop], {
272
+ stroke: 'lightgrey',
273
+ strokeWidth: 1,
274
+ strokeDashArray: [5, 2],
275
+ }),
276
+ );
277
+ const totalDistance = this.crossData.reduce((pre, cur) => pre + cur.crossDistance, 0);
278
+ let yDis = 0;
279
+ const contentHeight = this.canvas.height - this.yAxisTop - this.xAxisBottom;
280
+ this.crossData.forEach((item, index) => {
281
+ yDis += item.crossDistance;
282
+ let top = 0;
283
+ if (this.forwardDirection) {
284
+ top = ((totalDistance - yDis) / totalDistance) * contentHeight + this.yAxisTop;
285
+ } else {
286
+ top = (yDis / totalDistance) * contentHeight + this.yAxisTop;
287
+ }
288
+ this.canvas_l.add(
289
+ new fabric.Textbox(`【${item.crossRoadName}】`, {
290
+ fontSize: 14,
291
+ left: 0,
292
+ top: top,
293
+ textAlign: 'center',
294
+ width: this.yAxisWidth - 1,
295
+ backgroundColor: '#fff',
296
+ originY: 'bottom',
297
+ }),
298
+ );
299
+ if (index !== 0) {
300
+ let text_top = 0;
301
+ if (this.forwardDirection) {
302
+ text_top = ((totalDistance - yDis + item.crossDistance / 2) / totalDistance) * contentHeight + this.yAxisTop;
303
+ } else {
304
+ text_top = ((yDis - item.crossDistance / 2) / totalDistance) * contentHeight + this.yAxisTop;
305
+ }
306
+ var text2 = new fabric.Textbox(`${item.crossDistance},${item.forwardSpeed}km/h`, {
307
+ fontSize: 14,
308
+ left: 0,
309
+ top: text_top,
310
+ textAlign: 'center',
311
+ width: this.yAxisWidth - 1,
312
+ backgroundColor: '#fff',
313
+ originY: 'bottom',
314
+ });
315
+ this.canvas_l.add(text2);
316
+ }
317
+
318
+ // 绘制绿灯
319
+ let list = [];
320
+ let list_back = [];
321
+ for (let i = -1; i < this.calculateLoopTime; i++) {
322
+ const cross = new fabric.Line(
323
+ [
324
+ (item.forwardPhaseDiff + this.interval * i) * this.singleWaveWidth,
325
+ top,
326
+ (item.forwardPhaseDiff + item.forwardGreen + this.interval * i) * this.singleWaveWidth,
327
+ top,
328
+ ],
329
+ {
330
+ stroke: this.forwardColor,
331
+ strokeWidth: this.dragStrokeWidth,
332
+ originY: 'bottom',
333
+ },
334
+ );
335
+ const cross_back = new fabric.Line(
336
+ [
337
+ (item.reversePhaseDiff + this.interval * i) * this.singleWaveWidth,
338
+ top - this.dragStrokeWidth,
339
+ (item.reversePhaseDiff + item.reverseGreen + this.interval * i) * this.singleWaveWidth,
340
+ top - this.dragStrokeWidth,
341
+ ],
342
+ {
343
+ stroke: this.backwardColor,
344
+ strokeWidth: this.dragStrokeWidth,
345
+ originY: 'bottom',
346
+ },
347
+ );
348
+ list.push(cross);
349
+ list_back.push(cross_back);
350
+ }
351
+ const group = new fabric.Group(list, {
352
+ ...item,
353
+ roadType: 0, // 正向
354
+ hoverCursor: 'ew-resize',
355
+ moveCursor: 'ew-resize',
356
+ lockRotation: true,
357
+ lockMovementY: true,
358
+ hasControls: false,
359
+ hasBorders: false,
360
+ });
361
+ const group_back = new fabric.Group(list_back, {
362
+ ...item,
363
+ roadType: 1, // 反向
364
+ hoverCursor: 'ew-resize',
365
+ moveCursor: 'ew-resize',
366
+ lockRotation: true,
367
+ lockMovementY: true,
368
+ hasControls: false,
369
+ hasBorders: false,
370
+ });
371
+
372
+ // 初始位置
373
+ this.initialPositions[group.crossId] = group.left;
374
+ this.initialPositions[group_back.crossId] = group_back.left;
375
+
376
+ this.groups_forward.push(group);
377
+ this.groups_back.push(group_back);
378
+ this.canvas.add(group, group_back);
379
+ let startDistance = 0;
380
+ let endDistance = 0;
381
+ let crossId = '';
382
+ group.on('moving', e => {
383
+ if (e.transform.target.left <= -1 * this.interval * this.singleWaveWidth) {
384
+ e.transform.target.left = -1 * this.interval * this.singleWaveWidth;
385
+ } else if (e.transform.target.left >= 0) {
386
+ e.transform.target.left = 0;
387
+ }
388
+ const target = e.transform.target;
389
+ const index = this.groups_forward.findIndex(ele => ele.crossId === target.crossId);
390
+ if (index !== this.groups_forward.length - 1) {
391
+ this.drawForward(target, this.groups_forward[index + 1]);
392
+ }
393
+ if (index !== 0) {
394
+ this.drawForward(this.groups_forward[index - 1], target);
395
+ }
396
+
397
+ // 查找反向绿波组
398
+ this.groups_back[index].left = e.transform.target.left;
399
+ this.groups_back[index].setCoords();
400
+ if (index !== 0) {
401
+ this.drawBackward(this.groups_back[index], this.groups_back[index - 1]);
402
+ }
403
+ if (index !== this.groups_back.length - 1) {
404
+ this.drawBackward(this.groups_back[index + 1], this.groups_back[index]);
405
+ }
406
+ this.$emit('computeData', {
407
+ groups_forward: this.groups_forward,
408
+ groups_back: this.groups_back,
409
+ rate: this.singleWaveWidth,
410
+ current_group: group,
411
+ });
412
+ });
413
+ group.on('mouseup', e => {
414
+ this.$emit('updateCrossParams', {
415
+ rate: this.singleWaveWidth,
416
+ current_group: group,
417
+ direction: this.getGroupDirection(e.transform.target),
418
+ });
419
+ });
420
+ group_back.on('moving', e => {
421
+ if (e.transform.target.left <= -1 * this.interval * this.singleWaveWidth) {
422
+ e.transform.target.left = -1 * this.interval * this.singleWaveWidth;
423
+ } else if (e.transform.target.left >= 0) {
424
+ e.transform.target.left = 0;
425
+ }
426
+ const target = e.transform.target;
427
+ const index = this.groups_back.findIndex(ele => ele.crossId === target.crossId);
428
+ if (index !== 0) {
429
+ this.drawBackward(target, this.groups_back[index - 1]);
430
+ }
431
+ if (index !== this.groups_back.length - 1) {
432
+ this.drawBackward(this.groups_back[index + 1], target);
433
+ }
434
+ // 查找正向绿波组
435
+ this.groups_forward[index].left = e.transform.target.left;
436
+ this.groups_forward[index].setCoords();
437
+ if (index !== this.groups_forward.length - 1) {
438
+ this.drawForward(this.groups_forward[index], this.groups_back[index + 1]);
439
+ }
440
+ if (index !== 0) {
441
+ this.drawForward(this.groups_forward[index - 1], this.groups_back[index]);
442
+ }
443
+
444
+ this.$emit('computeData', {
445
+ groups_forward: this.groups_forward,
446
+ groups_back: this.groups_back,
447
+ rate: this.singleWaveWidth,
448
+ current_group: group_back,
449
+ });
450
+ });
451
+ group_back.on('mouseup', e => {
452
+ this.$emit('updateCrossParams', {
453
+ rate: this.singleWaveWidth,
454
+ current_group: group_back,
455
+ direction: this.getGroupDirection(e.transform.target),
456
+ });
457
+ });
458
+ });
459
+
460
+ // 初始化正向绿波
461
+ for (let i = 0; i < this.groups_forward.length - 1; i++) {
462
+ this.drawForward(this.groups_forward[i], this.groups_forward[i + 1]);
463
+ }
464
+ // 初始化反向绿波
465
+ for (let i = this.groups_back.length - 1; i > 0; i--) {
466
+ this.drawBackward(this.groups_back[i], this.groups_back[i - 1]);
467
+ }
468
+ this.$emit('computeData', {
469
+ groups_forward: this.groups_forward,
470
+ groups_back: this.groups_back,
471
+ rate: this.singleWaveWidth,
472
+ });
473
+ },
474
+
475
+ drawForward(startGroup, endGroup) {
476
+ // 计算到下个路口所需的时间
477
+ const time = (endGroup.crossDistance / 1000 / startGroup.forwardSpeed) * 3600;
478
+ let y1 = startGroup.aCoords.bl.y;
479
+ let y2 = endGroup.aCoords.bl.y;
480
+ startGroup.forEachObject((item, index) => {
481
+ let x1, x2, x3, x4; // 左下开始顺时针
482
+ const need_t = time * this.singleWaveWidth;
483
+ const pk = 'polygon' + index;
484
+ endGroup.forEachObject((ng, gidx) => {
485
+ const start_x0 = item.aCoords.tl.x + startGroup.left + startGroup.width / 2; // 开始点
486
+ const end_x0 = ng.aCoords.tl.x + endGroup.left + endGroup.width / 2;
487
+ if (
488
+ start_x0 + need_t > end_x0 &&
489
+ start_x0 + need_t < end_x0 + ng.width &&
490
+ start_x0 + item.width + need_t > end_x0 + ng.width
491
+ ) {
492
+ x1 = start_x0;
493
+ x2 = start_x0 + time * this.singleWaveWidth;
494
+ x3 = end_x0 + ng.width;
495
+ x4 = x1 + x3 - x2;
496
+ } else if (
497
+ start_x0 + need_t > end_x0 &&
498
+ start_x0 + need_t < end_x0 + ng.width &&
499
+ start_x0 + item.width + need_t > end_x0 &&
500
+ start_x0 + item.width + need_t < end_x0 + ng.width
501
+ ) {
502
+ x1 = start_x0;
503
+ x2 = start_x0 + need_t;
504
+ x3 = start_x0 + item.width + need_t;
505
+ x4 = start_x0 + item.width;
506
+ } else if (
507
+ start_x0 + need_t < end_x0 &&
508
+ start_x0 + item.width + need_t > end_x0 &&
509
+ start_x0 + item.width + need_t < end_x0 + ng.width
510
+ ) {
511
+ x1 = end_x0 - time * this.singleWaveWidth;
512
+ x2 = end_x0;
513
+ x3 = x2 + start_x0 + item.width - x1;
514
+ x4 = start_x0 + item.width;
515
+ } else if (
516
+ end_x0 - need_t > start_x0 &&
517
+ end_x0 - need_t < start_x0 + item.width &&
518
+ end_x0 + ng.width - need_t > start_x0 &&
519
+ end_x0 + ng.width - need_t < start_x0 + item.width
520
+ ) {
521
+ x1 = end_x0 - need_t;
522
+ x2 = end_x0;
523
+ x3 = end_x0 + ng.width;
524
+ x4 = x3 - need_t;
525
+ } else if (
526
+ end_x0 - need_t > start_x0 &&
527
+ end_x0 - need_t < start_x0 + item.width &&
528
+ end_x0 + ng.width - need_t > start_x0 + item.width
529
+ ) {
530
+ x1 = end_x0 - need_t;
531
+ x2 = end_x0;
532
+ x4 = start_x0 + item.width;
533
+ x3 = x4 + need_t;
534
+ } else if (
535
+ end_x0 - need_t < start_x0 &&
536
+ end_x0 + ng.width - need_t > start_x0 &&
537
+ end_x0 + ng.width - need_t < start_x0 + item.width
538
+ ) {
539
+ x1 = start_x0;
540
+ x2 = x1 - need_t;
541
+ x3 = end_x0 + ng.width;
542
+ x4 = x3 - need_t;
543
+ }
544
+ });
545
+ if (item[pk]) {
546
+ this.canvas.remove(item[pk]);
547
+ item[pk] = null;
548
+ }
549
+ if (x1 !== x3 && x2 !== x4) {
550
+ item[pk] = this.drawPolygon(
551
+ [
552
+ { x: x1, y: y1 },
553
+ { x: x2, y: y2 },
554
+ { x: x3, y: y2 },
555
+ { x: x4, y: y1 },
556
+ ],
557
+ this.forwardColor,
558
+ );
559
+ this.canvas.add(item[pk]);
560
+ }
561
+ });
562
+ },
563
+ // 反向绿波图
564
+ drawBackward(startGroup, endGroup) {
565
+ // 计算到下个路口所需的时间
566
+ const time = (startGroup.crossDistance / 1000 / startGroup.reverseSpeed) * 3600;
567
+ let y1 = startGroup.aCoords.bl.y;
568
+ let y2 = endGroup.aCoords.bl.y;
569
+ startGroup.forEachObject((item, index) => {
570
+ let x1, x2, x3, x4; // 左上开始顺时针
571
+ const need_t = time * this.singleWaveWidth;
572
+ const pk = 'polygon_back' + index;
573
+ endGroup.forEachObject((ng, gidx) => {
574
+ const start_x0 = item.aCoords.tl.x + startGroup.left + startGroup.width / 2; // 开始点
575
+ const end_x0 = ng.aCoords.tl.x + endGroup.left + endGroup.width / 2;
576
+
577
+ if (
578
+ start_x0 + need_t > end_x0 &&
579
+ start_x0 + need_t < end_x0 + ng.width &&
580
+ start_x0 + item.width + need_t > end_x0 + ng.width
581
+ ) {
582
+ x1 = start_x0;
583
+ x4 = start_x0 + time * this.singleWaveWidth;
584
+ x3 = end_x0 + ng.width;
585
+ x2 = x3 - x4 + x1;
586
+ } else if (
587
+ start_x0 + need_t > end_x0 &&
588
+ start_x0 + need_t < end_x0 + ng.width &&
589
+ start_x0 + item.width + need_t > end_x0 &&
590
+ start_x0 + item.width + need_t < end_x0 + ng.width
591
+ ) {
592
+ x1 = start_x0;
593
+ x4 = start_x0 + need_t;
594
+ x3 = start_x0 + item.width + need_t;
595
+ x2 = start_x0 + item.width;
596
+ } else if (
597
+ start_x0 + need_t < end_x0 &&
598
+ start_x0 + item.width + need_t > end_x0 &&
599
+ start_x0 + item.width + need_t < end_x0 + ng.width
600
+ ) {
601
+ x2 = start_x0 + item.width;
602
+ x3 = x2 + time * this.singleWaveWidth;
603
+ x4 = end_x0;
604
+ x1 = x2 - (x3 - x4);
605
+ } else if (
606
+ end_x0 - need_t > start_x0 &&
607
+ end_x0 - need_t < start_x0 + item.width &&
608
+ end_x0 + ng.width - need_t > start_x0 &&
609
+ end_x0 + ng.width - need_t < start_x0 + item.width
610
+ ) {
611
+ x1 = end_x0 - need_t;
612
+ x4 = end_x0;
613
+ x3 = end_x0 + ng.width;
614
+ x2 = x3 - need_t;
615
+ } else if (
616
+ end_x0 - need_t > start_x0 &&
617
+ end_x0 - need_t < start_x0 + item.width &&
618
+ end_x0 + ng.width - need_t > start_x0 + item.width
619
+ ) {
620
+ x1 = end_x0 - need_t;
621
+ x4 = end_x0;
622
+ x2 = start_x0 + item.width;
623
+ x3 = x4 + need_t;
624
+ } else if (
625
+ end_x0 - need_t < start_x0 &&
626
+ end_x0 + ng.width - need_t > start_x0 &&
627
+ end_x0 + ng.width - need_t < start_x0 + item.width
628
+ ) {
629
+ x1 = start_x0;
630
+ x3 = end_x0 + ng.width;
631
+ x2 = x3 - need_t;
632
+ x4 = x1 + need_t;
633
+ }
634
+ });
635
+ if (item[pk]) {
636
+ this.canvas.remove(item[pk]);
637
+ item[pk] = null;
638
+ }
639
+ if (x1 !== x3 && x2 !== x4) {
640
+ item[pk] = this.drawPolygon(
641
+ [
642
+ { x: x1, y: y1 },
643
+ { x: x2, y: y1 },
644
+ { x: x3, y: y2 },
645
+ { x: x4, y: y2 },
646
+ ],
647
+ this.backwardColor,
648
+ );
649
+ this.canvas.add(item[pk]);
650
+ }
651
+ });
652
+ },
653
+ drawPolygon(points = [], color = this.forwardColor) {
654
+ const polygon = new fabric.Polygon(points, {
655
+ fill: color,
656
+ opacity: 0.5,
657
+ selectable: false,
658
+ evented: false,
659
+ objectCaching: false,
660
+ });
661
+ // polygon.sendToBack();
662
+ return polygon;
663
+ },
664
+ },
665
+ };
666
+ </script>
667
+ <style lang="scss" scoped>
668
+ .canvas-wrapper {
669
+ display: flex;
670
+ }
671
+ </style>