jmgraph 3.2.20 → 3.2.22
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/README.md +137 -4
- package/dist/jmgraph.core.min.js +1 -1
- package/dist/jmgraph.core.min.js.map +1 -1
- package/dist/jmgraph.js +1064 -872
- package/dist/jmgraph.min.js +1 -1
- package/index.js +13 -26
- package/package.json +1 -1
- package/src/core/jmControl.js +208 -31
- package/src/core/jmFilter.js +150 -0
- package/src/core/jmGraph.js +30 -273
- package/src/core/jmUtils.js +46 -37
- package/src/lib/webgl/base.js +10 -36
- package/src/lib/webgl/gradient.js +16 -3
- package/src/lib/webgl/index.js +5 -4
- package/src/lib/webgl/path.js +164 -35
- package/src/shapes/jmArc.js +21 -7
- package/src/shapes/jmLabel.js +1 -62
- package/src/shapes/jmRect.js +107 -29
package/src/lib/webgl/path.js
CHANGED
|
@@ -9,6 +9,40 @@ class WebglPath extends WebglBase {
|
|
|
9
9
|
this.needCut = option.needCut || false;
|
|
10
10
|
this.control = option.control;
|
|
11
11
|
this.points = [];
|
|
12
|
+
// 缓存 buffer 和纹理,避免每帧创建/销毁
|
|
13
|
+
this.__cachedBuffers = [];
|
|
14
|
+
this.__cachedTexture = null;
|
|
15
|
+
this.__cachedTextureKey = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 释放缓存的 WebGL 资源
|
|
19
|
+
dispose() {
|
|
20
|
+
for(const buf of this.__cachedBuffers) {
|
|
21
|
+
this.deleteBuffer(buf);
|
|
22
|
+
}
|
|
23
|
+
this.__cachedBuffers = [];
|
|
24
|
+
if(this.__cachedTexture) {
|
|
25
|
+
this.deleteTexture(this.__cachedTexture);
|
|
26
|
+
this.__cachedTexture = null;
|
|
27
|
+
this.__cachedTextureKey = null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 获取或创建 buffer,优先复用缓存
|
|
32
|
+
getOrCreateBuffer(data, attr) {
|
|
33
|
+
let buffer = this.__cachedBuffers.find(b => b.attr === attr);
|
|
34
|
+
if(buffer) {
|
|
35
|
+
const gl = this.context;
|
|
36
|
+
const float32 = new Float32Array(data);
|
|
37
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer);
|
|
38
|
+
gl.bufferData(gl.ARRAY_BUFFER, float32, gl.DYNAMIC_DRAW);
|
|
39
|
+
buffer.data = data;
|
|
40
|
+
return buffer;
|
|
41
|
+
}
|
|
42
|
+
buffer = this.createFloat32Buffer(data);
|
|
43
|
+
buffer.attr = attr;
|
|
44
|
+
this.__cachedBuffers.push(buffer);
|
|
45
|
+
return buffer;
|
|
12
46
|
}
|
|
13
47
|
|
|
14
48
|
// 应用变换到点
|
|
@@ -21,8 +55,14 @@ class WebglPath extends WebglBase {
|
|
|
21
55
|
//this.useProgram();
|
|
22
56
|
|
|
23
57
|
if(parentBounds) this.parentAbsoluteBounds = parentBounds;
|
|
24
|
-
//
|
|
25
|
-
|
|
58
|
+
// 缓存中心点值,只在变化时才更新 uniform
|
|
59
|
+
const cx = this.graph.width / 2;
|
|
60
|
+
const cy = this.graph.height / 2;
|
|
61
|
+
if(this.__lastCenterX !== cx || this.__lastCenterY !== cy) {
|
|
62
|
+
this.context.uniform2f(this.program.uniforms.a_center_point.location, cx, cy);
|
|
63
|
+
this.__lastCenterX = cx;
|
|
64
|
+
this.__lastCenterY = cy;
|
|
65
|
+
}
|
|
26
66
|
}
|
|
27
67
|
|
|
28
68
|
setFragColor(color) {
|
|
@@ -63,6 +103,7 @@ class WebglPath extends WebglBase {
|
|
|
63
103
|
endDraw() {
|
|
64
104
|
if(this.points) delete this.points;
|
|
65
105
|
if(this.pathPoints) delete this.pathPoints;
|
|
106
|
+
// 缓存的纹理保留到下次绘制(渐变可能不变)
|
|
66
107
|
}
|
|
67
108
|
|
|
68
109
|
// 图形封闭
|
|
@@ -74,21 +115,47 @@ class WebglPath extends WebglBase {
|
|
|
74
115
|
}
|
|
75
116
|
}
|
|
76
117
|
|
|
77
|
-
//
|
|
118
|
+
// 绘制点数组(使用 DYNAMIC_DRAW 复用 buffer,避免每帧 create/delete)
|
|
78
119
|
writePoints(points, attr = this.program.attrs.a_position) {
|
|
79
|
-
|
|
80
120
|
const fixedPoints = [];
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
const [a, b, c, d, tx, ty] = this.transformMatrix;
|
|
122
|
+
const isIdentity = (a === 1 && b === 0 && c === 0 && d === 1 && tx === 0 && ty === 0);
|
|
123
|
+
const offsetLeft = this.parentAbsoluteBounds.left;
|
|
124
|
+
const offsetTop = this.parentAbsoluteBounds.top;
|
|
125
|
+
|
|
126
|
+
if(isIdentity) {
|
|
127
|
+
// 单位矩阵时直接加偏移,避免逐点调用 applyTransform
|
|
128
|
+
for(let i = 0; i < points.length; i++) {
|
|
129
|
+
fixedPoints.push(points[i].x + offsetLeft, points[i].y + offsetTop);
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
for(const p of points) {
|
|
133
|
+
const transformedPoint = this.applyTransform(p);
|
|
134
|
+
fixedPoints.push(
|
|
135
|
+
transformedPoint.x + offsetLeft,
|
|
136
|
+
transformedPoint.y + offsetTop
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const float32 = new Float32Array(fixedPoints);
|
|
141
|
+
const gl = this.context;
|
|
142
|
+
|
|
143
|
+
// 复用已有 buffer 或创建新的
|
|
144
|
+
if(this.__cachedBuffers.length > 0) {
|
|
145
|
+
// 找一个同 attr 的 buffer 复用
|
|
146
|
+
let buffer = this.__cachedBuffers.find(b => b.attr === attr);
|
|
147
|
+
if(buffer) {
|
|
148
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer);
|
|
149
|
+
gl.bufferData(gl.ARRAY_BUFFER, float32, gl.DYNAMIC_DRAW);
|
|
150
|
+
buffer.data = fixedPoints;
|
|
151
|
+
this.writeVertexAttrib(buffer, attr, 2, 0, 0);
|
|
152
|
+
return buffer;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const vertexBuffer = this.createFloat32Buffer(float32, gl.ARRAY_BUFFER, gl.DYNAMIC_DRAW);
|
|
90
156
|
this.writeVertexAttrib(vertexBuffer, attr, 2, 0, 0);
|
|
91
157
|
vertexBuffer.attr = attr;
|
|
158
|
+
this.__cachedBuffers.push(vertexBuffer);
|
|
92
159
|
return vertexBuffer;
|
|
93
160
|
}
|
|
94
161
|
|
|
@@ -370,9 +437,10 @@ class WebglPath extends WebglBase {
|
|
|
370
437
|
|
|
371
438
|
// 分割成一个个规则的三角形,不规则的多边形不全割的话纹理就会没法正确覆盖
|
|
372
439
|
getTriangles(points) {
|
|
373
|
-
|
|
374
440
|
this.trianglesCache = this.trianglesCache||(this.trianglesCache={});
|
|
375
|
-
|
|
441
|
+
// 快速缓存 key:用长度和首尾点坐标(比 JSON.stringify 快几个数量级)
|
|
442
|
+
const len = points.length;
|
|
443
|
+
const key = len + '_' + points[0].x + '_' + points[0].y + '_' + points[len-1].x + '_' + points[len-1].y;
|
|
376
444
|
if(this.trianglesCache[key]) return this.trianglesCache[key];
|
|
377
445
|
|
|
378
446
|
const res = [];
|
|
@@ -411,10 +479,9 @@ class WebglPath extends WebglBase {
|
|
|
411
479
|
points = regular? points : this.pathToPoints(points);
|
|
412
480
|
const buffer = this.writePoints(points);
|
|
413
481
|
this.context.drawArrays(regular? this.context.LINE_LOOP: this.context.POINTS, 0, points.length);
|
|
414
|
-
|
|
482
|
+
// buffer 由 endDraw 统一清理
|
|
415
483
|
}
|
|
416
|
-
colorBuffer && this.
|
|
417
|
-
colorBuffer && this.disableVertexAttribArray(colorBuffer.attr);
|
|
484
|
+
colorBuffer && this.disableVertexAttribArray(colorBuffer && colorBuffer.attr);
|
|
418
485
|
}
|
|
419
486
|
|
|
420
487
|
// 填充图形
|
|
@@ -445,8 +512,7 @@ class WebglPath extends WebglBase {
|
|
|
445
512
|
|
|
446
513
|
this.fillPolygons(points);
|
|
447
514
|
|
|
448
|
-
colorBuffer && this.
|
|
449
|
-
colorBuffer && this.disableVertexAttribArray(colorBuffer.attr);
|
|
515
|
+
colorBuffer && this.disableVertexAttribArray(colorBuffer && colorBuffer.attr);
|
|
450
516
|
|
|
451
517
|
}
|
|
452
518
|
|
|
@@ -456,8 +522,24 @@ class WebglPath extends WebglBase {
|
|
|
456
522
|
fillImage(img, points, bounds) {
|
|
457
523
|
if(!img) return;
|
|
458
524
|
|
|
459
|
-
//
|
|
460
|
-
|
|
525
|
+
// 对于 ImageData,生成缓存 key(基于渐变参数或 bounds),复用纹理
|
|
526
|
+
let texture = null;
|
|
527
|
+
if(img instanceof ImageData) {
|
|
528
|
+
const key = `${img.width}_${img.height}_${bounds.width}_${bounds.height}_${bounds.left}_${bounds.top}`;
|
|
529
|
+
if(this.__cachedTexture && this.__cachedTextureKey === key) {
|
|
530
|
+
texture = this.__cachedTexture;
|
|
531
|
+
} else {
|
|
532
|
+
texture = this.createDataTexture(img);
|
|
533
|
+
// 释放旧纹理
|
|
534
|
+
if(this.__cachedTexture) {
|
|
535
|
+
this.deleteTexture(this.__cachedTexture);
|
|
536
|
+
}
|
|
537
|
+
this.__cachedTexture = texture;
|
|
538
|
+
this.__cachedTextureKey = key;
|
|
539
|
+
}
|
|
540
|
+
} else {
|
|
541
|
+
texture = this.createImgTexture(img);
|
|
542
|
+
}
|
|
461
543
|
this.context.uniform1i(this.program.uniforms.u_sample.location, 0); // 纹理单元传递给着色器
|
|
462
544
|
|
|
463
545
|
// 指定纹理区域尺寸
|
|
@@ -470,7 +552,10 @@ class WebglPath extends WebglBase {
|
|
|
470
552
|
|
|
471
553
|
this.fillTexture(points);
|
|
472
554
|
|
|
473
|
-
|
|
555
|
+
// 仅对非缓存纹理(非 ImageData)立即删除
|
|
556
|
+
if(!(img instanceof ImageData)) {
|
|
557
|
+
this.deleteTexture(texture);
|
|
558
|
+
}
|
|
474
559
|
}
|
|
475
560
|
|
|
476
561
|
fillTexture(points) {
|
|
@@ -486,23 +571,67 @@ class WebglPath extends WebglBase {
|
|
|
486
571
|
|
|
487
572
|
// 进行多边形填充
|
|
488
573
|
fillPolygons(points, isTexture = false) {
|
|
489
|
-
if(points.length
|
|
490
|
-
|
|
491
|
-
if(triangles.length) {
|
|
492
|
-
for(const triangle of triangles) {
|
|
493
|
-
this.fillPolygons(triangle, isTexture);// 这里就变成了规则的图形了
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
574
|
+
if(points.length <= 3) {
|
|
575
|
+
// 3个点以下的三角形直接画
|
|
498
576
|
const buffer = this.writePoints(points);
|
|
499
|
-
// 纹理坐标
|
|
500
577
|
const coordBuffer = isTexture? this.writePoints(points, this.program.attrs.a_text_coord): null;
|
|
578
|
+
this.context.drawArrays(this.context.TRIANGLE_FAN, 0, points.length);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
501
581
|
|
|
582
|
+
// 规则图形(凸多边形,如圆):直接用 TRIANGLE_FAN 一次性绘制,无需 earcut
|
|
583
|
+
if(this.isRegular) {
|
|
584
|
+
const buffer = this.writePoints(points);
|
|
585
|
+
const coordBuffer = isTexture? this.writePoints(points, this.program.attrs.a_text_coord): null;
|
|
502
586
|
this.context.drawArrays(this.context.TRIANGLE_FAN, 0, points.length);
|
|
503
|
-
|
|
504
|
-
coordBuffer && this.deleteBuffer(coordBuffer);
|
|
587
|
+
return;
|
|
505
588
|
}
|
|
589
|
+
|
|
590
|
+
// 不规则图形:需要 earcut 三角化后,合并为一个大的顶点缓冲区,单次 drawArrays
|
|
591
|
+
const triangles = this.needCut? this.earCutPointsToTriangles(points): this.getTriangles(points);
|
|
592
|
+
if(!triangles.length) return;
|
|
593
|
+
|
|
594
|
+
// 合并所有三角形的顶点到一个数组
|
|
595
|
+
const allVertices = [];
|
|
596
|
+
const allTexCoords = [];
|
|
597
|
+
for(const triangle of triangles) {
|
|
598
|
+
for(const p of triangle) {
|
|
599
|
+
allVertices.push(p.x, p.y);
|
|
600
|
+
if(isTexture) allTexCoords.push(p.x, p.y);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// 一次性上传所有数据并绘制
|
|
605
|
+
const vertexData = new Float32Array(allVertices);
|
|
606
|
+
const gl = this.context;
|
|
607
|
+
|
|
608
|
+
// 复用或创建 position buffer
|
|
609
|
+
let posBuffer = this.__cachedBuffers.find(b => b.attr === this.program.attrs.a_position);
|
|
610
|
+
if(!posBuffer) {
|
|
611
|
+
posBuffer = this.createFloat32Buffer(vertexData, gl.ARRAY_BUFFER, gl.DYNAMIC_DRAW);
|
|
612
|
+
posBuffer.attr = this.program.attrs.a_position;
|
|
613
|
+
this.__cachedBuffers.push(posBuffer);
|
|
614
|
+
} else {
|
|
615
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer.buffer);
|
|
616
|
+
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.DYNAMIC_DRAW);
|
|
617
|
+
}
|
|
618
|
+
this.writeVertexAttrib(posBuffer, this.program.attrs.a_position, 2, 0, 0);
|
|
619
|
+
|
|
620
|
+
if(isTexture && allTexCoords.length) {
|
|
621
|
+
const texData = new Float32Array(allTexCoords);
|
|
622
|
+
let texBuffer = this.__cachedBuffers.find(b => b.attr === this.program.attrs.a_text_coord);
|
|
623
|
+
if(!texBuffer) {
|
|
624
|
+
texBuffer = this.createFloat32Buffer(texData, gl.ARRAY_BUFFER, gl.DYNAMIC_DRAW);
|
|
625
|
+
texBuffer.attr = this.program.attrs.a_text_coord;
|
|
626
|
+
this.__cachedBuffers.push(texBuffer);
|
|
627
|
+
} else {
|
|
628
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, texBuffer.buffer);
|
|
629
|
+
gl.bufferData(gl.ARRAY_BUFFER, texData, gl.DYNAMIC_DRAW);
|
|
630
|
+
}
|
|
631
|
+
this.writeVertexAttrib(texBuffer, this.program.attrs.a_text_coord, 2, 0, 0);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
gl.drawArrays(gl.TRIANGLES, 0, allVertices.length / 2);
|
|
506
635
|
}
|
|
507
636
|
|
|
508
637
|
// 填充图形
|
package/src/shapes/jmArc.js
CHANGED
|
@@ -123,7 +123,6 @@ export default class jmArc extends jmPath {
|
|
|
123
123
|
if((mw == 0 && mh == 0) || start == end) return;
|
|
124
124
|
|
|
125
125
|
let anticlockwise = this.anticlockwise;
|
|
126
|
-
this.points = [];
|
|
127
126
|
let step = 1 / Math.max(mw, mh);
|
|
128
127
|
|
|
129
128
|
//如果是逆时针绘制,则角度为负数,并且结束角为2Math.PI-end
|
|
@@ -134,18 +133,33 @@ export default class jmArc extends jmPath {
|
|
|
134
133
|
}
|
|
135
134
|
if(start > end) step = -step;
|
|
136
135
|
|
|
137
|
-
|
|
136
|
+
// 预计算需要的点数量
|
|
137
|
+
let pointCount = Math.ceil(Math.abs(end - start) / Math.abs(step)) + 1;
|
|
138
|
+
if(this.isFan) pointCount++;
|
|
139
|
+
|
|
140
|
+
// 复用已有数组,避免每帧分配;大小变化时才重建
|
|
141
|
+
if(!this.points || this.points.length !== pointCount) {
|
|
142
|
+
this.points = new Array(pointCount);
|
|
143
|
+
for(let i = 0; i < pointCount; i++) {
|
|
144
|
+
this.points[i] = { x: 0, y: 0 };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let idx = 0;
|
|
149
|
+
if(this.isFan) {
|
|
150
|
+
this.points[idx].x = location.center.x;
|
|
151
|
+
this.points[idx].y = location.center.y;
|
|
152
|
+
idx++;
|
|
153
|
+
}
|
|
138
154
|
|
|
139
155
|
//椭圆方程x=a*cos(r) ,y=b*sin(r)
|
|
140
156
|
for(let r=start;;r += step) {
|
|
141
157
|
if(step > 0 && r > end) r = end;
|
|
142
158
|
else if(step < 0 && r < end) r = end;
|
|
143
159
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
};
|
|
148
|
-
this.points.push(p);
|
|
160
|
+
this.points[idx].x = Math.cos(r) * mw + cx;
|
|
161
|
+
this.points[idx].y = Math.sin(r) * mh + cy;
|
|
162
|
+
idx++;
|
|
149
163
|
|
|
150
164
|
if(r == end) break;
|
|
151
165
|
}
|
package/src/shapes/jmLabel.js
CHANGED
|
@@ -22,7 +22,7 @@ export default class jmLabel extends jmControl {
|
|
|
22
22
|
this.style.textAlign = this.style.textAlign || 'left';
|
|
23
23
|
//文字垂直对齐
|
|
24
24
|
this.style.textBaseline = this.style.textBaseline || 'middle';
|
|
25
|
-
this.text = params.text || '';
|
|
25
|
+
this.text = params.text || params.value || '';
|
|
26
26
|
|
|
27
27
|
this.center = params.center || null;
|
|
28
28
|
}
|
|
@@ -311,67 +311,6 @@ export default class jmLabel extends jmControl {
|
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
|
-
//如果有指定边框,则画出边框
|
|
315
|
-
if(this.style.border) {
|
|
316
|
-
//如果指定了边框样式
|
|
317
|
-
if(this.style.border.style) {
|
|
318
|
-
this.context.save && this.context.save();
|
|
319
|
-
this.setStyle(this.style.border.style);
|
|
320
|
-
}
|
|
321
|
-
if(this.mode === '2d') {
|
|
322
|
-
this.context.moveTo(this.points[0].x + bounds.left,this.points[0].y + bounds.top);
|
|
323
|
-
if(this.style.border.top) {
|
|
324
|
-
this.context.lineTo(this.points[1].x + bounds.left,this.points[1].y + bounds.top);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if(this.style.border.right) {
|
|
328
|
-
this.context.moveTo(this.points[1].x + bounds.left,this.points[1].y + bounds.top);
|
|
329
|
-
this.context.lineTo(this.points[2].x + bounds.left,this.points[2].y + bounds.top);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if(this.style.border.bottom) {
|
|
333
|
-
this.context.moveTo(this.points[2].x + bounds.left,this.points[2].y + bounds.top);
|
|
334
|
-
this.context.lineTo(this.points[3].x + bounds.left,this.points[3].y + bounds.top);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if(this.style.border.left) {
|
|
338
|
-
this.context.moveTo(this.points[3].x + bounds.left,this.points[3].y + bounds.top);
|
|
339
|
-
this.context.lineTo(this.points[0].x + bounds.left,this.points[0].y + bounds.top);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
const points = [];
|
|
344
|
-
if(this.style.border.top) {
|
|
345
|
-
points.push(this.points[0]);
|
|
346
|
-
points.push(this.points[1]);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if(this.style.border.right) {
|
|
350
|
-
points.push({
|
|
351
|
-
...this.points[1],
|
|
352
|
-
m: true
|
|
353
|
-
});
|
|
354
|
-
points.push(this.points[2]);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if(this.style.border.bottom) {
|
|
358
|
-
points.push({
|
|
359
|
-
...this.points[2],
|
|
360
|
-
m: true
|
|
361
|
-
});
|
|
362
|
-
points.push(this.points[3]);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if(this.style.border.left) {
|
|
366
|
-
points.push({
|
|
367
|
-
...this.points[3],
|
|
368
|
-
m: true
|
|
369
|
-
});
|
|
370
|
-
points.push(this.points[0]);
|
|
371
|
-
}
|
|
372
|
-
points.length && this.webglControl && this.webglControl.stroke(points);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
314
|
}
|
|
376
315
|
|
|
377
316
|
endDraw() {
|
package/src/shapes/jmRect.js
CHANGED
|
@@ -8,6 +8,7 @@ import {jmLine} from './jmLine.js';
|
|
|
8
8
|
* @class jmRect
|
|
9
9
|
* @extends jmPath
|
|
10
10
|
* @param {object} params 参数 position=矩形左上角顶点坐标,width=宽,height=高,radius=边角弧度
|
|
11
|
+
* radius支持数字(四角相同)或对象 { topLeft, topRight, bottomRight, bottomLeft }
|
|
11
12
|
*/
|
|
12
13
|
export default class jmRect extends jmPath {
|
|
13
14
|
|
|
@@ -17,12 +18,24 @@ export default class jmRect extends jmPath {
|
|
|
17
18
|
super(params, t);
|
|
18
19
|
|
|
19
20
|
this.style.close = true;
|
|
20
|
-
|
|
21
|
+
const r = params.radius || this.style.radius || this.style.borderRadius || 0;
|
|
22
|
+
if(typeof r === 'object' && r !== null) {
|
|
23
|
+
// 四角独立圆角
|
|
24
|
+
this.radius = {
|
|
25
|
+
topLeft: Number(r.topLeft) || 0,
|
|
26
|
+
topRight: Number(r.topRight) || 0,
|
|
27
|
+
bottomRight: Number(r.bottomRight) || 0,
|
|
28
|
+
bottomLeft: Number(r.bottomLeft) || 0
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
this.radius = r;
|
|
33
|
+
}
|
|
21
34
|
}
|
|
22
35
|
/**
|
|
23
|
-
*
|
|
36
|
+
* 圆角半径,支持数字或四角独立对象
|
|
24
37
|
* @property radius
|
|
25
|
-
* @type {number}
|
|
38
|
+
* @type {number|object}
|
|
26
39
|
*/
|
|
27
40
|
get radius() {
|
|
28
41
|
return this.property('radius');
|
|
@@ -30,6 +43,36 @@ export default class jmRect extends jmPath {
|
|
|
30
43
|
set radius(v) {
|
|
31
44
|
this.needUpdate = true;
|
|
32
45
|
return this.property('radius', v);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 获取规范化的圆角值(四角独立)
|
|
50
|
+
* @returns {object} { topLeft, topRight, bottomRight, bottomLeft }
|
|
51
|
+
*/
|
|
52
|
+
getNormalizedRadius() {
|
|
53
|
+
const r = this.radius;
|
|
54
|
+
if(typeof r === 'number') {
|
|
55
|
+
const v = Math.max(0, r);
|
|
56
|
+
return { topLeft: v, topRight: v, bottomRight: v, bottomLeft: v };
|
|
57
|
+
}
|
|
58
|
+
if(typeof r === 'object' && r !== null) {
|
|
59
|
+
return {
|
|
60
|
+
topLeft: Math.max(0, Number(r.topLeft) || 0),
|
|
61
|
+
topRight: Math.max(0, Number(r.topRight) || 0),
|
|
62
|
+
bottomRight: Math.max(0, Number(r.bottomRight) || 0),
|
|
63
|
+
bottomLeft: Math.max(0, Number(r.bottomLeft) || 0)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return { topLeft: 0, topRight: 0, bottomRight: 0, bottomLeft: 0 };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 检查是否有圆角
|
|
71
|
+
* @returns {boolean}
|
|
72
|
+
*/
|
|
73
|
+
hasRadius() {
|
|
74
|
+
const nr = this.getNormalizedRadius();
|
|
75
|
+
return nr.topLeft > 0 || nr.topRight > 0 || nr.bottomRight > 0 || nr.bottomLeft > 0;
|
|
33
76
|
}
|
|
34
77
|
|
|
35
78
|
/**
|
|
@@ -92,7 +135,7 @@ export default class jmRect extends jmPath {
|
|
|
92
135
|
|
|
93
136
|
/**
|
|
94
137
|
* 初始化图形点
|
|
95
|
-
*
|
|
138
|
+
* 支持四角独立圆角,借助圆弧对象计算描点
|
|
96
139
|
*
|
|
97
140
|
* @method initPoints
|
|
98
141
|
* @private
|
|
@@ -109,32 +152,67 @@ export default class jmRect extends jmPath {
|
|
|
109
152
|
this.dottedLine = this.graph.createShape(jmLine, {style: this.style});
|
|
110
153
|
}
|
|
111
154
|
|
|
112
|
-
|
|
113
|
-
|
|
155
|
+
const nr = this.getNormalizedRadius();
|
|
156
|
+
const hasRadius = this.hasRadius();
|
|
157
|
+
|
|
158
|
+
// 如果有圆角(支持四角独立),借助圆弧对象计算描点
|
|
159
|
+
if(hasRadius) {
|
|
114
160
|
let q = Math.PI / 2;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
161
|
+
|
|
162
|
+
// 限制圆角不超过短边的一半
|
|
163
|
+
const maxR = Math.min(location.width / 2, location.height / 2);
|
|
164
|
+
const rtl = Math.min(nr.topLeft, maxR);
|
|
165
|
+
const rtr = Math.min(nr.topRight, maxR);
|
|
166
|
+
const rbr = Math.min(nr.bottomRight, maxR);
|
|
167
|
+
const rbl = Math.min(nr.bottomLeft, maxR);
|
|
168
|
+
|
|
169
|
+
// 左上角圆弧
|
|
170
|
+
if(rtl > 0) {
|
|
171
|
+
let arc = this.graph.createShape(jmArc,{radius:rtl,anticlockwise:false});
|
|
172
|
+
arc.center = {x:location.left + rtl, y:location.top + rtl};
|
|
173
|
+
arc.startAngle = Math.PI;
|
|
174
|
+
arc.endAngle = Math.PI + q;
|
|
175
|
+
var ps1 = arc.initPoints();
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
var ps1 = [p1];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 右上角圆弧
|
|
182
|
+
if(rtr > 0) {
|
|
183
|
+
let arc = this.graph.createShape(jmArc,{radius:rtr,anticlockwise:false});
|
|
184
|
+
arc.center = {x:p2.x - rtr, y:p2.y + rtr};
|
|
185
|
+
arc.startAngle = Math.PI + q;
|
|
186
|
+
arc.endAngle = Math.PI * 2;
|
|
187
|
+
var ps2 = arc.initPoints();
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
var ps2 = [p2];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 右下角圆弧
|
|
194
|
+
if(rbr > 0) {
|
|
195
|
+
let arc = this.graph.createShape(jmArc,{radius:rbr,anticlockwise:false});
|
|
196
|
+
arc.center = {x:p3.x - rbr, y:p3.y - rbr};
|
|
197
|
+
arc.startAngle = 0;
|
|
198
|
+
arc.endAngle = q;
|
|
199
|
+
var ps3 = arc.initPoints();
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
var ps3 = [p3];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 左下角圆弧
|
|
206
|
+
if(rbl > 0) {
|
|
207
|
+
let arc = this.graph.createShape(jmArc,{radius:rbl,anticlockwise:false});
|
|
208
|
+
arc.center = {x:p4.x + rbl, y:p4.y - rbl};
|
|
209
|
+
arc.startAngle = q;
|
|
210
|
+
arc.endAngle = Math.PI;
|
|
211
|
+
var ps4 = arc.initPoints();
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
var ps4 = [p4];
|
|
215
|
+
}
|
|
138
216
|
this.points = ps1.concat(ps2,ps3,ps4);
|
|
139
217
|
}
|
|
140
218
|
else {
|