@flowgram-vue/minimap-plugin 0.2.0
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/LICENSE +22 -0
- package/dist/index.cjs +632 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +183 -0
- package/dist/index.js +626 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
- package/src/component.ts +122 -0
- package/src/constant.ts +39 -0
- package/src/create-plugin.ts +23 -0
- package/src/draw.ts +149 -0
- package/src/env.d.ts +10 -0
- package/src/index.ts +22 -0
- package/src/layer.ts +70 -0
- package/src/service.ts +429 -0
- package/src/type.ts +63 -0
- package/src/wrap-layer-render.ts +38 -0
package/src/service.ts
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { throttle } from 'lodash-es';
|
|
7
|
+
import { inject, injectable } from 'inversify';
|
|
8
|
+
import { Disposable, DisposableCollection, IPoint, Rectangle } from '@flowgram-vue/utils';
|
|
9
|
+
import { FlowNodeTransformData } from '@flowgram-vue/document';
|
|
10
|
+
import { FlowNodeBaseType } from '@flowgram-vue/document';
|
|
11
|
+
import { FlowDocument } from '@flowgram-vue/document';
|
|
12
|
+
import { MouseTouchEvent, PlaygroundConfigEntity } from '@flowgram-vue/core';
|
|
13
|
+
|
|
14
|
+
import type { MinimapRenderContext, MinimapServiceOptions, MinimapCanvasStyle } from './type';
|
|
15
|
+
import { MinimapDraw } from './draw';
|
|
16
|
+
import { MinimapDefaultCanvasStyle, MinimapDefaultOptions } from './constant';
|
|
17
|
+
|
|
18
|
+
@injectable()
|
|
19
|
+
export class FlowMinimapService {
|
|
20
|
+
@inject(FlowDocument) private readonly document: FlowDocument;
|
|
21
|
+
|
|
22
|
+
@inject(PlaygroundConfigEntity)
|
|
23
|
+
private readonly playgroundConfig: PlaygroundConfigEntity;
|
|
24
|
+
|
|
25
|
+
public readonly canvas: HTMLCanvasElement;
|
|
26
|
+
|
|
27
|
+
public readonly context2D: CanvasRenderingContext2D;
|
|
28
|
+
|
|
29
|
+
public activated;
|
|
30
|
+
|
|
31
|
+
private onActiveCallbacks: Set<(activated: boolean) => void>;
|
|
32
|
+
|
|
33
|
+
private options: MinimapServiceOptions;
|
|
34
|
+
|
|
35
|
+
private toDispose: DisposableCollection;
|
|
36
|
+
|
|
37
|
+
private initialized;
|
|
38
|
+
|
|
39
|
+
private visible: boolean = false;
|
|
40
|
+
|
|
41
|
+
private isDragging;
|
|
42
|
+
|
|
43
|
+
private style: MinimapCanvasStyle;
|
|
44
|
+
|
|
45
|
+
private dragStart?: IPoint;
|
|
46
|
+
|
|
47
|
+
constructor() {
|
|
48
|
+
this.canvas = document.createElement('canvas');
|
|
49
|
+
this.context2D = this.canvas.getContext('2d')!;
|
|
50
|
+
this.initialized = !!this.context2D;
|
|
51
|
+
this.onActiveCallbacks = new Set();
|
|
52
|
+
this.toDispose = new DisposableCollection();
|
|
53
|
+
this.render = this._render;
|
|
54
|
+
this.activated = false;
|
|
55
|
+
this.isDragging = false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public init(options?: Partial<MinimapServiceOptions>) {
|
|
59
|
+
this.options = MinimapDefaultOptions;
|
|
60
|
+
Object.assign(this.options, options);
|
|
61
|
+
this.setThrottle(this.options.inactiveThrottleTime);
|
|
62
|
+
this.initStyle();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public dispose(): void {
|
|
66
|
+
this.toDispose.dispose();
|
|
67
|
+
this.initialized = false;
|
|
68
|
+
this.activated = false;
|
|
69
|
+
this.removeEventListeners();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setVisible(visible: boolean) {
|
|
73
|
+
this.visible = visible;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public setActivate(activate: boolean): void {
|
|
77
|
+
if (activate === this.activated) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (!activate && this.isDragging) {
|
|
81
|
+
// 拖拽时持续激活
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
this.activated = activate;
|
|
85
|
+
if (activate) {
|
|
86
|
+
this.setThrottle(this.options.activeThrottleTime);
|
|
87
|
+
this.addEventListeners();
|
|
88
|
+
} else {
|
|
89
|
+
this.setThrottle(this.options.inactiveThrottleTime);
|
|
90
|
+
this.removeEventListeners();
|
|
91
|
+
}
|
|
92
|
+
this.render();
|
|
93
|
+
this.onActiveCallbacks.forEach((callback) => callback(activate));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public onActive = (callback: (activated: boolean) => void): Disposable => {
|
|
97
|
+
this.onActiveCallbacks.add(callback);
|
|
98
|
+
return {
|
|
99
|
+
dispose: () => {
|
|
100
|
+
this.onActiveCallbacks.delete(callback);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
private initStyle() {
|
|
106
|
+
if (!this.initialized) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const { canvasClassName, canvasStyle } = this.options;
|
|
110
|
+
this.canvas.className = canvasClassName;
|
|
111
|
+
this.style = {
|
|
112
|
+
...MinimapDefaultCanvasStyle,
|
|
113
|
+
...canvasStyle,
|
|
114
|
+
};
|
|
115
|
+
this.canvas.width = this.style.canvasWidth;
|
|
116
|
+
this.canvas.height = this.style.canvasHeight;
|
|
117
|
+
this.canvas.style.borderRadius = this.style.canvasBorderRadius
|
|
118
|
+
? `${this.style.canvasBorderRadius}px`
|
|
119
|
+
: 'unset';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private setThrottle(throttleTime: number) {
|
|
123
|
+
this.render = throttle(this._render, throttleTime);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 触发渲染
|
|
128
|
+
*/
|
|
129
|
+
public render: () => void = this._render;
|
|
130
|
+
|
|
131
|
+
private _render(): void {
|
|
132
|
+
if (!this.initialized || !this.visible) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const renderContext = this.createRenderContext();
|
|
136
|
+
this.renderCanvas(renderContext);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private createRenderContext(): MinimapRenderContext {
|
|
140
|
+
const { canvas, context2D } = this;
|
|
141
|
+
const nodeTransforms: FlowNodeTransformData[] = this.transformVisibles;
|
|
142
|
+
const nodeRects: Rectangle[] = nodeTransforms.map((transform) => transform.bounds);
|
|
143
|
+
const viewRect: Rectangle = this.viewRect();
|
|
144
|
+
const renderRect: Rectangle = this.renderRect(nodeRects).withPadding({
|
|
145
|
+
top: this.style.canvasPadding,
|
|
146
|
+
bottom: this.style.canvasPadding,
|
|
147
|
+
left: this.style.canvasPadding,
|
|
148
|
+
right: this.style.canvasPadding,
|
|
149
|
+
});
|
|
150
|
+
const canvasRect: Rectangle = Rectangle.enlarge([viewRect, renderRect]);
|
|
151
|
+
|
|
152
|
+
const { scale, offset } = this.calculateScaleAndOffset({ canvasRect });
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
canvas,
|
|
156
|
+
context2D,
|
|
157
|
+
nodeRects,
|
|
158
|
+
canvasRect,
|
|
159
|
+
viewRect,
|
|
160
|
+
renderRect,
|
|
161
|
+
scale,
|
|
162
|
+
offset,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private renderCanvas(renderContext: MinimapRenderContext) {
|
|
167
|
+
const { canvas, context2D, nodeRects, viewRect, scale, offset } = renderContext;
|
|
168
|
+
|
|
169
|
+
// 清空画布
|
|
170
|
+
MinimapDraw.clear({ canvas, context: context2D });
|
|
171
|
+
|
|
172
|
+
// 设置背景色
|
|
173
|
+
MinimapDraw.backgroundColor({
|
|
174
|
+
canvas,
|
|
175
|
+
context: context2D,
|
|
176
|
+
color: this.style.canvasBackground,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// 绘制视窗
|
|
180
|
+
MinimapDraw.roundRectangle({
|
|
181
|
+
context: context2D,
|
|
182
|
+
rect: this.rectOnCanvas({ rect: viewRect, scale, offset }),
|
|
183
|
+
color: this.style.viewportBackground,
|
|
184
|
+
radius: this.style.viewportBorderRadius as number,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// 绘制节点
|
|
188
|
+
nodeRects.forEach((nodeRect: Rectangle) => {
|
|
189
|
+
MinimapDraw.roundRectangle({
|
|
190
|
+
context: context2D,
|
|
191
|
+
rect: this.rectOnCanvas({ rect: nodeRect, scale, offset }),
|
|
192
|
+
color: this.style.nodeColor as string,
|
|
193
|
+
radius: this.style.nodeBorderRadius as number,
|
|
194
|
+
borderWidth: this.style.nodeBorderWidth as number,
|
|
195
|
+
borderColor: this.style.nodeBorderColor as string,
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// 绘制视窗边框
|
|
200
|
+
MinimapDraw.roundRectangle({
|
|
201
|
+
context: context2D,
|
|
202
|
+
rect: this.rectOnCanvas({ rect: viewRect, scale, offset }),
|
|
203
|
+
color: 'rgba(255, 255, 255, 0)' as string,
|
|
204
|
+
radius: this.style.viewportBorderRadius as number,
|
|
205
|
+
borderColor: this.style.viewportBorderColor as string,
|
|
206
|
+
borderWidth: this.style.viewportBorderWidth as number,
|
|
207
|
+
borderDashLength: this.style.viewportBorderDashLength as number,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// 绘制视窗外的蒙层
|
|
211
|
+
MinimapDraw.overlay({
|
|
212
|
+
canvas,
|
|
213
|
+
context: context2D,
|
|
214
|
+
offset,
|
|
215
|
+
scale,
|
|
216
|
+
rect: viewRect,
|
|
217
|
+
color: this.style.overlayColor as string,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private calculateScaleAndOffset(params: { canvasRect: Rectangle }): {
|
|
222
|
+
scale: number;
|
|
223
|
+
offset: IPoint;
|
|
224
|
+
} {
|
|
225
|
+
const { canvasRect } = params;
|
|
226
|
+
const { width: canvasWidth, height: canvasHeight } = this.canvas;
|
|
227
|
+
|
|
228
|
+
// 计算缩放比例
|
|
229
|
+
const scaleX = canvasWidth / canvasRect.width;
|
|
230
|
+
const scaleY = canvasHeight / canvasRect.height;
|
|
231
|
+
const scale = Math.min(scaleX, scaleY);
|
|
232
|
+
|
|
233
|
+
// 计算缩放后的渲染区域尺寸
|
|
234
|
+
const scaledWidth = canvasRect.width * scale;
|
|
235
|
+
const scaledHeight = canvasRect.height * scale;
|
|
236
|
+
|
|
237
|
+
// 计算居中偏移量
|
|
238
|
+
const centerOffsetX = (canvasWidth - scaledWidth) / 2;
|
|
239
|
+
const centerOffsetY = (canvasHeight - scaledHeight) / 2;
|
|
240
|
+
|
|
241
|
+
// 计算最终偏移量
|
|
242
|
+
const offset = {
|
|
243
|
+
x: centerOffsetX / scale - canvasRect.x,
|
|
244
|
+
y: centerOffsetY / scale - canvasRect.y,
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return { scale, offset };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private get transformVisibles(): FlowNodeTransformData[] {
|
|
251
|
+
const transformVisible = this.document.getRenderDatas<FlowNodeTransformData>(
|
|
252
|
+
FlowNodeTransformData,
|
|
253
|
+
false
|
|
254
|
+
);
|
|
255
|
+
return transformVisible.filter((transform) => {
|
|
256
|
+
const node = transform.entity;
|
|
257
|
+
// 去除不可见节点
|
|
258
|
+
if (node.hidden) return false;
|
|
259
|
+
// 去除根节点
|
|
260
|
+
if (node.flowNodeType === FlowNodeBaseType.ROOT) return;
|
|
261
|
+
// 去除非一级节点
|
|
262
|
+
if (
|
|
263
|
+
!this.options.enableDisplayAllNodes &&
|
|
264
|
+
node.parent &&
|
|
265
|
+
node.parent.flowNodeType !== FlowNodeBaseType.ROOT
|
|
266
|
+
)
|
|
267
|
+
return;
|
|
268
|
+
return true;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private renderRect(rects: Rectangle[]): Rectangle {
|
|
273
|
+
return Rectangle.enlarge(rects);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private viewRect(): Rectangle {
|
|
277
|
+
const { width, height, scrollX, scrollY, zoom } = this.playgroundConfig.config;
|
|
278
|
+
return new Rectangle(scrollX / zoom, scrollY / zoom, width / zoom, height / zoom);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** 计算画布坐标系下的矩形 */
|
|
282
|
+
private rectOnCanvas(params: { rect: Rectangle; scale: number; offset: IPoint }): Rectangle {
|
|
283
|
+
const { rect, scale, offset } = params;
|
|
284
|
+
return new Rectangle(
|
|
285
|
+
(rect.x + offset.x) * scale,
|
|
286
|
+
(rect.y + offset.y) * scale,
|
|
287
|
+
rect.width * scale,
|
|
288
|
+
rect.height * scale
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private isPointInRect(params: { point: IPoint; rect: Rectangle }): boolean {
|
|
293
|
+
const { point, rect } = params;
|
|
294
|
+
return (
|
|
295
|
+
point.x >= rect.x &&
|
|
296
|
+
point.x <= rect.x + rect.width &&
|
|
297
|
+
point.y >= rect.y &&
|
|
298
|
+
point.y <= rect.y + rect.height
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private addEventListeners(): void {
|
|
303
|
+
this.canvas.addEventListener('wheel', this.handleWheel);
|
|
304
|
+
this.canvas.addEventListener('mousedown', this.handleStartDrag);
|
|
305
|
+
this.canvas.addEventListener('touchstart', this.handleStartDrag, { passive: false });
|
|
306
|
+
this.canvas.addEventListener('mousemove', this.handleCursor);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private removeEventListeners(): void {
|
|
310
|
+
this.canvas.removeEventListener('wheel', this.handleWheel);
|
|
311
|
+
this.canvas.removeEventListener('mousedown', this.handleStartDrag);
|
|
312
|
+
this.canvas.removeEventListener('touchstart', this.handleStartDrag);
|
|
313
|
+
this.canvas.removeEventListener('mousemove', this.handleCursor);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private handleWheel = (event: WheelEvent): void => {};
|
|
317
|
+
|
|
318
|
+
private handleStartDrag = (event: MouseEvent | TouchEvent): void => {
|
|
319
|
+
MouseTouchEvent.preventDefault(event);
|
|
320
|
+
event.stopPropagation();
|
|
321
|
+
const renderContext = this.createRenderContext();
|
|
322
|
+
const { viewRect, scale, offset } = renderContext;
|
|
323
|
+
const canvasRect = this.canvas.getBoundingClientRect();
|
|
324
|
+
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
|
|
325
|
+
const mousePoint: IPoint = {
|
|
326
|
+
x: clientX - canvasRect.left,
|
|
327
|
+
y: clientY - canvasRect.top,
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const viewRectOnCanvas = this.rectOnCanvas({
|
|
331
|
+
rect: viewRect,
|
|
332
|
+
scale,
|
|
333
|
+
offset,
|
|
334
|
+
});
|
|
335
|
+
if (!this.isPointInRect({ point: mousePoint, rect: viewRectOnCanvas })) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
this.isDragging = true;
|
|
339
|
+
this.dragStart = mousePoint;
|
|
340
|
+
// click
|
|
341
|
+
document.addEventListener('mousemove', this.handleDragging);
|
|
342
|
+
document.addEventListener('mouseup', this.handleEndDrag);
|
|
343
|
+
// touch
|
|
344
|
+
document.addEventListener('touchmove', this.handleDragging, { passive: false });
|
|
345
|
+
document.addEventListener('touchend', this.handleEndDrag);
|
|
346
|
+
document.addEventListener('touchcancel', this.handleEndDrag);
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
private handleDragging = (event: MouseEvent | TouchEvent): void => {
|
|
350
|
+
if (!this.isDragging || !this.dragStart) return;
|
|
351
|
+
MouseTouchEvent.preventDefault(event);
|
|
352
|
+
event.stopPropagation();
|
|
353
|
+
|
|
354
|
+
const renderContext = this.createRenderContext();
|
|
355
|
+
const { scale } = renderContext;
|
|
356
|
+
const canvasRect = this.canvas.getBoundingClientRect();
|
|
357
|
+
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
|
|
358
|
+
const mouseX = clientX - canvasRect.left;
|
|
359
|
+
const mouseY = clientY - canvasRect.top;
|
|
360
|
+
|
|
361
|
+
const deltaX = (mouseX - this.dragStart.x) / scale;
|
|
362
|
+
const deltaY = (mouseY - this.dragStart.y) / scale;
|
|
363
|
+
|
|
364
|
+
this.updateScrollPosition(deltaX, deltaY);
|
|
365
|
+
|
|
366
|
+
this.dragStart = { x: mouseX, y: mouseY };
|
|
367
|
+
this.render();
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
private handleEndDrag = (event: MouseEvent | TouchEvent): void => {
|
|
371
|
+
MouseTouchEvent.preventDefault(event);
|
|
372
|
+
event.stopPropagation();
|
|
373
|
+
// click
|
|
374
|
+
document.removeEventListener('mousemove', this.handleDragging);
|
|
375
|
+
document.removeEventListener('mouseup', this.handleEndDrag);
|
|
376
|
+
// touch
|
|
377
|
+
document.removeEventListener('touchmove', this.handleDragging);
|
|
378
|
+
document.removeEventListener('touchend', this.handleEndDrag);
|
|
379
|
+
document.removeEventListener('touchcancel', this.handleEndDrag);
|
|
380
|
+
this.isDragging = false;
|
|
381
|
+
this.dragStart = undefined;
|
|
382
|
+
this.setActivate(this.isMouseInCanvas(event));
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
private handleCursor = (event: MouseEvent): void => {
|
|
386
|
+
if (!this.activated) return;
|
|
387
|
+
|
|
388
|
+
const renderContext = this.createRenderContext();
|
|
389
|
+
const { viewRect, scale, offset } = renderContext;
|
|
390
|
+
const canvasRect = this.canvas.getBoundingClientRect();
|
|
391
|
+
const mousePoint: IPoint = {
|
|
392
|
+
x: event.clientX - canvasRect.left,
|
|
393
|
+
y: event.clientY - canvasRect.top,
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const viewRectOnCanvas = this.rectOnCanvas({
|
|
397
|
+
rect: viewRect,
|
|
398
|
+
scale,
|
|
399
|
+
offset,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
if (this.isPointInRect({ point: mousePoint, rect: viewRectOnCanvas })) {
|
|
403
|
+
// 鼠标在视窗框内
|
|
404
|
+
this.canvas.style.cursor = 'grab';
|
|
405
|
+
} else {
|
|
406
|
+
// 鼠标在视窗框外但在画布内
|
|
407
|
+
this.canvas.style.cursor = 'default';
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
private isMouseInCanvas(event: MouseEvent | TouchEvent): boolean {
|
|
412
|
+
const canvasRect = this.canvas.getBoundingClientRect();
|
|
413
|
+
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
|
|
414
|
+
return (
|
|
415
|
+
clientX >= canvasRect.left &&
|
|
416
|
+
clientX <= canvasRect.right &&
|
|
417
|
+
clientY >= canvasRect.top &&
|
|
418
|
+
clientY <= canvasRect.bottom
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private updateScrollPosition(deltaX: number, deltaY: number): void {
|
|
423
|
+
const { scrollX, scrollY, zoom } = this.playgroundConfig.config;
|
|
424
|
+
this.playgroundConfig.updateConfig({
|
|
425
|
+
scrollX: scrollX + deltaX * zoom,
|
|
426
|
+
scrollY: scrollY + deltaY * zoom,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
package/src/type.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CSSProperties } from 'vue';
|
|
7
|
+
|
|
8
|
+
import type { IPoint, Rectangle } from '@flowgram-vue/utils';
|
|
9
|
+
|
|
10
|
+
export interface MinimapCanvasStyle {
|
|
11
|
+
canvasWidth: number;
|
|
12
|
+
canvasHeight: number;
|
|
13
|
+
canvasPadding: number;
|
|
14
|
+
canvasBackground: string;
|
|
15
|
+
canvasBorderRadius: number | undefined;
|
|
16
|
+
viewportBackground: string;
|
|
17
|
+
viewportBorderRadius: number | undefined;
|
|
18
|
+
viewportBorderColor: string;
|
|
19
|
+
viewportBorderWidth: number;
|
|
20
|
+
viewportBorderDashLength: number | undefined;
|
|
21
|
+
nodeColor: string;
|
|
22
|
+
nodeBorderRadius: number | undefined;
|
|
23
|
+
nodeBorderWidth: number;
|
|
24
|
+
nodeBorderColor: string | undefined;
|
|
25
|
+
overlayColor: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface MinimapInactiveStyle {
|
|
29
|
+
scale: number;
|
|
30
|
+
opacity: number;
|
|
31
|
+
translateX: number;
|
|
32
|
+
translateY: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface MinimapServiceOptions {
|
|
36
|
+
canvasStyle: Partial<MinimapCanvasStyle>;
|
|
37
|
+
canvasClassName: string;
|
|
38
|
+
enableDisplayAllNodes: boolean;
|
|
39
|
+
activeThrottleTime: number;
|
|
40
|
+
inactiveThrottleTime: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface MinimapLayerOptions {
|
|
44
|
+
disableLayer?: boolean;
|
|
45
|
+
panelStyles?: CSSProperties;
|
|
46
|
+
containerStyles?: CSSProperties;
|
|
47
|
+
inactiveStyle?: Partial<MinimapInactiveStyle>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface CreateMinimapPluginOptions
|
|
51
|
+
extends MinimapLayerOptions,
|
|
52
|
+
Partial<MinimapServiceOptions> {}
|
|
53
|
+
|
|
54
|
+
export interface MinimapRenderContext {
|
|
55
|
+
canvas: HTMLCanvasElement;
|
|
56
|
+
context2D: CanvasRenderingContext2D;
|
|
57
|
+
nodeRects: Rectangle[];
|
|
58
|
+
viewRect: Rectangle;
|
|
59
|
+
renderRect: Rectangle;
|
|
60
|
+
canvasRect: Rectangle;
|
|
61
|
+
scale: number;
|
|
62
|
+
offset: IPoint;
|
|
63
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { defineComponent, h, provide, type VNodeChild } from 'vue';
|
|
7
|
+
import {
|
|
8
|
+
Playground,
|
|
9
|
+
PlaygroundVueContainerKey,
|
|
10
|
+
PlaygroundVueRefKey,
|
|
11
|
+
type PlaygroundContainerFactory,
|
|
12
|
+
} from '@flowgram-vue/core';
|
|
13
|
+
|
|
14
|
+
export const LayerVueProvide = defineComponent({
|
|
15
|
+
name: 'LayerVueProvide',
|
|
16
|
+
props: {
|
|
17
|
+
factory: {
|
|
18
|
+
type: Object,
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
setup(props) {
|
|
23
|
+
const factory = props.factory as PlaygroundContainerFactory;
|
|
24
|
+
provide(PlaygroundVueContainerKey, factory as any);
|
|
25
|
+
try {
|
|
26
|
+
provide(PlaygroundVueRefKey, factory.get(Playground));
|
|
27
|
+
} catch {
|
|
28
|
+
// 测试容器可能重复 bind PlaygroundConfig,此时仅 provide container
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
render() {
|
|
32
|
+
return this.$slots.default?.();
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export function wrapLayerRender(factory: PlaygroundContainerFactory, children: VNodeChild) {
|
|
37
|
+
return h(LayerVueProvide, { factory }, () => children);
|
|
38
|
+
}
|