@flowgram-vue/core 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 +0 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +0 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
- package/src/common/config-entity.ts +80 -0
- package/src/common/entity-data.ts +171 -0
- package/src/common/entity-manager-contribution.ts +12 -0
- package/src/common/entity-manager.ts +475 -0
- package/src/common/entity.ts +482 -0
- package/src/common/index.ts +22 -0
- package/src/common/playground-context.ts +35 -0
- package/src/common/playground-decorator-helper.ts +113 -0
- package/src/common/playground-decorators.ts +85 -0
- package/src/common/playground-schedule.ts +30 -0
- package/src/common/protect-wheel-area.ts +11 -0
- package/src/common/schema/index.ts +14 -0
- package/src/common/schema/node.ts +15 -0
- package/src/common/schema/opacity-schema.ts +19 -0
- package/src/common/schema/origin-schema.ts +35 -0
- package/src/common/schema/position-schema.ts +35 -0
- package/src/common/schema/rotation-schema.ts +19 -0
- package/src/common/schema/scale-schema.ts +35 -0
- package/src/common/schema/size-schema.ts +42 -0
- package/src/common/schema/skew-schema.ts +35 -0
- package/src/common/schema/transform-schema.ts +613 -0
- package/src/common/utils/bounds.spec.ts +229 -0
- package/src/common/utils/bounds.ts +176 -0
- package/src/common/utils/index.ts +6 -0
- package/src/composables/index.ts +16 -0
- package/src/composables/use-config-entity.ts +31 -0
- package/src/composables/use-entities.ts +26 -0
- package/src/composables/use-entity-data-from-context.ts +34 -0
- package/src/composables/use-entity-from-context.ts +31 -0
- package/src/composables/use-listen-events.ts +22 -0
- package/src/composables/use-playground-container.ts +16 -0
- package/src/composables/use-playground-context.ts +15 -0
- package/src/composables/use-playground-drag.ts +31 -0
- package/src/composables/use-playground.ts +16 -0
- package/src/composables/use-refresh.ts +6 -0
- package/src/composables/use-service.ts +17 -0
- package/src/core/index.ts +8 -0
- package/src/core/layer/config/editor-state-config-entity.ts +180 -0
- package/src/core/layer/config/index.ts +7 -0
- package/src/core/layer/config/playground-config-entity.ts +604 -0
- package/src/core/layer/index.ts +8 -0
- package/src/core/layer/layer.ts +248 -0
- package/src/core/layer/playground-layer.ts +547 -0
- package/src/core/pipeline/index.ts +10 -0
- package/src/core/pipeline/pipeline-entities-selector.ts +200 -0
- package/src/core/pipeline/pipeline-entities.ts +230 -0
- package/src/core/pipeline/pipeline-registry.ts +344 -0
- package/src/core/pipeline/pipeline-renderer.ts +248 -0
- package/src/core/pipeline/pipeline-vue-utils.ts +96 -0
- package/src/core/pipeline/pipeline.ts +28 -0
- package/src/core/utils/index.ts +12 -0
- package/src/core/utils/inject-provider-decorators.ts +27 -0
- package/src/core/utils/lazy-inject-decorators.ts +38 -0
- package/src/core/utils/mouse-touch-event.ts +120 -0
- package/src/core/utils/playground-drag.ts +511 -0
- package/src/core/utils/playground-gesture.spec.ts +135 -0
- package/src/core/utils/playground-gesture.ts +118 -0
- package/src/core/utils/tween.ts +156 -0
- package/src/core/utils/use-gesture/core/Controller.ts +212 -0
- package/src/core/utils/use-gesture/core/EventStore.ts +46 -0
- package/src/core/utils/use-gesture/core/TimeoutStore.ts +28 -0
- package/src/core/utils/use-gesture/core/actions.ts +63 -0
- package/src/core/utils/use-gesture/core/config/commonConfigResolver.ts +86 -0
- package/src/core/utils/use-gesture/core/config/coordinatesConfigResolver.ts +48 -0
- package/src/core/utils/use-gesture/core/config/dragConfigResolver.ts +140 -0
- package/src/core/utils/use-gesture/core/config/hoverConfigResolver.ts +11 -0
- package/src/core/utils/use-gesture/core/config/moveConfigResolver.ts +11 -0
- package/src/core/utils/use-gesture/core/config/pinchConfigResolver.ts +59 -0
- package/src/core/utils/use-gesture/core/config/resolver.ts +70 -0
- package/src/core/utils/use-gesture/core/config/scrollConfigResolver.ts +8 -0
- package/src/core/utils/use-gesture/core/config/sharedConfigResolver.ts +28 -0
- package/src/core/utils/use-gesture/core/config/support.ts +52 -0
- package/src/core/utils/use-gesture/core/config/wheelConfigResolver.ts +8 -0
- package/src/core/utils/use-gesture/core/engines/CoordinatesEngine.ts +78 -0
- package/src/core/utils/use-gesture/core/engines/DragEngine.ts +403 -0
- package/src/core/utils/use-gesture/core/engines/Engine.ts +406 -0
- package/src/core/utils/use-gesture/core/engines/HoverEngine.ts +43 -0
- package/src/core/utils/use-gesture/core/engines/MoveEngine.ts +52 -0
- package/src/core/utils/use-gesture/core/engines/PinchEngine.ts +345 -0
- package/src/core/utils/use-gesture/core/engines/ScrollEngine.ts +42 -0
- package/src/core/utils/use-gesture/core/engines/WheelEngine.ts +48 -0
- package/src/core/utils/use-gesture/core/index.ts +7 -0
- package/src/core/utils/use-gesture/core/parser.ts +92 -0
- package/src/core/utils/use-gesture/core/types/action.ts +19 -0
- package/src/core/utils/use-gesture/core/types/config.ts +255 -0
- package/src/core/utils/use-gesture/core/types/handlers.ts +73 -0
- package/src/core/utils/use-gesture/core/types/index.ts +11 -0
- package/src/core/utils/use-gesture/core/types/internalConfig.ts +80 -0
- package/src/core/utils/use-gesture/core/types/state.ts +275 -0
- package/src/core/utils/use-gesture/core/types/utils.ts +204 -0
- package/src/core/utils/use-gesture/core/types.ts +8 -0
- package/src/core/utils/use-gesture/core/utils/events.ts +158 -0
- package/src/core/utils/use-gesture/core/utils/fn.ts +32 -0
- package/src/core/utils/use-gesture/core/utils/maths.ts +63 -0
- package/src/core/utils/use-gesture/core/utils/state.ts +24 -0
- package/src/core/utils/use-gesture/core/utils.ts +8 -0
- package/src/core/utils/use-gesture/index.ts +6 -0
- package/src/core/utils/use-gesture/vanilla/DragGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/Gesture.ts +45 -0
- package/src/core/utils/use-gesture/vanilla/HoverGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/MoveGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/PinchGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/Recognizer.ts +43 -0
- package/src/core/utils/use-gesture/vanilla/ScrollGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/WheelGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/createGesture.ts +18 -0
- package/src/core/utils/use-gesture/vanilla/index.ts +18 -0
- package/src/env.d.ts +10 -0
- package/src/index.ts +17 -0
- package/src/playground-config.ts +70 -0
- package/src/playground-container.ts +129 -0
- package/src/playground-contribution.ts +81 -0
- package/src/playground-mock-tools.ts +143 -0
- package/src/playground.ts +401 -0
- package/src/plugin/index.ts +6 -0
- package/src/plugin/plugin.ts +226 -0
- package/src/services/clipboard-service.ts +40 -0
- package/src/services/context-menu-service.ts +25 -0
- package/src/services/index.ts +10 -0
- package/src/services/logger-service.ts +49 -0
- package/src/services/selection-service.ts +54 -0
- package/src/services/storage-service.ts +65 -0
- package/src/vue/index.ts +8 -0
- package/src/vue/playground-vue-context.ts +22 -0
- package/src/vue/playground-vue-provider.ts +115 -0
- package/src/vue/playground-vue-renderer.vue +44 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Disposable, domUtils, Emitter, PromiseDeferred, Rectangle } from '@flowgram-vue/utils'
|
|
7
|
+
import {
|
|
8
|
+
ConfigEntity,
|
|
9
|
+
Entity,
|
|
10
|
+
PositionData,
|
|
11
|
+
PositionSchema,
|
|
12
|
+
SizeData,
|
|
13
|
+
SizeSchema,
|
|
14
|
+
TransformData,
|
|
15
|
+
} from '../../../common'
|
|
16
|
+
import { MouseTouchEvent, startTween } from '../../utils'
|
|
17
|
+
// import { Selectable } from '../../able'
|
|
18
|
+
|
|
19
|
+
export interface PlaygroundConfigEntityData {
|
|
20
|
+
scrollX: number // 滚动 x
|
|
21
|
+
scrollY: number // 滚动 y
|
|
22
|
+
originX: number // 左上角默认开始的原点坐标
|
|
23
|
+
originY: number // 左上角默认开始原点坐标
|
|
24
|
+
width: number // 编辑区宽,在 onResize 触发后重制
|
|
25
|
+
height: number // 编辑区高,在 onResize 触发后重制
|
|
26
|
+
reverseScroll: boolean // 支持反方向滚动
|
|
27
|
+
overflowX: 'hidden' | 'scroll'
|
|
28
|
+
overflowY: 'hidden' | 'scroll'
|
|
29
|
+
minZoom: number // 最大
|
|
30
|
+
maxZoom: number // 最小
|
|
31
|
+
zoom: number // 缩放比
|
|
32
|
+
scrollLimitX?: number // 水平滚动限制
|
|
33
|
+
scrollLimitY?: number // 垂直滚动限制
|
|
34
|
+
mouseScrollDelta?: number | ((zoom: number) => number); // 鼠标滚动时的 delta 值
|
|
35
|
+
pageBounds?: { x: number; y: number; width: number; height: number } // 编辑的画布边框,用于处理外部对齐问题
|
|
36
|
+
disabled: boolean // 禁用状态
|
|
37
|
+
readonly: boolean // readonly 状态
|
|
38
|
+
scrollDisable: boolean
|
|
39
|
+
grabDisable: boolean // 禁用抓取拖拽画布能力
|
|
40
|
+
zoomDisable: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface PlaygroundConfigRevealOpts {
|
|
44
|
+
entities?: Entity[]
|
|
45
|
+
position?: PositionSchema // 滚动到指定位置,并居中
|
|
46
|
+
bounds?: Rectangle // 滚动的 bounds
|
|
47
|
+
// selection?: boolean 是否回到选择器所在位置,默认 true
|
|
48
|
+
scrollDelta?: PositionSchema
|
|
49
|
+
zoom?: number // 需要缩放的比例
|
|
50
|
+
easing?: boolean // 是否开启缓动,默认开启
|
|
51
|
+
easingDuration?: number // 默认 500 ms
|
|
52
|
+
scrollToCenter?: boolean // 是否滚动到中心
|
|
53
|
+
}
|
|
54
|
+
export const SCALE_WIDTH = 0
|
|
55
|
+
|
|
56
|
+
/** 鼠标缩放 delta */
|
|
57
|
+
export const MOUSE_SCROLL_DELTA = (zoom: number) => zoom / 20;
|
|
58
|
+
export type PlaygroundScrollLimitFn = (scroll: { scrollX: number; scrollY: number }) => {
|
|
59
|
+
scrollX: number
|
|
60
|
+
scrollY: number
|
|
61
|
+
}
|
|
62
|
+
export type Cursors = Record<string, string>;
|
|
63
|
+
/**
|
|
64
|
+
* 全局画布的配置信息
|
|
65
|
+
*/
|
|
66
|
+
export class PlaygroundConfigEntity extends ConfigEntity<PlaygroundConfigEntityData> {
|
|
67
|
+
static type = 'PlaygroundConfigEntity'
|
|
68
|
+
|
|
69
|
+
public getCursors: (() => Cursors | undefined) | undefined;
|
|
70
|
+
private _loading = false
|
|
71
|
+
private _zoomEnable = true
|
|
72
|
+
|
|
73
|
+
private _scrollLimitFn?: PlaygroundScrollLimitFn
|
|
74
|
+
|
|
75
|
+
private _onReadonlyOrDisabledChangeEmitter = new Emitter<{ readonly: boolean, disabled: boolean }>()
|
|
76
|
+
private _onGrabDisableChangeEmitter = new Emitter<boolean>()
|
|
77
|
+
readonly onGrabDisableChange = this._onGrabDisableChangeEmitter.event;
|
|
78
|
+
readonly onReadonlyOrDisabledChange = this._onReadonlyOrDisabledChangeEmitter.event
|
|
79
|
+
playgroundDomNode: HTMLElement = document.createElement('div')
|
|
80
|
+
|
|
81
|
+
cursor = 'default'
|
|
82
|
+
constructor(opts: any) {
|
|
83
|
+
super(opts);
|
|
84
|
+
this.toDispose.push(this._onReadonlyOrDisabledChangeEmitter)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 是否禁用抓取拖拽画布能力
|
|
89
|
+
*/
|
|
90
|
+
get grabDisable(): boolean {
|
|
91
|
+
return this.config.grabDisable;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 是否禁用抓取拖拽画布能力
|
|
96
|
+
*/
|
|
97
|
+
set grabDisable(grabDisable: boolean) {
|
|
98
|
+
this.updateConfig({
|
|
99
|
+
grabDisable
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
get scrollDisable(): boolean {
|
|
103
|
+
return this.config.scrollDisable;
|
|
104
|
+
}
|
|
105
|
+
set scrollDisable(scrollDisable: boolean) {
|
|
106
|
+
this.updateConfig({
|
|
107
|
+
scrollDisable
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
get zoomDisable(): boolean {
|
|
111
|
+
return this.config.zoomDisable;
|
|
112
|
+
}
|
|
113
|
+
set zoomDisable(zoomDisable: boolean) {
|
|
114
|
+
this.updateConfig({
|
|
115
|
+
zoomDisable
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
getDefaultConfig(): PlaygroundConfigEntityData {
|
|
119
|
+
return {
|
|
120
|
+
scrollX: 0,
|
|
121
|
+
scrollY: 0,
|
|
122
|
+
originX: 0,
|
|
123
|
+
originY: 0,
|
|
124
|
+
width: 0,
|
|
125
|
+
height: 0,
|
|
126
|
+
minZoom: 0.25,
|
|
127
|
+
maxZoom: 2,
|
|
128
|
+
zoom: 1,
|
|
129
|
+
reverseScroll: true,
|
|
130
|
+
overflowX: 'scroll',
|
|
131
|
+
overflowY: 'scroll',
|
|
132
|
+
disabled: false,
|
|
133
|
+
readonly: false,
|
|
134
|
+
grabDisable: false,
|
|
135
|
+
scrollDisable: false,
|
|
136
|
+
zoomDisable: false,
|
|
137
|
+
mouseScrollDelta: MOUSE_SCROLL_DELTA
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 添加滚动限制逻辑
|
|
143
|
+
* @param fn
|
|
144
|
+
*/
|
|
145
|
+
addScrollLimit(fn: PlaygroundScrollLimitFn): void {
|
|
146
|
+
this._scrollLimitFn = fn
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 更新实体配置
|
|
151
|
+
* @param props
|
|
152
|
+
*/
|
|
153
|
+
updateConfig(props: Partial<PlaygroundConfigEntityData>): void {
|
|
154
|
+
if (props.zoom !== undefined) {
|
|
155
|
+
props = { ...props, zoom: this.normalizeZoom(props.zoom) }
|
|
156
|
+
}
|
|
157
|
+
props = { ...this.config, ...props }
|
|
158
|
+
if (!props.reverseScroll) {
|
|
159
|
+
if (props.scrollX! < this.config.originX) {
|
|
160
|
+
props.scrollX = this.config.originX
|
|
161
|
+
}
|
|
162
|
+
if (props.scrollY! < this.config.originY) {
|
|
163
|
+
props.scrollY = this.config.originY
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (props.scrollLimitX !== undefined && props.scrollX! < props.scrollLimitX) {
|
|
167
|
+
props.scrollX = props.scrollLimitX
|
|
168
|
+
}
|
|
169
|
+
if (props.scrollLimitY !== undefined && props.scrollY! < props.scrollLimitY) {
|
|
170
|
+
props.scrollY = props.scrollLimitY
|
|
171
|
+
}
|
|
172
|
+
if (props.overflowX === 'hidden') {
|
|
173
|
+
props.scrollX = this.config.originX
|
|
174
|
+
}
|
|
175
|
+
if (props.overflowY === 'hidden') {
|
|
176
|
+
props.scrollY = this.config.originY
|
|
177
|
+
}
|
|
178
|
+
const scrollDisable = props.scrollDisable || this.scrollDisable
|
|
179
|
+
const { readonly, disabled, grabDisable } = this
|
|
180
|
+
if (scrollDisable) {
|
|
181
|
+
props.scrollX = this.config.scrollX
|
|
182
|
+
props.scrollY = this.config.scrollY
|
|
183
|
+
}
|
|
184
|
+
super.updateConfig(
|
|
185
|
+
this._scrollLimitFn
|
|
186
|
+
? { ...props, ...this._scrollLimitFn({ scrollX: props.scrollX!, scrollY: props.scrollY! }) }
|
|
187
|
+
: props
|
|
188
|
+
)
|
|
189
|
+
const readonlyOrDisableChanged = readonly !== this.readonly || disabled !== this.disabled
|
|
190
|
+
if (readonlyOrDisableChanged) this._onReadonlyOrDisabledChangeEmitter.fire({ readonly: this.readonly, disabled: this.disabled })
|
|
191
|
+
if (grabDisable !== this.grabDisable) this._onGrabDisableChangeEmitter.fire(this.grabDisable)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 缩放比例
|
|
196
|
+
* 使用 zoom 替代
|
|
197
|
+
* @deprecated
|
|
198
|
+
*/
|
|
199
|
+
get finalScale(): number {
|
|
200
|
+
if (!this.zoomEnable) return 1
|
|
201
|
+
return this.config.zoom
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 缩放比例
|
|
206
|
+
*/
|
|
207
|
+
get zoom(): number {
|
|
208
|
+
if (!this.zoomEnable) return 1
|
|
209
|
+
return this.config.zoom
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
get scrollData(): { scrollX: number; scrollY: number } {
|
|
213
|
+
return {
|
|
214
|
+
scrollX: this.config.scrollX,
|
|
215
|
+
scrollY: this.config.scrollY,
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
protected normalizeZoom(zoom: number): number {
|
|
220
|
+
if (!this.zoomEnable) return 1
|
|
221
|
+
if (this.zoomDisable) return this.config.zoom;
|
|
222
|
+
if (zoom < this.config.minZoom) {
|
|
223
|
+
zoom = this.config.minZoom
|
|
224
|
+
} else if (zoom > this.config.maxZoom) {
|
|
225
|
+
zoom = this.config.maxZoom
|
|
226
|
+
}
|
|
227
|
+
return zoom
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 修改画布光标
|
|
232
|
+
* @param cursor
|
|
233
|
+
*/
|
|
234
|
+
updateCursor(cursor: string): void {
|
|
235
|
+
if (this.cursor !== cursor) {
|
|
236
|
+
this.cursor = cursor
|
|
237
|
+
this.fireChange()
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 获取相对画布的位置
|
|
243
|
+
* @param event
|
|
244
|
+
* @param widthScale 是否要计算缩放
|
|
245
|
+
*/
|
|
246
|
+
getPosFromMouseEvent(
|
|
247
|
+
event:
|
|
248
|
+
| MouseEvent
|
|
249
|
+
| TouchEvent
|
|
250
|
+
| {
|
|
251
|
+
clientX: number;
|
|
252
|
+
clientY: number;
|
|
253
|
+
},
|
|
254
|
+
withScale = true
|
|
255
|
+
): PositionSchema {
|
|
256
|
+
const { config } = this
|
|
257
|
+
const scale = withScale ? this.zoom : 1
|
|
258
|
+
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event)
|
|
259
|
+
const clientRect = this.playgroundDomNode.getBoundingClientRect()
|
|
260
|
+
return {
|
|
261
|
+
x: (clientX + config.scrollX - clientRect.x) / scale,
|
|
262
|
+
y: (clientY + config.scrollY - clientRect.y) / scale,
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
getClientBounds(): Rectangle {
|
|
266
|
+
const clientRect = this.playgroundDomNode.getBoundingClientRect()
|
|
267
|
+
return new Rectangle(clientRect.x, clientRect.y, clientRect.width, clientRect.height)
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* 将画布中的位置转成相对 window 的位置
|
|
271
|
+
* @param pos
|
|
272
|
+
*/
|
|
273
|
+
toFixedPos(pos: PositionSchema): PositionSchema {
|
|
274
|
+
const { config } = this
|
|
275
|
+
const clientRect = this.playgroundDomNode.getBoundingClientRect()
|
|
276
|
+
return {
|
|
277
|
+
x: pos.x - config.scrollX + clientRect.x,
|
|
278
|
+
y: pos.y - config.scrollY + clientRect.y,
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 获取可视区域
|
|
284
|
+
*/
|
|
285
|
+
getViewport(withScale: boolean = true): Rectangle {
|
|
286
|
+
const { config } = this
|
|
287
|
+
const scale = withScale ? this.finalScale : 1
|
|
288
|
+
return new Rectangle(
|
|
289
|
+
config.scrollX / scale,
|
|
290
|
+
config.scrollY / scale,
|
|
291
|
+
config.width / scale,
|
|
292
|
+
config.height / scale
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 判断矩形是否在可视区域,如果有擦边页代表在可是区域
|
|
298
|
+
* @param bounds
|
|
299
|
+
* @param rotation
|
|
300
|
+
* @param includeAll - 是否包含在里边,默认 false
|
|
301
|
+
*/
|
|
302
|
+
isViewportVisible(bounds: Rectangle, rotation: number = 0, includeAll: boolean = false): boolean {
|
|
303
|
+
return Rectangle.isViewportVisible(bounds, this.getViewport(), rotation, includeAll)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 按下边顺序执行
|
|
308
|
+
* 1. 指定的 entity 位置或 pos 位置
|
|
309
|
+
* 3. 初始化位置
|
|
310
|
+
*/
|
|
311
|
+
scrollToView(opts: PlaygroundConfigRevealOpts = {}): Promise<void> {
|
|
312
|
+
const {
|
|
313
|
+
scrollDelta,
|
|
314
|
+
position: pos,
|
|
315
|
+
// selection = true,
|
|
316
|
+
easing = true,
|
|
317
|
+
easingDuration = 300,
|
|
318
|
+
entities,
|
|
319
|
+
} = opts
|
|
320
|
+
const { config } = this
|
|
321
|
+
const scale = opts.zoom ? opts.zoom : this.finalScale
|
|
322
|
+
let bounds: Rectangle | undefined
|
|
323
|
+
if (entities && entities.length > 0) {
|
|
324
|
+
const entitiesBounds = entities
|
|
325
|
+
.map((e) => {
|
|
326
|
+
const transform = e.getData<TransformData>(TransformData)
|
|
327
|
+
if (transform) return transform.bounds
|
|
328
|
+
const position = e.getData<PositionData>(PositionData)
|
|
329
|
+
const size = e.getData<SizeData>(SizeData) || { width: 0, height: 0 }
|
|
330
|
+
if (!position) return
|
|
331
|
+
return new Rectangle(position.x, position.y, size.width, size.height || 0)
|
|
332
|
+
})
|
|
333
|
+
.filter((e) => !!e) as Rectangle[]
|
|
334
|
+
if (entitiesBounds.length > 0) {
|
|
335
|
+
bounds = Rectangle.enlarge(entitiesBounds)
|
|
336
|
+
}
|
|
337
|
+
} else if (pos) {
|
|
338
|
+
bounds = new Rectangle(pos.x, pos.y, 0, 0)
|
|
339
|
+
} else if (opts.bounds) {
|
|
340
|
+
bounds = opts.bounds
|
|
341
|
+
} // else if (selection) {
|
|
342
|
+
// bounds = Selectable.getSelectedBounds(this.entityManager)
|
|
343
|
+
// }
|
|
344
|
+
if (!bounds) {
|
|
345
|
+
const defaultConfig = this.getDefaultConfig()
|
|
346
|
+
bounds = new Rectangle(
|
|
347
|
+
(defaultConfig.scrollX + config.width / 2) / scale,
|
|
348
|
+
(defaultConfig.scrollY + config.height / 2) / scale,
|
|
349
|
+
0,
|
|
350
|
+
0
|
|
351
|
+
)
|
|
352
|
+
}
|
|
353
|
+
if (!opts.scrollToCenter) {
|
|
354
|
+
const boundsVisible = this.getViewport()
|
|
355
|
+
// 判断是否看得见
|
|
356
|
+
if (boundsVisible.containsRectangle(bounds)) {
|
|
357
|
+
return Promise.resolve()
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// TODO 微调滚动,而不是直接滚动到中心
|
|
361
|
+
const toValues = {
|
|
362
|
+
scrollX:
|
|
363
|
+
(bounds.x + bounds.width / 2 + (scrollDelta ? scrollDelta.x : 0)) * scale -
|
|
364
|
+
config.width / 2,
|
|
365
|
+
scrollY:
|
|
366
|
+
(bounds.y + bounds.height / 2 + (scrollDelta ? scrollDelta.y : 0)) * scale -
|
|
367
|
+
config.height / 2,
|
|
368
|
+
zoom: opts.zoom,
|
|
369
|
+
}
|
|
370
|
+
return this.scroll(toValues, easing, easingDuration)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* 这只画布边框,元素编辑的时候回吸附画布边框
|
|
375
|
+
* @param bounds
|
|
376
|
+
*/
|
|
377
|
+
setPageBounds(bounds: Rectangle): void {
|
|
378
|
+
this.updateConfig({
|
|
379
|
+
pageBounds: {
|
|
380
|
+
x: bounds.x,
|
|
381
|
+
y: bounds.y,
|
|
382
|
+
width: bounds.width,
|
|
383
|
+
height: bounds.height,
|
|
384
|
+
},
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
getPageBounds(): Rectangle | undefined {
|
|
389
|
+
const { pageBounds } = this.config
|
|
390
|
+
if (pageBounds) {
|
|
391
|
+
return new Rectangle(pageBounds.x, pageBounds.y, pageBounds.width, pageBounds.height)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* 滚动到画布中央
|
|
397
|
+
* @param zoomToFit 是否缩放并适配外围大小
|
|
398
|
+
* @param fitPadding 适配外围的留白
|
|
399
|
+
* @param easing 是否缓动
|
|
400
|
+
*/
|
|
401
|
+
scrollPageBoundsToCenter(
|
|
402
|
+
zoomToFit: boolean = true,
|
|
403
|
+
fitPadding = 16,
|
|
404
|
+
easing = true
|
|
405
|
+
): Promise<void> {
|
|
406
|
+
const pageBounds = this.getPageBounds()
|
|
407
|
+
if (pageBounds) {
|
|
408
|
+
let zoom: number | undefined
|
|
409
|
+
const fitPaddingDouble = fitPadding * 2
|
|
410
|
+
if (zoomToFit) {
|
|
411
|
+
const fixedScale = SizeSchema.fixSize(
|
|
412
|
+
{
|
|
413
|
+
width: pageBounds.width,
|
|
414
|
+
height: pageBounds.height,
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
width:
|
|
418
|
+
fitPaddingDouble > this.config.width
|
|
419
|
+
? fitPaddingDouble
|
|
420
|
+
: this.config.width - fitPaddingDouble,
|
|
421
|
+
height:
|
|
422
|
+
fitPaddingDouble > this.config.height
|
|
423
|
+
? fitPaddingDouble
|
|
424
|
+
: this.config.height - fitPaddingDouble,
|
|
425
|
+
}
|
|
426
|
+
)
|
|
427
|
+
zoom = fixedScale
|
|
428
|
+
}
|
|
429
|
+
return this.scrollToView({
|
|
430
|
+
bounds: pageBounds,
|
|
431
|
+
zoom,
|
|
432
|
+
scrollToCenter: true,
|
|
433
|
+
// selection: false,
|
|
434
|
+
easing,
|
|
435
|
+
})
|
|
436
|
+
}
|
|
437
|
+
return this.scrollToView({ easing })
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private cancelScrollTeeen?: Disposable
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* 滚动
|
|
444
|
+
* @param scroll
|
|
445
|
+
* @param easing - 是否开启缓动,默认开启
|
|
446
|
+
* @param easingDuration - 滚动持续时间,默认 300ms
|
|
447
|
+
*/
|
|
448
|
+
scroll(
|
|
449
|
+
scroll: Partial<{ scrollX: number; scrollY: number; zoom: number }>,
|
|
450
|
+
easing: boolean = true,
|
|
451
|
+
easingDuration = 300
|
|
452
|
+
): Promise<void> {
|
|
453
|
+
const deferred = new PromiseDeferred<void>()
|
|
454
|
+
if (this.cancelScrollTeeen) this.cancelScrollTeeen.dispose()
|
|
455
|
+
if (easing) {
|
|
456
|
+
const fromValues = {
|
|
457
|
+
scrollX: this.config.scrollX,
|
|
458
|
+
scrollY: this.config.scrollY,
|
|
459
|
+
zoom: this.config.zoom,
|
|
460
|
+
}
|
|
461
|
+
this.cancelScrollTeeen = startTween({
|
|
462
|
+
from: fromValues,
|
|
463
|
+
to: {
|
|
464
|
+
...fromValues,
|
|
465
|
+
...scroll,
|
|
466
|
+
},
|
|
467
|
+
onUpdate: (v) => {
|
|
468
|
+
this.updateConfig(v)
|
|
469
|
+
},
|
|
470
|
+
onComplete: () => {
|
|
471
|
+
this.cancelScrollTeeen = undefined
|
|
472
|
+
deferred.resolve()
|
|
473
|
+
},
|
|
474
|
+
onDispose: () => {
|
|
475
|
+
deferred.resolve()
|
|
476
|
+
},
|
|
477
|
+
duration: easingDuration,
|
|
478
|
+
})
|
|
479
|
+
} else {
|
|
480
|
+
this.updateConfig(scroll)
|
|
481
|
+
deferred.resolve()
|
|
482
|
+
}
|
|
483
|
+
return deferred.promise
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* 让 layer 的 node 节点不随着画布滚动条滚动
|
|
488
|
+
* @param layerNode
|
|
489
|
+
*/
|
|
490
|
+
fixLayerPosition(layerNode: HTMLElement): void {
|
|
491
|
+
domUtils.setStyle(layerNode, {
|
|
492
|
+
left: this.config.scrollX,
|
|
493
|
+
top: this.config.scrollY,
|
|
494
|
+
})
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
get loading(): boolean {
|
|
498
|
+
return this._loading
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
set loading(loading: boolean) {
|
|
502
|
+
if (this.loading !== loading) {
|
|
503
|
+
this._loading = loading
|
|
504
|
+
this.fireChange()
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* @deprecated use 'zoomDisable' instead
|
|
510
|
+
*/
|
|
511
|
+
get zoomEnable(): boolean {
|
|
512
|
+
return this._zoomEnable
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* 开启缩放
|
|
517
|
+
* @deprecated use 'zoomDisable' instead
|
|
518
|
+
*/
|
|
519
|
+
set zoomEnable(zoomEnable: boolean) {
|
|
520
|
+
if (this._zoomEnable !== zoomEnable) {
|
|
521
|
+
this._zoomEnable = zoomEnable
|
|
522
|
+
this.fireChange()
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* 放大
|
|
528
|
+
*/
|
|
529
|
+
zoomin(easing?: boolean, easingDuration?: number): void {
|
|
530
|
+
const unit = this.config.zoom / 10
|
|
531
|
+
const newZoom = Math.ceil((this.config.zoom + unit) * 10) / 10
|
|
532
|
+
this.updateZoom(newZoom, easing, easingDuration)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* 缩小
|
|
537
|
+
*/
|
|
538
|
+
zoomout(easing?: boolean, easingDuration?: number): void {
|
|
539
|
+
const unit = this.config.zoom / 10
|
|
540
|
+
const newZoom = Math.floor((this.config.zoom - unit) * 10) / 10
|
|
541
|
+
this.updateZoom(newZoom, easing, easingDuration)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
updateZoom(newZoom: number, easing: boolean = true, easingDuration = 200): void {
|
|
545
|
+
newZoom = this.normalizeZoom(newZoom)
|
|
546
|
+
const { center } = this.getViewport()
|
|
547
|
+
const oldScale = this.finalScale
|
|
548
|
+
const newScale = !this.zoomEnable ? oldScale : newZoom
|
|
549
|
+
if (newScale !== oldScale) {
|
|
550
|
+
const delta = {
|
|
551
|
+
x: center.x * newScale - center.x * oldScale,
|
|
552
|
+
y: center.y * newScale - center.y * oldScale,
|
|
553
|
+
}
|
|
554
|
+
this.scroll(
|
|
555
|
+
{
|
|
556
|
+
scrollX: this.config.scrollX + delta.x,
|
|
557
|
+
scrollY: this.config.scrollY + delta.y,
|
|
558
|
+
zoom: newZoom,
|
|
559
|
+
},
|
|
560
|
+
easing,
|
|
561
|
+
easingDuration
|
|
562
|
+
)
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
get disabled(): boolean {
|
|
566
|
+
return this.config.disabled
|
|
567
|
+
}
|
|
568
|
+
get readonly(): boolean {
|
|
569
|
+
return this.config.readonly
|
|
570
|
+
}
|
|
571
|
+
get readonlyOrDisabled(): boolean {
|
|
572
|
+
return this.config.readonly || this.config.disabled
|
|
573
|
+
}
|
|
574
|
+
set readonly(readonly) {
|
|
575
|
+
this.updateConfig({
|
|
576
|
+
readonly
|
|
577
|
+
})
|
|
578
|
+
}
|
|
579
|
+
set disabled(disabled) {
|
|
580
|
+
this.updateConfig({
|
|
581
|
+
disabled
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* 适应大小
|
|
587
|
+
* @param bounds 目标大小
|
|
588
|
+
* @param easing 是否开启动画,默认开启
|
|
589
|
+
* @param padding 边界空白
|
|
590
|
+
* @param easingDuration
|
|
591
|
+
*/
|
|
592
|
+
fitView(bounds: Rectangle, easing = true, padding = 0, easingDuration = 300): Promise<void> {
|
|
593
|
+
const viewport = this.getViewport(false);
|
|
594
|
+
const zoom = SizeSchema.fixSize(bounds.clone().pad(padding, padding), viewport);
|
|
595
|
+
return this.scrollToView({
|
|
596
|
+
bounds,
|
|
597
|
+
zoom,
|
|
598
|
+
easing,
|
|
599
|
+
easingDuration,
|
|
600
|
+
scrollToCenter: true,
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
}
|
|
604
|
+
}
|