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.
- package/README.md +311 -6
- package/dist/jmgraph.core.min.js +1 -1
- package/dist/jmgraph.core.min.js.map +1 -1
- package/dist/jmgraph.js +2022 -368
- package/dist/jmgraph.min.js +1 -1
- package/index.js +23 -25
- package/package.json +1 -1
- package/src/core/jmControl.js +199 -30
- package/src/core/jmFilter.js +150 -0
- package/src/core/jmGraph.js +207 -7
- package/src/core/jmLayer.js +142 -0
- package/src/core/jmPath.js +55 -0
- 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 +156 -33
- package/src/shapes/jmEllipse.js +91 -0
- package/src/shapes/jmLabel.js +126 -75
- package/src/shapes/jmPolygon.js +129 -0
- package/src/shapes/jmRect.js +107 -29
- package/src/shapes/jmStar.js +160 -0
- package/example/ball.html +0 -217
- package/example/base.html +0 -112
- package/example/canvas.html +0 -54
- package/example/cell.html +0 -284
- package/example/controls/arc.html +0 -129
- package/example/controls/arrowline.html +0 -78
- package/example/controls/bezier.html +0 -299
- package/example/controls/img.html +0 -97
- package/example/controls/label.html +0 -87
- package/example/controls/line.html +0 -173
- package/example/controls/prismatic.html +0 -63
- package/example/controls/rect.html +0 -64
- package/example/controls/resize.html +0 -112
- package/example/controls/test.html +0 -360
- package/example/es.html +0 -70
- package/example/es5module.html +0 -63
- package/example/heartarc.html +0 -116
- package/example/index.html +0 -47
- package/example/js/require.js +0 -5
- package/example/love/img/bling/bling.tps +0 -265
- package/example/love/img/bling.json +0 -87
- package/example/love/img/bling.tps +0 -295
- package/example/love/img/doc/bling.gif +0 -0
- package/example/love/img/love.json +0 -95
- package/example/love/img/love.tps +0 -315
- package/example/love/img/qq/qq.tps +0 -399
- package/example/love/img/qq.json +0 -242
- package/example/love/index.html +0 -40
- package/example/love/js/game.js +0 -558
- package/example/music.html +0 -211
- package/example/node/test.js +0 -138
- package/example/pdf.html +0 -187
- package/example/progress.html +0 -173
- package/example/pso.html +0 -148
- package/example/sort.html +0 -805
- package/example/tweenjs.html +0 -84
- package/example/webgl.html +0 -278
- package/example/xfj/img/dr_die.gif +0 -0
- package/example/xfj/index.html +0 -332
- package/example/xfj/shake.js +0 -49
- package/example/xfj/testori.html +0 -76
package/src/core/jmGraph.js
CHANGED
|
@@ -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
|
-
|
|
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 };
|
package/src/core/jmPath.js
CHANGED
|
@@ -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
|
}
|
package/src/core/jmUtils.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
//
|
|
182
|
-
if(
|
|
183
|
-
return target;
|
|
183
|
+
// 非对象直接返回
|
|
184
|
+
if(!source || typeof source !== 'object') {
|
|
185
|
+
return typeof target !== 'undefined' ? target : source;
|
|
184
186
|
}
|
|
185
187
|
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
205
|
+
return source.slice(0);
|
|
222
206
|
}
|
|
223
|
-
|
|
224
|
-
|
|
207
|
+
|
|
208
|
+
// 不复制页面元素和class对象(如jmControl实例等复杂对象保持引用)
|
|
209
|
+
if(source.tagName || source.getContext || source.emit) {
|
|
210
|
+
return source;
|
|
225
211
|
}
|
|
226
212
|
|
|
227
|
-
|
|
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
|
/**
|
package/src/lib/webgl/base.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
477
|
+
// 规则图形用 fillRect,比 beginPath/lineTo/fill 快
|
|
477
478
|
if(!points || !points.length) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
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 {
|