jmgraph 3.2.19 → 3.2.21

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.
Files changed (63) hide show
  1. package/README.md +311 -6
  2. package/dist/jmgraph.core.min.js +1 -1
  3. package/dist/jmgraph.core.min.js.map +1 -1
  4. package/dist/jmgraph.js +2022 -368
  5. package/dist/jmgraph.min.js +1 -1
  6. package/index.js +23 -25
  7. package/package.json +1 -1
  8. package/src/core/jmControl.js +199 -30
  9. package/src/core/jmFilter.js +150 -0
  10. package/src/core/jmGraph.js +207 -7
  11. package/src/core/jmLayer.js +142 -0
  12. package/src/core/jmPath.js +55 -0
  13. package/src/core/jmUtils.js +46 -37
  14. package/src/lib/webgl/base.js +10 -36
  15. package/src/lib/webgl/gradient.js +16 -3
  16. package/src/lib/webgl/index.js +5 -4
  17. package/src/lib/webgl/path.js +156 -33
  18. package/src/shapes/jmEllipse.js +91 -0
  19. package/src/shapes/jmLabel.js +126 -75
  20. package/src/shapes/jmPolygon.js +129 -0
  21. package/src/shapes/jmRect.js +107 -29
  22. package/src/shapes/jmStar.js +160 -0
  23. package/example/ball.html +0 -217
  24. package/example/base.html +0 -112
  25. package/example/canvas.html +0 -54
  26. package/example/cell.html +0 -284
  27. package/example/controls/arc.html +0 -129
  28. package/example/controls/arrowline.html +0 -78
  29. package/example/controls/bezier.html +0 -299
  30. package/example/controls/img.html +0 -97
  31. package/example/controls/label.html +0 -87
  32. package/example/controls/line.html +0 -173
  33. package/example/controls/prismatic.html +0 -63
  34. package/example/controls/rect.html +0 -64
  35. package/example/controls/resize.html +0 -112
  36. package/example/controls/test.html +0 -360
  37. package/example/es.html +0 -70
  38. package/example/es5module.html +0 -63
  39. package/example/heartarc.html +0 -116
  40. package/example/index.html +0 -47
  41. package/example/js/require.js +0 -5
  42. package/example/love/img/bling/bling.tps +0 -265
  43. package/example/love/img/bling.json +0 -87
  44. package/example/love/img/bling.tps +0 -295
  45. package/example/love/img/doc/bling.gif +0 -0
  46. package/example/love/img/love.json +0 -95
  47. package/example/love/img/love.tps +0 -315
  48. package/example/love/img/qq/qq.tps +0 -399
  49. package/example/love/img/qq.json +0 -242
  50. package/example/love/index.html +0 -40
  51. package/example/love/js/game.js +0 -558
  52. package/example/music.html +0 -211
  53. package/example/node/test.js +0 -138
  54. package/example/pdf.html +0 -187
  55. package/example/progress.html +0 -173
  56. package/example/pso.html +0 -148
  57. package/example/sort.html +0 -805
  58. package/example/tweenjs.html +0 -84
  59. package/example/webgl.html +0 -278
  60. package/example/xfj/img/dr_die.gif +0 -0
  61. package/example/xfj/index.html +0 -332
  62. package/example/xfj/shake.js +0 -49
  63. package/example/xfj/testori.html +0 -76
@@ -3,6 +3,7 @@ import {jmList} from "./jmList.js";
3
3
  import {jmProperty} from './jmProperty.js';
4
4
  import {jmShadow} from "./jmShadow.js";
5
5
  import {jmGradient} from "./jmGradient.js";
6
+ import {jmFilter} from "./jmFilter.js";
6
7
  import {jmEvents} from "./jmEvents.js";
7
8
  import {jmControl} from "./jmControl.js";
8
9
  import {jmPath} from "./jmPath.js";
@@ -46,6 +47,10 @@ export default class jmGraph extends jmControl {
46
47
  // 模式 webgl | 2d
47
48
  this.mode = option.mode || '2d';
48
49
 
50
+ // 缩放和平移相关
51
+ this.scaleFactor = 1;
52
+ this.translation = {x: 0, y: 0};
53
+
49
54
  //如果是小程序
50
55
  if(typeof wx != 'undefined' && wx.canIUse && wx.canIUse('canvas')) {
51
56
  if(typeof canvas === 'string') canvas = wx.createSelectorQuery().select('#' + canvas);
@@ -119,14 +124,24 @@ export default class jmGraph extends jmControl {
119
124
  * 画控件前初始化
120
125
  * 为了解决一像素线条问题
121
126
  */
122
- this.on('beginDraw', function() {
127
+ this.on('beginDraw', function() {
123
128
  this.context.translate && this.context.translate(0.5, 0.5);
129
+ // 应用缩放和平移变换
130
+ if(this.context.translate && this.context.scale) {
131
+ this.context.translate(this.translation.x, this.translation.y);
132
+ this.context.scale(this.scaleFactor, this.scaleFactor);
133
+ }
124
134
  });
125
135
  /**
126
136
  * 结束控件绘制 为了解决一像素线条问题
127
137
  */
128
- this.on('endDraw', function() {
129
- this.context.translate && this.context.translate(-0.5, -0.5);
138
+ this.on('endDraw', function() {
139
+ this.context.translate && this.context.translate(-0.5, -0.5);
140
+ // 恢复缩放和平移变换
141
+ if(this.context.translate && this.context.scale) {
142
+ this.context.scale(1/this.scaleFactor, 1/this.scaleFactor);
143
+ this.context.translate(-this.translation.x, -this.translation.y);
144
+ }
130
145
  });
131
146
 
132
147
  // devicePixelRatio初始化
@@ -271,7 +286,7 @@ export default class jmGraph extends jmControl {
271
286
  if(shape) {
272
287
  if(!args) args = {};
273
288
  args.graph = this;
274
- let obj = new shape(args);
289
+ const obj = new shape(args);
275
290
  return obj;
276
291
  }
277
292
  }
@@ -501,7 +516,7 @@ export default class jmGraph extends jmControl {
501
516
  this.normalSize = {
502
517
  width: this.canvas.width,
503
518
  height: this.canvas.height
504
- };
519
+ };
505
520
  }
506
521
 
507
522
  //this.context.scale && this.context.scale(dx,dy);
@@ -514,6 +529,86 @@ export default class jmGraph extends jmControl {
514
529
  this.canvas.style && (this.canvas.style.transform = `scale(${this.scaleSize.x}, ${this.scaleSize.y})`);
515
530
  }
516
531
 
532
+ /**
533
+ * 设置缩放因子
534
+ * 支持以指定点为中心进行缩放,保持该点在屏幕上的位置不变
535
+ *
536
+ * @method setZoom
537
+ * @param {number} zoom 缩放因子(建议范围:0.1 - 10)
538
+ * @param {number} [x] 缩放中心X坐标(画布坐标)
539
+ * @param {number} [y] 缩放中心Y坐标(画布坐标)
540
+ * @return {jmGraph} 返回当前实例,支持链式调用
541
+ */
542
+ setZoom(zoom, x, y) {
543
+ // 参数验证
544
+ if(typeof zoom !== 'number' || isNaN(zoom)) {
545
+ console.warn('jmGraph: setZoom - 无效的缩放因子');
546
+ return this;
547
+ }
548
+
549
+ // 限制缩放范围,防止过度缩放导致性能问题或显示异常
550
+ const minZoom = 0.1; // 最小缩放到10%
551
+ const maxZoom = 10; // 最大放大到10倍
552
+ zoom = Math.max(minZoom, Math.min(maxZoom, zoom));
553
+
554
+ if (x !== undefined && y !== undefined) {
555
+ // 计算缩放前后的坐标偏移
556
+ // 保持缩放中心点在屏幕上的位置不变
557
+ const oldZoom = this.scaleFactor;
558
+ const newZoom = zoom;
559
+
560
+ // 调整平移量以保持缩放中心位置不变
561
+ this.translation.x = x - (x - this.translation.x) * (newZoom / oldZoom);
562
+ this.translation.y = y - (y - this.translation.y) * (newZoom / oldZoom);
563
+ }
564
+
565
+ this.scaleFactor = zoom;
566
+ this.needUpdate = true;
567
+ this.redraw();
568
+
569
+ return this; // 支持链式调用
570
+ }
571
+
572
+ /**
573
+ * 平移画布
574
+ * 移动画布视图,改变可视区域
575
+ *
576
+ * @method pan
577
+ * @param {number} dx X轴平移量(像素)
578
+ * @param {number} dy Y轴平移量(像素)
579
+ * @return {jmGraph} 返回当前实例,支持链式调用
580
+ */
581
+ pan(dx, dy) {
582
+ // 参数验证
583
+ if(typeof dx !== 'number' || typeof dy !== 'number' || isNaN(dx) || isNaN(dy)) {
584
+ console.warn('jmGraph: pan - 无效的平移参数');
585
+ return this;
586
+ }
587
+
588
+ this.translation.x += dx;
589
+ this.translation.y += dy;
590
+ this.needUpdate = true;
591
+ this.redraw();
592
+
593
+ return this; // 支持链式调用
594
+ }
595
+
596
+ /**
597
+ * 重置缩放和平移
598
+ * 恢复画布到初始状态(缩放为1,平移为0)
599
+ *
600
+ * @method resetTransform
601
+ * @return {jmGraph} 返回当前实例,支持链式调用
602
+ */
603
+ resetTransform() {
604
+ this.scaleFactor = 1;
605
+ this.translation = {x: 0, y: 0};
606
+ this.needUpdate = true;
607
+ this.redraw();
608
+
609
+ return this; // 支持链式调用
610
+ }
611
+
517
612
  /**
518
613
  * 保存为base64图形数据
519
614
  *
@@ -525,6 +620,110 @@ export default class jmGraph extends jmControl {
525
620
  return data;
526
621
  }
527
622
 
623
+ /**
624
+ * 导出为PNG图片
625
+ * 使用Canvas的toDataURL方法导出当前画布内容
626
+ *
627
+ * @method exportToPNG
628
+ * @param {string} [fileName='jmgraph-export'] 文件名(不含扩展名)
629
+ * @param {string} [format='image/png'] 图片格式,支持image/png和image/jpeg
630
+ * @param {number} [quality=0.9] 图片质量(0-1之间,仅对JPEG格式有效)
631
+ */
632
+ exportToPNG(fileName = 'jmgraph-export', format = 'image/png', quality = 0.9) {
633
+ try {
634
+ // 确保画布已渲染
635
+ this.redraw();
636
+
637
+ const dataURL = this.canvas.toDataURL(format, quality);
638
+ this.downloadFile(dataURL, fileName, 'png');
639
+ } catch(error) {
640
+ console.error('jmGraph: exportToPNG - 导出失败', error);
641
+ }
642
+ }
643
+
644
+ /**
645
+ * 导出为JPEG图片
646
+ *
647
+ * @method exportToJPEG
648
+ * @param {string} [fileName='jmgraph-export'] 文件名(不含扩展名)
649
+ * @param {number} [quality=0.9] 图片质量(0-1之间)
650
+ */
651
+ exportToJPEG(fileName = 'jmgraph-export', quality = 0.9) {
652
+ this.exportToPNG(fileName, 'image/jpeg', quality);
653
+ }
654
+
655
+ /**
656
+ * 导出为SVG文件
657
+ * 将当前画布内容转换为SVG格式
658
+ * 注意:只有实现了toSVG方法的形状才能被导出
659
+ *
660
+ * @method exportToSVG
661
+ * @param {string} [fileName='jmgraph-export'] 文件名(不含扩展名)
662
+ */
663
+ exportToSVG(fileName = 'jmgraph-export') {
664
+ try {
665
+ const svg = this.toSVG();
666
+ const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
667
+ const url = URL.createObjectURL(blob);
668
+ this.downloadFile(url, fileName, 'svg');
669
+
670
+ // 释放URL对象,避免内存泄漏
671
+ setTimeout(() => URL.revokeObjectURL(url), 100);
672
+ } catch(error) {
673
+ console.error('jmGraph: exportToSVG - 导出失败', error);
674
+ }
675
+ }
676
+
677
+ /**
678
+ * 遍历所有形状,生成SVG标记
679
+ *
680
+ * @method toSVG
681
+ * @return {string} SVG字符串
682
+ */
683
+ toSVG() {
684
+ // SVG头部,包含命名空间和画布尺寸
685
+ let svg = `<svg width="${this.width}" height="${this.height}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${this.width} ${this.height}">`;
686
+
687
+ // 添加背景色(如果有)
688
+ if(this.style && this.style.fill) {
689
+ svg += `<rect width="100%" height="100%" fill="${this.style.fill}"/>`;
690
+ }
691
+
692
+ // 遍历所有直接添加的形状
693
+ this.children.each((i, shape) => {
694
+ if(shape.toSVG) {
695
+ svg += shape.toSVG();
696
+ }
697
+ });
698
+
699
+ svg += '</svg>';
700
+ return svg;
701
+ }
702
+
703
+ /**
704
+ * 下载文件
705
+ * 创建临时链接元素触发浏览器下载
706
+ *
707
+ * @method downloadFile
708
+ * @private
709
+ * @param {string} url 文件URL或Data URL
710
+ * @param {string} fileName 文件名(不含扩展名)
711
+ * @param {string} extension 文件扩展名
712
+ */
713
+ downloadFile(url, fileName, extension) {
714
+ // 创建临时链接元素
715
+ const link = document.createElement('a');
716
+ link.href = url;
717
+ link.download = `${fileName}.${extension}`;
718
+
719
+ // 添加到DOM并触发点击
720
+ document.body.appendChild(link);
721
+ link.click();
722
+
723
+ // 清理DOM
724
+ document.body.removeChild(link);
725
+ }
726
+
528
727
  /**
529
728
  * 自动刷新画版
530
729
  * @param {function} callback 执行回调
@@ -562,13 +761,14 @@ export default class jmGraph extends jmControl {
562
761
  }
563
762
  }
564
763
 
565
- export {
566
- jmGraph,
764
+ export {
765
+ jmGraph,
567
766
  jmUtils,
568
767
  jmList,
569
768
  jmProperty,
570
769
  jmShadow,
571
770
  jmGradient,
771
+ jmFilter,
572
772
  jmEvents,
573
773
  jmControl,
574
774
  jmPath,
@@ -0,0 +1,142 @@
1
+ import {jmControl} from "./jmControl.js";
2
+
3
+ /**
4
+ * 图层类
5
+ * 用于组织和管理图形对象,支持可见性和锁定控制
6
+ * 图层可以包含多个图形对象,并控制它们的显示和交互
7
+ *
8
+ * @class jmLayer
9
+ * @extends jmControl
10
+ * @param {object} params 图层参数
11
+ * @param {string} [params.name] 图层名称,默认为 'Layer_${timestamp}'
12
+ * @param {boolean} [params.visible=true] 图层是否可见
13
+ * @param {boolean} [params.locked=false] 图层是否锁定(锁定后不可交互)
14
+ * @param {jmGraph} [params.graph] 所属的画布对象
15
+ */
16
+ export default class jmLayer extends jmControl {
17
+
18
+ constructor(params, t='jmLayer') {
19
+ params = params || {};
20
+ params.interactive = false; // 图层本身不响应交互事件
21
+ super(params, t);
22
+
23
+ this.name = params.name || `Layer_${Date.now()}`;
24
+ this.visible = params.visible !== false;
25
+ this.locked = params.locked || false;
26
+ }
27
+
28
+ /**
29
+ * 图层名称
30
+ * 图层的唯一标识符,用于查找和管理图层
31
+ *
32
+ * @property name
33
+ * @type {string}
34
+ */
35
+ get name() {
36
+ return this.property('name');
37
+ }
38
+ set name(v) {
39
+ if(!v || typeof v !== 'string') {
40
+ console.warn('jmLayer: name must be a non-empty string');
41
+ return;
42
+ }
43
+ return this.property('name', v);
44
+ }
45
+
46
+ /**
47
+ * 图层是否可见
48
+ * 不可见的图层不会被渲染,但仍然存在于图层列表中
49
+ *
50
+ * @property visible
51
+ * @type {boolean}
52
+ */
53
+ get visible() {
54
+ return this.property('visible');
55
+ }
56
+ set visible(v) {
57
+ this.needUpdate = true;
58
+ return this.property('visible', v);
59
+ }
60
+
61
+ /**
62
+ * 图层是否锁定
63
+ * 锁定的图层中的图形不可被选中或移动,但仍然可见
64
+ * 适用于背景图层或参考图层
65
+ *
66
+ * @property locked
67
+ * @type {boolean}
68
+ */
69
+ get locked() {
70
+ return this.property('locked');
71
+ }
72
+ set locked(v) {
73
+ return this.property('locked', v);
74
+ }
75
+
76
+ /**
77
+ * 绘制图层
78
+ * 只有可见的图层才会被绘制
79
+ *
80
+ * @method paint
81
+ * @param {boolean} v 是否需要重绘
82
+ */
83
+ paint(v) {
84
+ if(this.visible !== false) {
85
+ super.paint(v);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * 检查点是否在图层内
91
+ * 锁定的图层不会响应鼠标事件
92
+ *
93
+ * @method checkPoint
94
+ * @param {object} p 坐标点 {x, y}
95
+ * @param {number} [pad] padding,额外的检测范围
96
+ * @return {boolean} 是否在图层内
97
+ */
98
+ checkPoint(p, pad) {
99
+ // 锁定的图层不响应交互
100
+ if(this.locked) return false;
101
+ return super.checkPoint(p, pad);
102
+ }
103
+
104
+ /**
105
+ * 清空图层
106
+ * 移除图层中的所有图形对象
107
+ *
108
+ * @method clear
109
+ */
110
+ clear() {
111
+ this.children.clear();
112
+ this.needUpdate = true;
113
+ }
114
+
115
+ /**
116
+ * 获取图层中的图形数量
117
+ *
118
+ * @method getShapeCount
119
+ * @return {number} 图形数量
120
+ */
121
+ getShapeCount() {
122
+ return this.children.length;
123
+ }
124
+
125
+ /**
126
+ * 获取图层信息
127
+ * 返回图层的基本信息,用于调试和日志
128
+ *
129
+ * @method getInfo
130
+ * @return {object} 图层信息对象
131
+ */
132
+ getInfo() {
133
+ return {
134
+ name: this.name,
135
+ visible: this.visible,
136
+ locked: this.locked,
137
+ shapeCount: this.getShapeCount()
138
+ };
139
+ }
140
+ }
141
+
142
+ export { jmLayer };
@@ -28,6 +28,61 @@ export default class jmPath extends jmControl {
28
28
  set points(v) {
29
29
  this.needUpdate = true;
30
30
  return this.property('points', v);
31
+ }
32
+
33
+ /**
34
+ * 转换为SVG路径
35
+ *
36
+ * @method toSVG
37
+ * @return {string} SVG路径字符串
38
+ */
39
+ toSVG() {
40
+ if(!this.points || this.points.length === 0) return '';
41
+
42
+ let pathData = '';
43
+ const points = this.points;
44
+
45
+ // 移动到起点
46
+ pathData += `M ${points[0].x} ${points[0].y}`;
47
+
48
+ // 绘制路径
49
+ for(let i = 1; i < points.length; i++) {
50
+ const p = points[i];
51
+ if(p.m) {
52
+ // 移动到新位置
53
+ pathData += ` M ${p.x} ${p.y}`;
54
+ } else {
55
+ // 直线到
56
+ pathData += ` L ${p.x} ${p.y}`;
57
+ }
58
+ }
59
+
60
+ // 如果是封闭路径
61
+ if(this.style && this.style.close) {
62
+ pathData += ' Z';
63
+ }
64
+
65
+ // 构建SVG元素
66
+ let svg = '<path d="' + pathData + '"';
67
+
68
+ // 添加样式
69
+ if(this.style) {
70
+ if(this.style.fill) {
71
+ svg += ' fill="' + this.style.fill + '"';
72
+ }
73
+ if(this.style.stroke) {
74
+ svg += ' stroke="' + this.style.stroke + '"';
75
+ }
76
+ if(this.style.lineWidth) {
77
+ svg += ' stroke-width="' + this.style.lineWidth + '"';
78
+ }
79
+ if(this.style.opacity) {
80
+ svg += ' opacity="' + this.style.opacity + '"';
81
+ }
82
+ }
83
+
84
+ svg += '/>';
85
+ return svg;
31
86
  }
32
87
 
33
88
  }
@@ -165,66 +165,75 @@ export default class jmUtils {
165
165
  * @param {function} copyHandler 复制对象回调,如果返回undefined,就走后面的逻辑,否则到这里中止
166
166
  * @return {object} 参数source的拷贝对象
167
167
  */
168
- static clone(source, target, deep = false, copyHandler = null, deepIndex = 0) {
168
+ static clone(source, target, deep = false, copyHandler = null, deepIndex = 0, cloned = null) {
169
169
  // 如果有指定回调,则用回调处理,否则走后面的复制逻辑
170
170
  if(typeof copyHandler === 'function') {
171
171
  const obj = copyHandler(source, deep, deepIndex);
172
172
  if(obj) return obj;
173
173
  }
174
- deepIndex++; // 每执行一次,需要判断最大拷贝深度
174
+
175
+ // 首次调用时初始化克隆映射表(用于处理循环引用)
176
+ if(!cloned) cloned = new WeakMap();
175
177
 
176
178
  if(typeof target === 'boolean') {
177
179
  deep = target;
178
180
  target = undefined;
179
181
  }
180
182
 
181
- // 超过100拷贝深度,直接返回
182
- if(deepIndex > 100) {
183
- return target;
183
+ // 非对象直接返回
184
+ if(!source || typeof source !== 'object') {
185
+ return typeof target !== 'undefined' ? target : source;
184
186
  }
185
187
 
186
- if(source && typeof source === 'object') {
187
- target = target || {};
188
+ // 如果source已经被克隆过,直接返回之前的克隆对象,打破循环引用
189
+ if(cloned.has(source)) return cloned.get(source);
188
190
 
191
+ // 数组处理
192
+ if(Array.isArray(source)) {
189
193
  //如果为当前泛型,则直接new
190
194
  if(this.isType(source, jmList)) {
191
195
  return new jmList(source);
192
196
  }
193
- else if(Array.isArray(source)) {
194
- //如果是深度复,则拷贝每个对象
195
- if(deep) {
196
- let dest = [];
197
- for(let i=0; i<source.length; i++) {
198
- dest.push(this.clone(source[i], target[i], deep, copyHandler, deepIndex));
199
- }
200
- return dest;
201
- }
202
- return source.slice(0);
203
- }
204
-
205
- if(source.__proto__) target.__proto__ = source.__proto__;
206
-
207
- for(let k in source) {
208
- if(k === 'constructor') continue;
209
- const v = source[k];
210
- // 不复制页面元素和class对象
211
- if(v && (v.tagName || v.getContext)) {
212
- target[k] = v;
213
- continue;
214
- }
215
-
216
- // 如果不是对象和空,则采用target的属性
217
- if(typeof target[k] === 'object' || typeof target[k] === 'undefined') {
218
- target[k] = this.clone(v, target[k], deep, copyHandler, deepIndex);
197
+ if(deep) {
198
+ let dest = [];
199
+ cloned.set(source, dest);
200
+ for(let i = 0; i < source.length; i++) {
201
+ dest.push(this.clone(source[i], undefined, deep, copyHandler, deepIndex + 1, cloned));
219
202
  }
203
+ return dest;
220
204
  }
221
- return target;
205
+ return source.slice(0);
222
206
  }
223
- else if(typeof target != 'undefined') {
224
- return target;
207
+
208
+ // 不复制页面元素和class对象(如jmControl实例等复杂对象保持引用)
209
+ if(source.tagName || source.getContext || source.emit) {
210
+ return source;
225
211
  }
226
212
 
227
- return source;
213
+ // 普通对象处理
214
+ target = target || {};
215
+ cloned.set(source, target);
216
+
217
+ // 保持原型链一致
218
+ if(source.__proto__) target.__proto__ = source.__proto__;
219
+
220
+ // 遍历自身可枚举属性(字符串键 + Symbol键),避免触发原型链上宿主对象的getter
221
+ const keys = Object.keys(source).concat(Object.getOwnPropertySymbols(source));
222
+ for(const k of keys) {
223
+ if(k === 'constructor') continue;
224
+ const v = source[k];
225
+ // 不复制页面元素和class对象
226
+ if(v && (v.tagName || v.getContext || v.emit)) {
227
+ target[k] = v;
228
+ continue;
229
+ }
230
+
231
+ // 如果不是对象和空,则采用target的属性
232
+ if(typeof target[k] === 'object' || typeof target[k] === 'undefined') {
233
+ target[k] = this.clone(v, target[k], deep, copyHandler, deepIndex + 1, cloned);
234
+ }
235
+ }
236
+ return target;
228
237
  }
229
238
 
230
239
  /**
@@ -265,7 +265,6 @@ class WeblBase {
265
265
 
266
266
  // 创建程序
267
267
  createProgram(vertexSrc, fragmentSrc) {
268
- this.context.lineWidth(1);
269
268
  return createProgram(this.context, vertexSrc, fragmentSrc);
270
269
  }
271
270
 
@@ -378,7 +377,9 @@ class WeblBase {
378
377
  // polygonIndices 顶点索引,
379
378
  earCutPointsToTriangles(points) {
380
379
  this.earCutCache = this.earCutCache || (this.earCutCache = {});
381
- const key = JSON.stringify(points);
380
+ // 快速缓存 key:用长度和首尾点坐标
381
+ const len = points.length;
382
+ const key = len + '_' + points[0].x + '_' + points[0].y + '_' + points[len-1].x + '_' + points[len-1].y;
382
383
  if (this.earCutCache[key]) return this.earCutCache[key];
383
384
 
384
385
  const ps = this.earCutPoints(points);// 切割得到3角色顶点索引,
@@ -473,31 +474,11 @@ class WeblBase {
473
474
 
474
475
  this.textureContext.fillStyle = fillStyle;
475
476
 
476
- this.textureContext.beginPath();
477
+ // 规则图形用 fillRect,比 beginPath/lineTo/fill 快
477
478
  if(!points || !points.length) {
478
- points = [];
479
- points.push({
480
- x: bounds.left,
481
- y: bounds.top
482
- });
483
- points.push({
484
- x: bounds.left + bounds.width,
485
- y: bounds.top
486
- });
487
- points.push({
488
- x: bounds.left + bounds.width,
489
- y: bounds.top + bounds.height
490
- });
491
- points.push({
492
- x: bounds.left,
493
- y: bounds.top + bounds.height
494
- });
495
- points.push({
496
- x: bounds.left,
497
- y: bounds.top
498
- });
499
- }
500
- if(points && points.length) {
479
+ this.textureContext.fillRect(0, 0, bounds.width, bounds.height);
480
+ } else {
481
+ this.textureContext.beginPath();
501
482
  for(const p of points) {
502
483
  //移至当前坐标
503
484
  if(p.m) {
@@ -506,17 +487,10 @@ class WeblBase {
506
487
  else {
507
488
  this.textureContext.lineTo(p.x - bounds.left, p.y - bounds.top);
508
489
  }
509
- }
510
- }
511
- else {
512
- this.textureContext.moveTo(0, 0);
513
- this.textureContext.lineTo(bounds.width, 0);
514
- this.textureContext.lineTo(bounds.width, bounds.height);
515
- this.textureContext.lineTo(0, bounds.height);
516
- this.textureContext.lineTo(0, 0);
490
+ }
491
+ this.textureContext.closePath();
492
+ this.textureContext.fill();
517
493
  }
518
- this.textureContext.closePath();
519
- this.textureContext.fill();
520
494
 
521
495
  const data = this.textureContext.getImageData(0, 0, canvas.width, canvas.height);
522
496
  return {