jmgraph 3.2.21 → 3.2.24
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 +1 -0
- package/dist/jmgraph.core.min.js +1 -1
- package/dist/jmgraph.core.min.js.map +1 -1
- package/dist/jmgraph.js +695 -311
- package/dist/jmgraph.min.js +1 -1
- package/index.js +5 -1
- package/package.json +1 -1
- package/src/core/jmControl.js +9 -1
- package/src/core/jmGradient.js +4 -2
- package/src/core/jmGraph.js +14 -10
- package/src/lib/webgl/base.js +109 -90
- package/src/lib/webgl/gradient.js +99 -142
- package/src/lib/webgl/index.js +9 -10
- package/src/lib/webgl/path.js +302 -26
- package/src/shapes/jmArc.js +21 -7
- package/src/shapes/jmHArc.js +2 -2
package/src/lib/webgl/index.js
CHANGED
|
@@ -105,23 +105,22 @@ class webgl {
|
|
|
105
105
|
// 由具体的绘制方法处理
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
//
|
|
108
|
+
// 测量文本宽度
|
|
109
109
|
measureText(text) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return ctx2.measureText(text);
|
|
110
|
+
if(this.base && this.base._measureCtx) {
|
|
111
|
+
return this.base._measureCtx.measureText(text);
|
|
112
|
+
}
|
|
113
|
+
return { width: 15 };
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
// 创建线性渐变
|
|
118
|
-
createLinearGradient(x1, y1, x2, y2) {
|
|
119
|
-
return this.base.createLinearGradient(x1, y1, x2, y2);
|
|
117
|
+
createLinearGradient(x1, y1, x2, y2, bounds) {
|
|
118
|
+
return this.base.createLinearGradient(x1, y1, x2, y2, bounds);
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
// 创建径向渐变
|
|
123
|
-
createRadialGradient(x1, y1, r1, x2, y2, r2) {
|
|
124
|
-
return this.base.createRadialGradient(x1, y1, r1, x2, y2, r2);
|
|
122
|
+
createRadialGradient(x1, y1, r1, x2, y2, r2, bounds) {
|
|
123
|
+
return this.base.createRadialGradient(x1, y1, r1, x2, y2, r2, bounds);
|
|
125
124
|
}
|
|
126
125
|
|
|
127
126
|
// 绘制图像
|
package/src/lib/webgl/path.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import WebglBase from './base.js';
|
|
1
|
+
import WebglBase, { MAX_STOPS } from './base.js';
|
|
2
|
+
import earcut from '../earcut.js';
|
|
2
3
|
|
|
3
4
|
// path 绘制类
|
|
4
5
|
class WebglPath extends WebglBase {
|
|
@@ -55,8 +56,14 @@ class WebglPath extends WebglBase {
|
|
|
55
56
|
//this.useProgram();
|
|
56
57
|
|
|
57
58
|
if(parentBounds) this.parentAbsoluteBounds = parentBounds;
|
|
58
|
-
//
|
|
59
|
-
|
|
59
|
+
// 缓存中心点值,只在变化时才更新 uniform
|
|
60
|
+
const cx = this.graph.width / 2;
|
|
61
|
+
const cy = this.graph.height / 2;
|
|
62
|
+
if(this.__lastCenterX !== cx || this.__lastCenterY !== cy) {
|
|
63
|
+
this.context.uniform2f(this.program.uniforms.a_center_point.location, cx, cy);
|
|
64
|
+
this.__lastCenterX = cx;
|
|
65
|
+
this.__lastCenterY = cy;
|
|
66
|
+
}
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
setFragColor(color) {
|
|
@@ -198,6 +205,112 @@ class WebglPath extends WebglBase {
|
|
|
198
205
|
equalPoint(p1, p2) {
|
|
199
206
|
return p1.x === p2.x && p1.y === p2.y;
|
|
200
207
|
}
|
|
208
|
+
|
|
209
|
+
// 将带 moveTo 标记的点集拆分为外轮廓和多个洞
|
|
210
|
+
splitSubPaths(points) {
|
|
211
|
+
const subPaths = [];
|
|
212
|
+
let current = [];
|
|
213
|
+
for(let i = 0; i < points.length; i++) {
|
|
214
|
+
const p = points[i];
|
|
215
|
+
if(p.m && current.length > 0) {
|
|
216
|
+
subPaths.push(current);
|
|
217
|
+
current = [];
|
|
218
|
+
}
|
|
219
|
+
current.push(p);
|
|
220
|
+
}
|
|
221
|
+
if(current.length > 0) subPaths.push(current);
|
|
222
|
+
|
|
223
|
+
// 面积最大的作为外轮廓,其余作为洞
|
|
224
|
+
let maxArea = -1;
|
|
225
|
+
let outerIdx = 0;
|
|
226
|
+
for(let i = 0; i < subPaths.length; i++) {
|
|
227
|
+
const area = Math.abs(this.polygonArea(subPaths[i]));
|
|
228
|
+
if(area > maxArea) {
|
|
229
|
+
maxArea = area;
|
|
230
|
+
outerIdx = i;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const outerPoints = subPaths[outerIdx];
|
|
235
|
+
const holes = [];
|
|
236
|
+
for(let i = 0; i < subPaths.length; i++) {
|
|
237
|
+
if(i !== outerIdx) holes.push(subPaths[i]);
|
|
238
|
+
}
|
|
239
|
+
return { outerPoints, holes };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 计算多边形面积(Shoelace 公式)
|
|
243
|
+
polygonArea(points) {
|
|
244
|
+
let area = 0;
|
|
245
|
+
const n = points.length;
|
|
246
|
+
for(let i = 0; i < n; i++) {
|
|
247
|
+
const j = (i + 1) % n;
|
|
248
|
+
area += points[i].x * points[j].y;
|
|
249
|
+
area -= points[j].x * points[i].y;
|
|
250
|
+
}
|
|
251
|
+
return area / 2;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 使用 earcut 带 holes 填充多边形
|
|
255
|
+
fillWithHoles(outerPoints, holes, isTexture = false) {
|
|
256
|
+
// 将所有点合并:外轮廓 + 各个洞,并记录洞的起始索引
|
|
257
|
+
const allPoints = [...outerPoints];
|
|
258
|
+
const holeIndices = [];
|
|
259
|
+
for(const hole of holes) {
|
|
260
|
+
holeIndices.push(allPoints.length);
|
|
261
|
+
allPoints.push(...hole);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const dim = 2;
|
|
265
|
+
const vertexData = [];
|
|
266
|
+
for(const p of allPoints) {
|
|
267
|
+
vertexData.push(p.x, p.y);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 用 earcut 进行带洞三角化
|
|
271
|
+
const indices = earcut(vertexData, holeIndices, dim);
|
|
272
|
+
|
|
273
|
+
if(!indices || indices.length < 3) return;
|
|
274
|
+
|
|
275
|
+
// 构建 GPU 顶点数据
|
|
276
|
+
const allVertices = [];
|
|
277
|
+
const allTexCoords = [];
|
|
278
|
+
for(let i = 0; i < indices.length; i++) {
|
|
279
|
+
const p = allPoints[indices[i]];
|
|
280
|
+
allVertices.push(p.x, p.y);
|
|
281
|
+
if(isTexture) allTexCoords.push(p.x, p.y);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const gl = this.context;
|
|
285
|
+
const vertexArr = new Float32Array(allVertices);
|
|
286
|
+
|
|
287
|
+
let posBuffer = this.__cachedBuffers.find(b => b.attr === this.program.attrs.a_position);
|
|
288
|
+
if(!posBuffer) {
|
|
289
|
+
posBuffer = this.createFloat32Buffer(vertexArr, gl.ARRAY_BUFFER, gl.DYNAMIC_DRAW);
|
|
290
|
+
posBuffer.attr = this.program.attrs.a_position;
|
|
291
|
+
this.__cachedBuffers.push(posBuffer);
|
|
292
|
+
} else {
|
|
293
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer.buffer);
|
|
294
|
+
gl.bufferData(gl.ARRAY_BUFFER, vertexArr, gl.DYNAMIC_DRAW);
|
|
295
|
+
}
|
|
296
|
+
this.writeVertexAttrib(posBuffer, this.program.attrs.a_position, 2, 0, 0);
|
|
297
|
+
|
|
298
|
+
if(isTexture && allTexCoords.length) {
|
|
299
|
+
const texData = new Float32Array(allTexCoords);
|
|
300
|
+
let texBuffer = this.__cachedBuffers.find(b => b.attr === this.program.attrs.a_text_coord);
|
|
301
|
+
if(!texBuffer) {
|
|
302
|
+
texBuffer = this.createFloat32Buffer(texData, gl.ARRAY_BUFFER, gl.DYNAMIC_DRAW);
|
|
303
|
+
texBuffer.attr = this.program.attrs.a_text_coord;
|
|
304
|
+
this.__cachedBuffers.push(texBuffer);
|
|
305
|
+
} else {
|
|
306
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, texBuffer.buffer);
|
|
307
|
+
gl.bufferData(gl.ARRAY_BUFFER, texData, gl.DYNAMIC_DRAW);
|
|
308
|
+
}
|
|
309
|
+
this.writeVertexAttrib(texBuffer, this.program.attrs.a_text_coord, 2, 0, 0);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
gl.drawArrays(gl.TRIANGLES, 0, allVertices.length / 2);
|
|
313
|
+
}
|
|
201
314
|
// 把path坐标集合转为线段集
|
|
202
315
|
pathToLines(points) {
|
|
203
316
|
let start = null;
|
|
@@ -470,9 +583,46 @@ class WebglPath extends WebglBase {
|
|
|
470
583
|
}
|
|
471
584
|
if(points && points.length) {
|
|
472
585
|
const regular = lineWidth <= 1.2;
|
|
473
|
-
|
|
474
|
-
const
|
|
475
|
-
|
|
586
|
+
const hasMoveTo = points.some && points.some(p => p.m);
|
|
587
|
+
const isRing = !hasMoveTo && this.needCut; // 空心形状(jmHArc close=true 时无 m 标记)
|
|
588
|
+
if(regular && (hasMoveTo || isRing)) {
|
|
589
|
+
// 有 moveTo 标记或空心形状时,分段绘制每个子路径的 LINE_LOOP
|
|
590
|
+
// 避免 LINE_LOOP 把不同子路径的点连起来产生拉扯线
|
|
591
|
+
if(hasMoveTo) {
|
|
592
|
+
let subPath = [];
|
|
593
|
+
for(let i = 0; i < points.length; i++) {
|
|
594
|
+
if(points[i].m && subPath.length > 0) {
|
|
595
|
+
const buffer = this.writePoints(subPath);
|
|
596
|
+
this.context.drawArrays(this.context.LINE_LOOP, 0, subPath.length);
|
|
597
|
+
subPath = [];
|
|
598
|
+
}
|
|
599
|
+
subPath.push(points[i]);
|
|
600
|
+
}
|
|
601
|
+
if(subPath.length > 1) {
|
|
602
|
+
const buffer = this.writePoints(subPath);
|
|
603
|
+
this.context.drawArrays(this.context.LINE_LOOP, 0, subPath.length);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
else if(isRing) {
|
|
607
|
+
// 空心形状:前半段为内弧,后半段为外弧(反向),各自 LINE_LOOP
|
|
608
|
+
const mid = Math.floor(points.length / 2);
|
|
609
|
+
const inner = points.slice(0, mid);
|
|
610
|
+
const outer = points.slice(mid);
|
|
611
|
+
if(inner.length > 1) {
|
|
612
|
+
this.writePoints(inner);
|
|
613
|
+
this.context.drawArrays(this.context.LINE_LOOP, 0, inner.length);
|
|
614
|
+
}
|
|
615
|
+
if(outer.length > 1) {
|
|
616
|
+
this.writePoints(outer);
|
|
617
|
+
this.context.drawArrays(this.context.LINE_LOOP, 0, outer.length);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
points = regular? points : this.pathToPoints(points);
|
|
623
|
+
const buffer = this.writePoints(points);
|
|
624
|
+
this.context.drawArrays(regular? this.context.LINE_LOOP: this.context.POINTS, 0, points.length);
|
|
625
|
+
}
|
|
476
626
|
// buffer 由 endDraw 统一清理
|
|
477
627
|
}
|
|
478
628
|
colorBuffer && this.disableVertexAttribArray(colorBuffer && colorBuffer.attr);
|
|
@@ -494,10 +644,9 @@ class WebglPath extends WebglBase {
|
|
|
494
644
|
|
|
495
645
|
fillColor(color, points, bounds, type=1) {
|
|
496
646
|
|
|
497
|
-
//
|
|
647
|
+
// 如果是渐变色,使用 GLSL 着色器直接计算
|
|
498
648
|
if(this.isGradient(color)) {
|
|
499
|
-
|
|
500
|
-
return this.fillImage(imgData.data, imgData.points, bounds);
|
|
649
|
+
return this.fillGradient(color, points, bounds);
|
|
501
650
|
}
|
|
502
651
|
|
|
503
652
|
// 标注为fill
|
|
@@ -510,6 +659,83 @@ class WebglPath extends WebglBase {
|
|
|
510
659
|
|
|
511
660
|
}
|
|
512
661
|
|
|
662
|
+
/**
|
|
663
|
+
* 使用 GLSL 着色器渲染渐变填充
|
|
664
|
+
* 无需 textureCanvas,直接通过 uniform 传递渐变参数给 GPU
|
|
665
|
+
*/
|
|
666
|
+
fillGradient(gradient, points, bounds) {
|
|
667
|
+
const params = gradient.toUniformParams();
|
|
668
|
+
if(!params) return;
|
|
669
|
+
|
|
670
|
+
// 标注为 GLSL 渐变 (type=5)
|
|
671
|
+
this.context.uniform1i(this.program.uniforms.a_type.location, 5);
|
|
672
|
+
|
|
673
|
+
// 设置 globalAlpha(通过 v_single_color.a 传递给着色器)
|
|
674
|
+
this.context.uniform4f(this.program.uniforms.v_single_color.location, 1.0, 1.0, 1.0, this.style.globalAlpha);
|
|
675
|
+
|
|
676
|
+
// 设置渐变类型
|
|
677
|
+
if(this.program.uniforms.u_gradient_type) {
|
|
678
|
+
this.context.uniform1i(this.program.uniforms.u_gradient_type.location, params.gradientType);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// 设置渐变起点/终点
|
|
682
|
+
if(this.program.uniforms.u_gradient_start) {
|
|
683
|
+
this.context.uniform4fv(this.program.uniforms.u_gradient_start.location, params.gradientStart);
|
|
684
|
+
}
|
|
685
|
+
if(this.program.uniforms.u_gradient_end) {
|
|
686
|
+
this.context.uniform4fv(this.program.uniforms.u_gradient_end.location, params.gradientEnd);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// 设置颜色断点数量
|
|
690
|
+
if(this.program.uniforms.u_gradient_stop_count) {
|
|
691
|
+
this.context.uniform1i(this.program.uniforms.u_gradient_stop_count.location, params.stopCount);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// 设置每个 stop 的 offset
|
|
695
|
+
// 关键:必须填充完整的 MAX_STOPS 长度数组,否则未初始化元素默认为 0
|
|
696
|
+
// 会导致着色器循环中 t >= 0 始终为 true,返回黑色
|
|
697
|
+
if(this.program.uniforms.u_gradient_offsets) {
|
|
698
|
+
const offsets = new Float32Array(MAX_STOPS);
|
|
699
|
+
for(let i = 0; i < params.stopCount; i++) {
|
|
700
|
+
offsets[i] = params.stops[i * 5];
|
|
701
|
+
}
|
|
702
|
+
// 用 2.0 填充剩余项,使 t(0~1) >= 2.0 为 false,不会被匹配
|
|
703
|
+
for(let i = params.stopCount; i < MAX_STOPS; i++) {
|
|
704
|
+
offsets[i] = 2.0;
|
|
705
|
+
}
|
|
706
|
+
this.context.uniform1fv(this.program.uniforms.u_gradient_offsets.location, offsets);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// 设置每个 stop 的颜色 (rgba)
|
|
710
|
+
if(this.program.uniforms.u_gradient_colors) {
|
|
711
|
+
const colors = new Float32Array(MAX_STOPS * 4);
|
|
712
|
+
for(let i = 0; i < params.stopCount; i++) {
|
|
713
|
+
colors[i * 4 + 0] = params.stops[i * 5 + 1]; // r
|
|
714
|
+
colors[i * 4 + 1] = params.stops[i * 5 + 2]; // g
|
|
715
|
+
colors[i * 4 + 2] = params.stops[i * 5 + 3]; // b
|
|
716
|
+
colors[i * 4 + 3] = params.stops[i * 5 + 4]; // a
|
|
717
|
+
}
|
|
718
|
+
// 用最后一个 stop 的颜色填充剩余项,确保不会返回黑色
|
|
719
|
+
if(params.stopCount > 0) {
|
|
720
|
+
const lastR = params.stops[(params.stopCount - 1) * 5 + 1];
|
|
721
|
+
const lastG = params.stops[(params.stopCount - 1) * 5 + 2];
|
|
722
|
+
const lastB = params.stops[(params.stopCount - 1) * 5 + 3];
|
|
723
|
+
const lastA = params.stops[(params.stopCount - 1) * 5 + 4];
|
|
724
|
+
for(let i = params.stopCount; i < MAX_STOPS; i++) {
|
|
725
|
+
colors[i * 4 + 0] = lastR;
|
|
726
|
+
colors[i * 4 + 1] = lastG;
|
|
727
|
+
colors[i * 4 + 2] = lastB;
|
|
728
|
+
colors[i * 4 + 3] = lastA;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
this.context.uniform4fv(this.program.uniforms.u_gradient_colors.location, colors);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// 填充多边形(需要纹理坐标来计算渐变位置)
|
|
735
|
+
this.fillPolygons(points, true);
|
|
736
|
+
this.disableVertexAttribArray(this.program.attrs.a_text_coord);
|
|
737
|
+
}
|
|
738
|
+
|
|
513
739
|
// 区域填充图片
|
|
514
740
|
// points绘制的图形顶点
|
|
515
741
|
// 图片整体绘制区域
|
|
@@ -575,6 +801,29 @@ class WebglPath extends WebglBase {
|
|
|
575
801
|
|
|
576
802
|
// 规则图形(凸多边形,如圆):直接用 TRIANGLE_FAN 一次性绘制,无需 earcut
|
|
577
803
|
if(this.isRegular) {
|
|
804
|
+
// 检查是否有 moveTo 标记,如果有说明路径包含多个子路径(如空心圆弧 jmHArc)
|
|
805
|
+
const hasMoveTo = points.some && points.some(p => p.m);
|
|
806
|
+
if(hasMoveTo) {
|
|
807
|
+
// 有 m 标记:按 m 标记拆分子路径
|
|
808
|
+
const { outerPoints, holes } = this.splitSubPaths(points);
|
|
809
|
+
this.fillWithHoles(outerPoints, holes, isTexture);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
// 无 m 标记但 needCut=true 表示空心形状(如 jmHArc close=true)
|
|
813
|
+
// 前半段为内弧,后半段为外弧(反向),按中点拆分
|
|
814
|
+
if(this.needCut && points.length >= 6) {
|
|
815
|
+
const mid = Math.floor(points.length / 2);
|
|
816
|
+
const inner = points.slice(0, mid);
|
|
817
|
+
const outer = points.slice(mid);
|
|
818
|
+
const innerArea = Math.abs(this.polygonArea(inner));
|
|
819
|
+
const outerArea = Math.abs(this.polygonArea(outer));
|
|
820
|
+
if(outerArea >= innerArea) {
|
|
821
|
+
this.fillWithHoles(outer, [inner], isTexture);
|
|
822
|
+
} else {
|
|
823
|
+
this.fillWithHoles(inner, [outer], isTexture);
|
|
824
|
+
}
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
578
827
|
const buffer = this.writePoints(points);
|
|
579
828
|
const coordBuffer = isTexture? this.writePoints(points, this.program.attrs.a_text_coord): null;
|
|
580
829
|
this.context.drawArrays(this.context.TRIANGLE_FAN, 0, points.length);
|
|
@@ -642,46 +891,73 @@ class WebglPath extends WebglBase {
|
|
|
642
891
|
}
|
|
643
892
|
|
|
644
893
|
drawText(text, x, y, bounds) {
|
|
645
|
-
|
|
894
|
+
// 文本渲染仍需要 2D canvas 绘制字形,然后作为纹理上传
|
|
895
|
+
// 使用临时 canvas,不依赖共享的 textureCanvas
|
|
896
|
+
if(!bounds.width || !bounds.height) return null;
|
|
897
|
+
if(typeof document === 'undefined') return null;
|
|
898
|
+
|
|
899
|
+
let canvas = this.__textCanvas;
|
|
646
900
|
if(!canvas) {
|
|
647
|
-
|
|
901
|
+
canvas = document.createElement('canvas');
|
|
902
|
+
this.__textCanvas = canvas;
|
|
648
903
|
}
|
|
649
904
|
canvas.width = bounds.width;
|
|
650
905
|
canvas.height = bounds.height;
|
|
651
906
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
}
|
|
907
|
+
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
|
908
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
655
909
|
|
|
656
|
-
this.textureContext.clearRect(0, 0, canvas.width, canvas.height);
|
|
657
910
|
// 修改字体
|
|
658
|
-
|
|
911
|
+
ctx.font = this.style.font || (this.style.fontSize + 'px ' + this.style.fontFamily);
|
|
659
912
|
|
|
660
913
|
x -= bounds.left;
|
|
661
914
|
y -= bounds.top;
|
|
662
915
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
916
|
+
// 设置文本样式
|
|
917
|
+
if(this.style.fillStyle) {
|
|
918
|
+
ctx.fillStyle = this.graph.utils.toColor(this.style.fillStyle);
|
|
919
|
+
}
|
|
920
|
+
if(this.style.strokeStyle) {
|
|
921
|
+
ctx.strokeStyle = this.graph.utils.toColor(this.style.strokeStyle);
|
|
922
|
+
}
|
|
923
|
+
if(this.style.shadowColor) {
|
|
924
|
+
ctx.shadowColor = this.graph.utils.toColor(this.style.shadowColor);
|
|
925
|
+
}
|
|
926
|
+
if(this.style.shadowBlur) {
|
|
927
|
+
ctx.shadowBlur = this.style.shadowBlur;
|
|
928
|
+
}
|
|
929
|
+
if(this.style.shadowOffsetX !== undefined) {
|
|
930
|
+
ctx.shadowOffsetX = this.style.shadowOffsetX;
|
|
931
|
+
}
|
|
932
|
+
if(this.style.shadowOffsetY !== undefined) {
|
|
933
|
+
ctx.shadowOffsetY = this.style.shadowOffsetY;
|
|
934
|
+
}
|
|
935
|
+
if(this.style.textAlign) {
|
|
936
|
+
ctx.textAlign = this.style.textAlign;
|
|
937
|
+
}
|
|
938
|
+
if(this.style.textBaseline) {
|
|
939
|
+
ctx.textBaseline = this.style.textBaseline;
|
|
940
|
+
}
|
|
666
941
|
|
|
942
|
+
if(this.style.fillStyle && ctx.fillText) {
|
|
667
943
|
if(this.style.maxWidth) {
|
|
668
|
-
|
|
944
|
+
ctx.fillText(text, x, y, this.style.maxWidth);
|
|
669
945
|
}
|
|
670
946
|
else {
|
|
671
|
-
|
|
947
|
+
ctx.fillText(text, x, y);
|
|
672
948
|
}
|
|
673
949
|
}
|
|
674
|
-
if(this.
|
|
675
|
-
|
|
950
|
+
if(this.style.strokeStyle && ctx.strokeText) {
|
|
676
951
|
if(this.style.maxWidth) {
|
|
677
|
-
|
|
952
|
+
ctx.strokeText(text, x, y, this.style.maxWidth);
|
|
678
953
|
}
|
|
679
954
|
else {
|
|
680
|
-
|
|
955
|
+
ctx.strokeText(text, x, y);
|
|
681
956
|
}
|
|
682
957
|
}
|
|
958
|
+
|
|
683
959
|
// 用纹理图片代替文字
|
|
684
|
-
const data =
|
|
960
|
+
const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
685
961
|
this.fillImage(data, this.points, bounds);
|
|
686
962
|
}
|
|
687
963
|
}
|
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/jmHArc.js
CHANGED
|
@@ -103,8 +103,8 @@ export default class jmHArc extends jmArc {
|
|
|
103
103
|
|
|
104
104
|
maxps.reverse();//大圆逆序
|
|
105
105
|
if(!this.style || !this.style.close) {
|
|
106
|
-
maxps[0].m = true
|
|
107
|
-
}
|
|
106
|
+
maxps[0].m = true;//非闭合时标记 moveTo,分隔内外两个子路径
|
|
107
|
+
}
|
|
108
108
|
this.points = minps.concat(maxps);
|
|
109
109
|
}
|
|
110
110
|
}
|