@logicflow/extension 2.2.0-alpha.4 → 2.2.0-alpha.6
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +21 -0
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/es/components/control/index.js +3 -3
- package/es/materials/curved-edge/index.js +49 -21
- package/es/tools/snapshot/index.d.ts +7 -3
- package/es/tools/snapshot/index.js +46 -64
- package/lib/components/control/index.js +3 -3
- package/lib/materials/curved-edge/index.js +49 -21
- package/lib/tools/snapshot/index.d.ts +7 -3
- package/lib/tools/snapshot/index.js +46 -64
- package/package.json +5 -5
- package/src/components/control/index.ts +3 -3
- package/src/materials/curved-edge/index.ts +57 -25
- package/src/tools/snapshot/index.ts +47 -76
- package/stats.html +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logicflow/extension",
|
|
3
|
-
"version": "2.2.0-alpha.
|
|
3
|
+
"version": "2.2.0-alpha.6",
|
|
4
4
|
"description": "LogicFlow Extensions",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"author": "Logicflow-Team",
|
|
21
21
|
"license": "Apache-2.0",
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@logicflow/core": "2.2.0-alpha.
|
|
24
|
-
"@logicflow/vue-node-registry": "1.2.0-alpha.
|
|
23
|
+
"@logicflow/core": "2.2.0-alpha.6",
|
|
24
|
+
"@logicflow/vue-node-registry": "1.2.0-alpha.6"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@antv/hierarchy": "^0.6.11",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"preact": "^10.17.1",
|
|
33
33
|
"rangy": "^1.3.1",
|
|
34
34
|
"vanilla-picker": "^2.12.3",
|
|
35
|
-
"@logicflow/core": "2.2.0-alpha.
|
|
36
|
-
"@logicflow/vue-node-registry": "1.2.0-alpha.
|
|
35
|
+
"@logicflow/core": "2.2.0-alpha.6",
|
|
36
|
+
"@logicflow/vue-node-registry": "1.2.0-alpha.6"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"less": "^4.1.1",
|
|
@@ -130,13 +130,13 @@ export class Control {
|
|
|
130
130
|
} else {
|
|
131
131
|
itemContainer.append(icon)
|
|
132
132
|
}
|
|
133
|
-
switch (item.
|
|
134
|
-
case '
|
|
133
|
+
switch (item.key) {
|
|
134
|
+
case 'undo':
|
|
135
135
|
this.lf.on('history:change', ({ data: { undoAble } }: any) => {
|
|
136
136
|
itemContainer.className = undoAble ? NORMAL : DISABLED
|
|
137
137
|
})
|
|
138
138
|
break
|
|
139
|
-
case '
|
|
139
|
+
case 'redo':
|
|
140
140
|
this.lf.on('history:change', ({ data: { redoAble } }: any) => {
|
|
141
141
|
itemContainer.className = redoAble ? NORMAL : DISABLED
|
|
142
142
|
})
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { PolylineEdge, PolylineEdgeModel, h, LogicFlow } from '@logicflow/core'
|
|
2
2
|
import PointTuple = LogicFlow.PointTuple
|
|
3
3
|
|
|
4
|
+
// 方向类型:t=上(top), b=下(bottom), l=左(left), r=右(right),'' 表示未确定
|
|
4
5
|
type DirectionType = 't' | 'b' | 'l' | 'r' | ''
|
|
6
|
+
// 圆弧所在象限:tl=左上,tr=右上,bl=左下,br=右下,'-' 表示不需要圆弧
|
|
5
7
|
type ArcQuadrantType = 'tl' | 'tr' | 'bl' | 'br' | '-'
|
|
6
8
|
|
|
9
|
+
// 方向组合到圆弧象限的映射。
|
|
10
|
+
// key 由进入方向(dir1)和离开方向(dir2)拼接,例如 'tr' 表示从上(t)到右(r)的拐角。
|
|
11
|
+
// 通过该映射确定在拐点处应该绘制的圆弧象限,用于计算中间控制点。
|
|
7
12
|
const directionMap: {
|
|
8
13
|
[key: string]: ArcQuadrantType
|
|
9
14
|
} = {
|
|
@@ -17,19 +22,24 @@ const directionMap: {
|
|
|
17
22
|
rt: 'br',
|
|
18
23
|
}
|
|
19
24
|
|
|
25
|
+
// 过滤折线中的共线中间点,减少不必要的顶点
|
|
20
26
|
function pointFilter(points: number[][]) {
|
|
27
|
+
// 原地修改传入的数组
|
|
21
28
|
const all = points
|
|
29
|
+
// 从第二个点开始,检查三点是否共线
|
|
22
30
|
let i = 1
|
|
23
31
|
while (i < all.length - 1) {
|
|
24
32
|
const [x, y] = all[i - 1]
|
|
25
33
|
const [x1, y1] = all[i]
|
|
26
34
|
const [x2, y2] = all[i + 1]
|
|
35
|
+
// 如果三点在同一条水平或垂直直线上,删除中间点
|
|
27
36
|
if ((x === x1 && x1 === x2) || (y === y1 && y1 === y2)) {
|
|
28
37
|
all.splice(i, 1)
|
|
29
38
|
} else {
|
|
30
39
|
i++
|
|
31
40
|
}
|
|
32
41
|
}
|
|
42
|
+
// 返回精简后的点集
|
|
33
43
|
return all
|
|
34
44
|
}
|
|
35
45
|
|
|
@@ -87,45 +97,67 @@ function getMidPoints(
|
|
|
87
97
|
}
|
|
88
98
|
}
|
|
89
99
|
|
|
100
|
+
/**
|
|
101
|
+
* 生成局部路径片段(包含圆角)
|
|
102
|
+
* - 输入为上一个顶点、当前拐点、下一个顶点,计算方向组合并选择圆弧象限
|
|
103
|
+
* - 将圆角半径限制在相邻两段长度的一半以内,避免过度弯曲
|
|
104
|
+
* @param prevPoint 上一个顶点
|
|
105
|
+
* @param cornerPoint 当前拐点(圆角所在拐点)
|
|
106
|
+
* @param nextPoint 下一个顶点
|
|
107
|
+
* @param cornerRadius 圆角半径上限
|
|
108
|
+
* @returns 局部 path 字符串(包含 L/Q 操作)
|
|
109
|
+
*/
|
|
90
110
|
function getPartialPath(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
111
|
+
prevPoint: PointTuple,
|
|
112
|
+
cornerPoint: PointTuple,
|
|
113
|
+
nextPoint: PointTuple,
|
|
114
|
+
cornerRadius: number,
|
|
95
115
|
): string {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
116
|
+
// 轴对齐容差(像素),用于消除微小误差
|
|
117
|
+
const epsilon = 1
|
|
118
|
+
|
|
119
|
+
const resolveDir = (a: PointTuple, b: PointTuple): DirectionType => {
|
|
120
|
+
const dx = b[0] - a[0]
|
|
121
|
+
const dy = b[1] - a[1]
|
|
122
|
+
const adx = Math.abs(dx)
|
|
123
|
+
const ady = Math.abs(dy)
|
|
124
|
+
if (ady <= epsilon && adx > epsilon) {
|
|
125
|
+
return dx < 0 ? 'l' : 'r'
|
|
126
|
+
}
|
|
127
|
+
if (adx <= epsilon && ady > epsilon) {
|
|
128
|
+
return dy < 0 ? 't' : 'b'
|
|
129
|
+
}
|
|
130
|
+
if (adx <= epsilon && ady <= epsilon) {
|
|
131
|
+
return ''
|
|
132
|
+
}
|
|
133
|
+
// 非严格对齐时,选择更接近的轴
|
|
134
|
+
return adx < ady ? (dx < 0 ? 'l' : 'r') : dy < 0 ? 't' : 'b'
|
|
103
135
|
}
|
|
104
136
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
} else if (cur[1] === next[1]) {
|
|
108
|
-
dir2 = cur[0] > next[0] ? 'l' : 'r'
|
|
109
|
-
}
|
|
137
|
+
const dir1: DirectionType = resolveDir(prevPoint, cornerPoint)
|
|
138
|
+
const dir2: DirectionType = resolveDir(cornerPoint, nextPoint)
|
|
110
139
|
|
|
111
140
|
const r =
|
|
112
141
|
Math.min(
|
|
113
|
-
Math.hypot(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
142
|
+
Math.hypot(cornerPoint[0] - prevPoint[0], cornerPoint[1] - prevPoint[1]) /
|
|
143
|
+
2,
|
|
144
|
+
Math.hypot(nextPoint[0] - cornerPoint[0], nextPoint[1] - cornerPoint[1]) /
|
|
145
|
+
2,
|
|
146
|
+
cornerRadius,
|
|
147
|
+
) || (1 / 5) * cornerRadius
|
|
117
148
|
|
|
118
149
|
const key = `${dir1}${dir2}`
|
|
119
150
|
const orientation: ArcQuadrantType = directionMap[key] || '-'
|
|
120
|
-
let path =
|
|
151
|
+
let path = ''
|
|
121
152
|
|
|
122
153
|
if (orientation === '-') {
|
|
123
|
-
|
|
154
|
+
// 仅移动到当前拐点,由下一次迭代决定如何从拐点继续(直线或圆角)
|
|
155
|
+
path += `L ${cornerPoint[0]} ${cornerPoint[1]}`
|
|
124
156
|
} else {
|
|
125
|
-
const [mid1, mid2] = getMidPoints(
|
|
157
|
+
const [mid1, mid2] = getMidPoints(cornerPoint, key, orientation, r)
|
|
126
158
|
if (mid1 && mid2) {
|
|
127
|
-
path += `L ${mid1[0]} ${mid1[1]} Q ${
|
|
128
|
-
;[
|
|
159
|
+
path += `L ${mid1[0]} ${mid1[1]} Q ${cornerPoint[0]} ${cornerPoint[1]} ${mid2[0]} ${mid2[1]}`
|
|
160
|
+
;[cornerPoint[0], cornerPoint[1]] = mid2
|
|
129
161
|
}
|
|
130
162
|
}
|
|
131
163
|
return path
|
|
@@ -315,31 +315,34 @@ export class Snapshot {
|
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
318
|
+
* 根据浏览器对 canvas 的大小限制,计算等比缩放比例
|
|
319
|
+
* - 参考 MDN 最大的画布尺寸:
|
|
320
|
+
* https://developer.mozilla.org/zh-CN/docs/Web/HTML/Reference/Elements/canvas#最大的画布尺寸
|
|
321
|
+
*
|
|
322
|
+
* @param width
|
|
323
|
+
* @param height
|
|
320
324
|
*/
|
|
321
|
-
private
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const
|
|
325
|
+
private getCanvasScaleRatio(width: number, height: number): number {
|
|
326
|
+
/* 单边最大像素 */
|
|
327
|
+
const maxCanvasSide = 32767
|
|
328
|
+
/* 最大像素面积 */
|
|
329
|
+
const maxCanvasArea = 268435456
|
|
326
330
|
|
|
327
|
-
|
|
328
|
-
let maxCanvasDimension = 65535
|
|
329
|
-
let otherMaxCanvasDimension = 4096
|
|
331
|
+
const area = width * height
|
|
330
332
|
|
|
331
333
|
if (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
+
width <= maxCanvasSide &&
|
|
335
|
+
height <= maxCanvasSide &&
|
|
336
|
+
area <= maxCanvasArea
|
|
334
337
|
) {
|
|
335
|
-
|
|
336
|
-
otherMaxCanvasDimension = 4096
|
|
337
|
-
} else if (userAgent.indexOf('Firefox') !== -1) {
|
|
338
|
-
maxCanvasDimension = 32767
|
|
339
|
-
otherMaxCanvasDimension = 3814
|
|
338
|
+
return 1
|
|
340
339
|
}
|
|
341
340
|
|
|
342
|
-
|
|
341
|
+
const widthScale = maxCanvasSide / width
|
|
342
|
+
const heightScale = maxCanvasSide / height
|
|
343
|
+
const areaScale = maxCanvasArea / area
|
|
344
|
+
|
|
345
|
+
return Math.min(widthScale, heightScale, areaScale)
|
|
343
346
|
}
|
|
344
347
|
|
|
345
348
|
/**
|
|
@@ -389,76 +392,50 @@ export class Snapshot {
|
|
|
389
392
|
// 计算实际宽高,考虑缩放因素
|
|
390
393
|
// 在宽画布情况下,getBoundingClientRect可能无法获取到所有元素的边界
|
|
391
394
|
// 因此我们添加一个安全系数来确保能够容纳所有元素
|
|
392
|
-
const safetyFactor = toImageOptions.safetyFactor || 1
|
|
393
|
-
const actualWidth =
|
|
394
|
-
const actualHeight =
|
|
395
|
+
const safetyFactor = toImageOptions.safetyFactor || 1 // 安全系数,增加10%的空间
|
|
396
|
+
const actualWidth = bbox.width / SCALE_X
|
|
397
|
+
const actualHeight = bbox.height / SCALE_Y
|
|
398
|
+
const factorWidth = actualWidth * (safetyFactor - 1)
|
|
399
|
+
const factorHeight = actualHeight * (safetyFactor - 1)
|
|
395
400
|
|
|
396
401
|
// 包含所有元素的最小宽高,确保足够大以容纳所有元素
|
|
397
|
-
const bboxWidth = Math.ceil(actualWidth)
|
|
398
|
-
const bboxHeight = Math.ceil(actualHeight)
|
|
402
|
+
const bboxWidth = Math.ceil(actualWidth + factorWidth)
|
|
403
|
+
const bboxHeight = Math.ceil(actualHeight + factorHeight)
|
|
399
404
|
const canvas = document.createElement('canvas')
|
|
400
405
|
canvas.style.width = `${bboxWidth}px`
|
|
401
406
|
canvas.style.height = `${bboxHeight}px`
|
|
402
407
|
|
|
403
408
|
// 宽高值 默认加padding 40,保证图形不会紧贴着下载图片
|
|
404
409
|
// 为宽画布添加额外的安全边距,确保不会裁剪
|
|
405
|
-
const safetyMargin = toImageOptions.safetyMargin ||
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
this.getCanvasDimensionsByBrowser()
|
|
410
|
-
const MAX_CANVAS_DIMENSION = maxCanvasDimension
|
|
411
|
-
const OTHER_MAX_CANVAS_DIMENSION = otherMaxCanvasDimension
|
|
412
|
-
|
|
413
|
-
let targetWidth = bboxWidth * dpr + padding * 2 + safetyMargin
|
|
414
|
-
let targetHeight = bboxHeight * dpr + padding * 2 + safetyMargin
|
|
415
|
-
let scaleWidth = 1 //宽 缩放
|
|
416
|
-
let scaleHeight = 1 //高 缩放
|
|
417
|
-
// 对宽和高分别进行缩放,如chrome,矩形单边最大宽度不超过65535,如宽超过65535,那么高不能超过4096,否则像素会超,也会显示不出。
|
|
418
|
-
if (
|
|
419
|
-
targetWidth > MAX_CANVAS_DIMENSION &&
|
|
420
|
-
targetHeight > OTHER_MAX_CANVAS_DIMENSION
|
|
421
|
-
) {
|
|
422
|
-
scaleWidth = MAX_CANVAS_DIMENSION / targetWidth
|
|
423
|
-
scaleHeight = OTHER_MAX_CANVAS_DIMENSION / targetHeight
|
|
424
|
-
} else if (
|
|
425
|
-
targetWidth > OTHER_MAX_CANVAS_DIMENSION &&
|
|
426
|
-
targetHeight > MAX_CANVAS_DIMENSION
|
|
427
|
-
) {
|
|
428
|
-
scaleWidth = OTHER_MAX_CANVAS_DIMENSION / targetWidth
|
|
429
|
-
scaleHeight = MAX_CANVAS_DIMENSION / targetHeight
|
|
430
|
-
} else if (
|
|
431
|
-
targetWidth > MAX_CANVAS_DIMENSION &&
|
|
432
|
-
targetHeight < OTHER_MAX_CANVAS_DIMENSION
|
|
433
|
-
) {
|
|
434
|
-
scaleHeight = scaleWidth = MAX_CANVAS_DIMENSION / targetWidth
|
|
435
|
-
} else if (
|
|
436
|
-
targetWidth < OTHER_MAX_CANVAS_DIMENSION &&
|
|
437
|
-
targetHeight > MAX_CANVAS_DIMENSION
|
|
438
|
-
) {
|
|
439
|
-
scaleHeight = scaleWidth = MAX_CANVAS_DIMENSION / targetHeight
|
|
440
|
-
}
|
|
410
|
+
const safetyMargin = toImageOptions.safetyMargin || 0 // 额外的安全边距
|
|
411
|
+
|
|
412
|
+
let targetWidth = bboxWidth * dpr
|
|
413
|
+
let targetHeight = bboxHeight * dpr
|
|
441
414
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
415
|
+
// 超出 canvas 大小限制时,进行等比缩放
|
|
416
|
+
const scaleRatio = this.getCanvasScaleRatio(targetWidth, targetHeight)
|
|
417
|
+
if (scaleRatio < 1) {
|
|
418
|
+
targetWidth = Math.floor(targetWidth * scaleRatio)
|
|
419
|
+
targetHeight = Math.floor(targetHeight * scaleRatio)
|
|
445
420
|
}
|
|
421
|
+
|
|
446
422
|
// 将导出区域移动到左上角,canvas 绘制的时候是从左上角开始绘制的
|
|
447
423
|
// 在transform矩阵中加入padding值,确保左侧元素不会被截断
|
|
448
424
|
// 对这个矩阵进行缩放,否则会导致截断
|
|
449
425
|
;(copy.lastChild as SVGElement).style.transform =
|
|
450
|
-
`matrix(${
|
|
451
|
-
(-offsetX + TRANSLATE_X) * (1 / SCALE_X) *
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
canvas.
|
|
426
|
+
`matrix(${scaleRatio}, 0, 0, ${scaleRatio}, ${
|
|
427
|
+
(-offsetX + TRANSLATE_X) * (1 / SCALE_X) * scaleRatio +
|
|
428
|
+
padding +
|
|
429
|
+
factorWidth / 2 +
|
|
430
|
+
safetyMargin
|
|
431
|
+
}, ${(-offsetY + TRANSLATE_Y) * (1 / SCALE_Y) * scaleRatio + padding + factorHeight / 2 + safetyMargin})`
|
|
432
|
+
canvas.width = targetWidth + (padding + safetyMargin) * 2 * dpr
|
|
433
|
+
canvas.height = targetHeight + (padding + safetyMargin) * 2 * dpr
|
|
457
434
|
const ctx = canvas.getContext('2d')
|
|
458
435
|
if (ctx) {
|
|
459
436
|
// 清空canvas
|
|
460
437
|
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
461
|
-
ctx.scale(dpr
|
|
438
|
+
ctx.scale(dpr, dpr)
|
|
462
439
|
// 如果有背景色,设置流程图导出的背景色
|
|
463
440
|
if (backgroundColor) {
|
|
464
441
|
ctx.fillStyle = backgroundColor
|
|
@@ -690,12 +667,6 @@ export class Snapshot {
|
|
|
690
667
|
fileType?: string,
|
|
691
668
|
toImageOptions?: ToImageOptions,
|
|
692
669
|
): Promise<SnapshotResponse> {
|
|
693
|
-
console.log(
|
|
694
|
-
'getSnapshotBase64---------------',
|
|
695
|
-
backgroundColor,
|
|
696
|
-
fileType,
|
|
697
|
-
toImageOptions,
|
|
698
|
-
)
|
|
699
670
|
return await this.withExportPreparation(
|
|
700
671
|
() => this._getSnapshotBase64(backgroundColor, fileType, toImageOptions),
|
|
701
672
|
toImageOptions,
|