jmgraph 3.2.19 → 3.2.20
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 +174 -2
- package/dist/jmgraph.core.min.js +1 -1
- package/dist/jmgraph.core.min.js.map +1 -1
- package/dist/jmgraph.js +1640 -135
- package/dist/jmgraph.min.js +1 -1
- package/index.js +13 -2
- package/package.json +1 -1
- package/src/core/jmGraph.js +453 -4
- package/src/core/jmLayer.js +142 -0
- package/src/core/jmPath.js +55 -0
- package/src/shapes/jmEllipse.js +91 -0
- package/src/shapes/jmLabel.js +127 -15
- package/src/shapes/jmPolygon.js +129 -0
- 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/index.js
CHANGED
|
@@ -12,6 +12,9 @@ import {jmArrowLine} from "./src/shapes/jmArrowLine.js";
|
|
|
12
12
|
import {jmImage} from "./src/shapes/jmImage.js";
|
|
13
13
|
import {jmLabel} from "./src/shapes/jmLabel.js";
|
|
14
14
|
import {jmResize} from "./src/shapes/jmResize.js";
|
|
15
|
+
import {jmEllipse} from "./src/shapes/jmEllipse.js";
|
|
16
|
+
import {jmPolygon} from "./src/shapes/jmPolygon.js";
|
|
17
|
+
import {jmStar} from "./src/shapes/jmStar.js";
|
|
15
18
|
|
|
16
19
|
import { jmGraph as jmGraphCore,
|
|
17
20
|
jmUtils,
|
|
@@ -21,7 +24,8 @@ import { jmGraph as jmGraphCore,
|
|
|
21
24
|
jmGradient,
|
|
22
25
|
jmEvents,
|
|
23
26
|
jmControl,
|
|
24
|
-
jmPath,
|
|
27
|
+
jmPath,
|
|
28
|
+
jmLayer } from "./src/core/jmGraph.js";
|
|
25
29
|
|
|
26
30
|
const shapes = {
|
|
27
31
|
"arc": jmArc,
|
|
@@ -36,7 +40,10 @@ const shapes = {
|
|
|
36
40
|
"image": jmImage,
|
|
37
41
|
"img": jmImage,
|
|
38
42
|
"label": jmLabel,
|
|
39
|
-
"resize": jmResize
|
|
43
|
+
"resize": jmResize,
|
|
44
|
+
"ellipse": jmEllipse,
|
|
45
|
+
"polygon": jmPolygon,
|
|
46
|
+
"star": jmStar
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
export default class jmGraph extends jmGraphCore {
|
|
@@ -93,6 +100,10 @@ export {
|
|
|
93
100
|
jmImage,
|
|
94
101
|
jmLabel,
|
|
95
102
|
jmResize,
|
|
103
|
+
jmEllipse,
|
|
104
|
+
jmPolygon,
|
|
105
|
+
jmStar,
|
|
106
|
+
jmLayer,
|
|
96
107
|
jmGraph,
|
|
97
108
|
createJmGraph as create
|
|
98
109
|
};
|
package/package.json
CHANGED
package/src/core/jmGraph.js
CHANGED
|
@@ -6,6 +6,7 @@ import {jmGradient} from "./jmGradient.js";
|
|
|
6
6
|
import {jmEvents} from "./jmEvents.js";
|
|
7
7
|
import {jmControl} from "./jmControl.js";
|
|
8
8
|
import {jmPath} from "./jmPath.js";
|
|
9
|
+
import {jmLayer} from "./jmLayer.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* jmGraph画图类库
|
|
@@ -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初始化
|
|
@@ -272,6 +287,8 @@ export default class jmGraph extends jmControl {
|
|
|
272
287
|
if(!args) args = {};
|
|
273
288
|
args.graph = this;
|
|
274
289
|
let obj = new shape(args);
|
|
290
|
+
// 添加到活动图层
|
|
291
|
+
this.addShapeToLayer(obj);
|
|
275
292
|
return obj;
|
|
276
293
|
}
|
|
277
294
|
}
|
|
@@ -501,7 +518,7 @@ export default class jmGraph extends jmControl {
|
|
|
501
518
|
this.normalSize = {
|
|
502
519
|
width: this.canvas.width,
|
|
503
520
|
height: this.canvas.height
|
|
504
|
-
};
|
|
521
|
+
};
|
|
505
522
|
}
|
|
506
523
|
|
|
507
524
|
//this.context.scale && this.context.scale(dx,dy);
|
|
@@ -514,6 +531,312 @@ export default class jmGraph extends jmControl {
|
|
|
514
531
|
this.canvas.style && (this.canvas.style.transform = `scale(${this.scaleSize.x}, ${this.scaleSize.y})`);
|
|
515
532
|
}
|
|
516
533
|
|
|
534
|
+
/**
|
|
535
|
+
* 设置缩放因子
|
|
536
|
+
* 支持以指定点为中心进行缩放,保持该点在屏幕上的位置不变
|
|
537
|
+
*
|
|
538
|
+
* @method setZoom
|
|
539
|
+
* @param {number} zoom 缩放因子(建议范围:0.1 - 10)
|
|
540
|
+
* @param {number} [x] 缩放中心X坐标(画布坐标)
|
|
541
|
+
* @param {number} [y] 缩放中心Y坐标(画布坐标)
|
|
542
|
+
* @return {jmGraph} 返回当前实例,支持链式调用
|
|
543
|
+
*/
|
|
544
|
+
setZoom(zoom, x, y) {
|
|
545
|
+
// 参数验证
|
|
546
|
+
if(typeof zoom !== 'number' || isNaN(zoom)) {
|
|
547
|
+
console.warn('jmGraph: setZoom - 无效的缩放因子');
|
|
548
|
+
return this;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// 限制缩放范围,防止过度缩放导致性能问题或显示异常
|
|
552
|
+
const minZoom = 0.1; // 最小缩放到10%
|
|
553
|
+
const maxZoom = 10; // 最大放大到10倍
|
|
554
|
+
zoom = Math.max(minZoom, Math.min(maxZoom, zoom));
|
|
555
|
+
|
|
556
|
+
if (x !== undefined && y !== undefined) {
|
|
557
|
+
// 计算缩放前后的坐标偏移
|
|
558
|
+
// 保持缩放中心点在屏幕上的位置不变
|
|
559
|
+
const oldZoom = this.scaleFactor;
|
|
560
|
+
const newZoom = zoom;
|
|
561
|
+
|
|
562
|
+
// 调整平移量以保持缩放中心位置不变
|
|
563
|
+
this.translation.x = x - (x - this.translation.x) * (newZoom / oldZoom);
|
|
564
|
+
this.translation.y = y - (y - this.translation.y) * (newZoom / oldZoom);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
this.scaleFactor = zoom;
|
|
568
|
+
this.needUpdate = true;
|
|
569
|
+
this.redraw();
|
|
570
|
+
|
|
571
|
+
return this; // 支持链式调用
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* 平移画布
|
|
576
|
+
* 移动画布视图,改变可视区域
|
|
577
|
+
*
|
|
578
|
+
* @method pan
|
|
579
|
+
* @param {number} dx X轴平移量(像素)
|
|
580
|
+
* @param {number} dy Y轴平移量(像素)
|
|
581
|
+
* @return {jmGraph} 返回当前实例,支持链式调用
|
|
582
|
+
*/
|
|
583
|
+
pan(dx, dy) {
|
|
584
|
+
// 参数验证
|
|
585
|
+
if(typeof dx !== 'number' || typeof dy !== 'number' || isNaN(dx) || isNaN(dy)) {
|
|
586
|
+
console.warn('jmGraph: pan - 无效的平移参数');
|
|
587
|
+
return this;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
this.translation.x += dx;
|
|
591
|
+
this.translation.y += dy;
|
|
592
|
+
this.needUpdate = true;
|
|
593
|
+
this.redraw();
|
|
594
|
+
|
|
595
|
+
return this; // 支持链式调用
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* 重置缩放和平移
|
|
600
|
+
* 恢复画布到初始状态(缩放为1,平移为0)
|
|
601
|
+
*
|
|
602
|
+
* @method resetTransform
|
|
603
|
+
* @return {jmGraph} 返回当前实例,支持链式调用
|
|
604
|
+
*/
|
|
605
|
+
resetTransform() {
|
|
606
|
+
this.scaleFactor = 1;
|
|
607
|
+
this.translation = {x: 0, y: 0};
|
|
608
|
+
this.needUpdate = true;
|
|
609
|
+
this.redraw();
|
|
610
|
+
|
|
611
|
+
return this; // 支持链式调用
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* 初始化图层系统
|
|
616
|
+
* 创建图层管理的基础结构,包括默认图层
|
|
617
|
+
*
|
|
618
|
+
* @method initLayers
|
|
619
|
+
* @private
|
|
620
|
+
*/
|
|
621
|
+
initLayers() {
|
|
622
|
+
if(!this.layers) {
|
|
623
|
+
this.layers = new jmList();
|
|
624
|
+
// 创建默认图层
|
|
625
|
+
const defaultLayer = this.createLayer('Default Layer');
|
|
626
|
+
this.activeLayer = defaultLayer;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* 创建新图层
|
|
632
|
+
* 图层用于组织和管理图形对象,支持可见性和锁定控制
|
|
633
|
+
*
|
|
634
|
+
* @method createLayer
|
|
635
|
+
* @param {string} name 图层名称(必须唯一)
|
|
636
|
+
* @param {object} [options] 图层选项
|
|
637
|
+
* @param {boolean} [options.visible=true] 图层是否可见
|
|
638
|
+
* @param {boolean} [options.locked=false] 图层是否锁定(锁定后不可交互)
|
|
639
|
+
* @return {jmLayer} 新创建的图层
|
|
640
|
+
*/
|
|
641
|
+
createLayer(name, options = {}) {
|
|
642
|
+
// 参数验证
|
|
643
|
+
if(!name || typeof name !== 'string') {
|
|
644
|
+
console.warn('jmGraph: createLayer - 图层名称必须是非空字符串');
|
|
645
|
+
name = `Layer_${Date.now()}`;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
this.initLayers();
|
|
649
|
+
|
|
650
|
+
// 检查图层名称是否已存在
|
|
651
|
+
const existingLayer = this.getLayer(name);
|
|
652
|
+
if(existingLayer) {
|
|
653
|
+
console.warn(`jmGraph: 图层 "${name}" 已存在,将返回现有图层`);
|
|
654
|
+
return existingLayer;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const layer = new jmLayer({
|
|
658
|
+
name: name,
|
|
659
|
+
graph: this,
|
|
660
|
+
...options
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
this.layers.add(layer);
|
|
664
|
+
this.children.add(layer);
|
|
665
|
+
this.needUpdate = true;
|
|
666
|
+
return layer;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* 获取所有图层
|
|
671
|
+
*
|
|
672
|
+
* @method getLayers
|
|
673
|
+
* @return {jmList} 图层列表
|
|
674
|
+
*/
|
|
675
|
+
getLayers() {
|
|
676
|
+
this.initLayers();
|
|
677
|
+
return this.layers;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* 根据名称获取图层
|
|
682
|
+
*
|
|
683
|
+
* @method getLayer
|
|
684
|
+
* @param {string} name 图层名称
|
|
685
|
+
* @return {jmLayer|null} 图层对象,如果不存在则返回null
|
|
686
|
+
*/
|
|
687
|
+
getLayer(name) {
|
|
688
|
+
this.initLayers();
|
|
689
|
+
|
|
690
|
+
if(!name) return null;
|
|
691
|
+
|
|
692
|
+
let result = null;
|
|
693
|
+
this.layers.each((i, layer) => {
|
|
694
|
+
if(layer.name === name) {
|
|
695
|
+
result = layer;
|
|
696
|
+
return false; // 找到后停止遍历
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
return result;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* 设置活动图层
|
|
704
|
+
* 新创建的图形将自动添加到活动图层
|
|
705
|
+
*
|
|
706
|
+
* @method setActiveLayer
|
|
707
|
+
* @param {string|jmLayer} layer 图层名称或图层对象
|
|
708
|
+
* @return {jmGraph} 返回当前实例,支持链式调用
|
|
709
|
+
*/
|
|
710
|
+
setActiveLayer(layer) {
|
|
711
|
+
this.initLayers();
|
|
712
|
+
|
|
713
|
+
// 支持传入图层名称或图层对象
|
|
714
|
+
if(typeof layer === 'string') {
|
|
715
|
+
layer = this.getLayer(layer);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if(!layer || !(layer instanceof jmLayer)) {
|
|
719
|
+
console.warn('jmGraph: setActiveLayer - 无效的图层');
|
|
720
|
+
return this;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
this.activeLayer = layer;
|
|
724
|
+
return this;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* 获取当前活动图层
|
|
729
|
+
* 活动图层是新创建图形的默认容器
|
|
730
|
+
*
|
|
731
|
+
* @method getActiveLayer
|
|
732
|
+
* @return {jmLayer} 当前活动图层
|
|
733
|
+
*/
|
|
734
|
+
getActiveLayer() {
|
|
735
|
+
this.initLayers();
|
|
736
|
+
return this.activeLayer;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* 移除图层
|
|
741
|
+
* 删除指定图层及其包含的所有图形
|
|
742
|
+
* 注意:默认图层不可删除
|
|
743
|
+
*
|
|
744
|
+
* @method removeLayer
|
|
745
|
+
* @param {string|jmLayer} layer 图层名称或图层对象
|
|
746
|
+
* @return {boolean} 是否成功删除
|
|
747
|
+
*/
|
|
748
|
+
removeLayer(layer) {
|
|
749
|
+
this.initLayers();
|
|
750
|
+
|
|
751
|
+
// 支持传入图层名称或图层对象
|
|
752
|
+
if(typeof layer === 'string') {
|
|
753
|
+
layer = this.getLayer(layer);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if(!layer) {
|
|
757
|
+
console.warn('jmGraph: removeLayer - 图层不存在');
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// 禁止删除默认图层
|
|
762
|
+
if(layer.name === 'Default Layer') {
|
|
763
|
+
console.warn('jmGraph: 不能删除默认图层');
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// 如果删除的是当前活动图层,切换到默认图层
|
|
768
|
+
if(this.activeLayer === layer) {
|
|
769
|
+
this.activeLayer = this.getLayer('Default Layer');
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
this.layers.remove(layer);
|
|
773
|
+
this.children.remove(layer);
|
|
774
|
+
this.needUpdate = true;
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* 将形状添加到指定图层
|
|
780
|
+
* 如果未指定图层,则添加到当前活动图层
|
|
781
|
+
*
|
|
782
|
+
* @method addShapeToLayer
|
|
783
|
+
* @param {jmControl} shape 要添加的形状对象
|
|
784
|
+
* @param {string|jmLayer} [layer] 图层名称或图层对象,默认为当前活动图层
|
|
785
|
+
* @return {jmGraph} 返回当前实例,支持链式调用
|
|
786
|
+
*/
|
|
787
|
+
addShapeToLayer(shape, layer) {
|
|
788
|
+
this.initLayers();
|
|
789
|
+
|
|
790
|
+
// 参数验证
|
|
791
|
+
if(!shape) {
|
|
792
|
+
console.warn('jmGraph: addShapeToLayer - 无效的形状对象');
|
|
793
|
+
return this;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// 确定目标图层
|
|
797
|
+
if(!layer) {
|
|
798
|
+
layer = this.activeLayer;
|
|
799
|
+
} else if(typeof layer === 'string') {
|
|
800
|
+
layer = this.getLayer(layer);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if(!layer) {
|
|
804
|
+
console.warn('jmGraph: addShapeToLayer - 图层不存在');
|
|
805
|
+
return this;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
layer.children.add(shape);
|
|
809
|
+
this.needUpdate = true;
|
|
810
|
+
return this;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* 从图层中移除形状
|
|
815
|
+
*
|
|
816
|
+
* @method removeShapeFromLayer
|
|
817
|
+
* @param {jmControl} shape 要移除的形状对象
|
|
818
|
+
* @return {jmGraph} 返回当前实例,支持链式调用
|
|
819
|
+
*/
|
|
820
|
+
removeShapeFromLayer(shape) {
|
|
821
|
+
if(!shape) {
|
|
822
|
+
console.warn('jmGraph: removeShapeFromLayer - 无效的形状对象');
|
|
823
|
+
return this;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// 从所有图层中查找并移除
|
|
827
|
+
if(this.layers) {
|
|
828
|
+
this.layers.each((i, layer) => {
|
|
829
|
+
if(layer.children.contains(shape)) {
|
|
830
|
+
layer.children.remove(shape);
|
|
831
|
+
this.needUpdate = true;
|
|
832
|
+
return false; // 找到后停止遍历
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
return this;
|
|
838
|
+
}
|
|
839
|
+
|
|
517
840
|
/**
|
|
518
841
|
* 保存为base64图形数据
|
|
519
842
|
*
|
|
@@ -525,6 +848,131 @@ export default class jmGraph extends jmControl {
|
|
|
525
848
|
return data;
|
|
526
849
|
}
|
|
527
850
|
|
|
851
|
+
/**
|
|
852
|
+
* 导出为PNG图片
|
|
853
|
+
* 使用Canvas的toDataURL方法导出当前画布内容
|
|
854
|
+
*
|
|
855
|
+
* @method exportToPNG
|
|
856
|
+
* @param {string} [fileName='jmgraph-export'] 文件名(不含扩展名)
|
|
857
|
+
* @param {string} [format='image/png'] 图片格式,支持image/png和image/jpeg
|
|
858
|
+
* @param {number} [quality=0.9] 图片质量(0-1之间,仅对JPEG格式有效)
|
|
859
|
+
*/
|
|
860
|
+
exportToPNG(fileName = 'jmgraph-export', format = 'image/png', quality = 0.9) {
|
|
861
|
+
try {
|
|
862
|
+
// 确保画布已渲染
|
|
863
|
+
this.redraw();
|
|
864
|
+
|
|
865
|
+
const dataURL = this.canvas.toDataURL(format, quality);
|
|
866
|
+
this.downloadFile(dataURL, fileName, 'png');
|
|
867
|
+
} catch(error) {
|
|
868
|
+
console.error('jmGraph: exportToPNG - 导出失败', error);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* 导出为JPEG图片
|
|
874
|
+
*
|
|
875
|
+
* @method exportToJPEG
|
|
876
|
+
* @param {string} [fileName='jmgraph-export'] 文件名(不含扩展名)
|
|
877
|
+
* @param {number} [quality=0.9] 图片质量(0-1之间)
|
|
878
|
+
*/
|
|
879
|
+
exportToJPEG(fileName = 'jmgraph-export', quality = 0.9) {
|
|
880
|
+
this.exportToPNG(fileName, 'image/jpeg', quality);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* 导出为SVG文件
|
|
885
|
+
* 将当前画布内容转换为SVG格式
|
|
886
|
+
* 注意:只有实现了toSVG方法的形状才能被导出
|
|
887
|
+
*
|
|
888
|
+
* @method exportToSVG
|
|
889
|
+
* @param {string} [fileName='jmgraph-export'] 文件名(不含扩展名)
|
|
890
|
+
*/
|
|
891
|
+
exportToSVG(fileName = 'jmgraph-export') {
|
|
892
|
+
try {
|
|
893
|
+
const svg = this.toSVG();
|
|
894
|
+
const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
|
|
895
|
+
const url = URL.createObjectURL(blob);
|
|
896
|
+
this.downloadFile(url, fileName, 'svg');
|
|
897
|
+
|
|
898
|
+
// 释放URL对象,避免内存泄漏
|
|
899
|
+
setTimeout(() => URL.revokeObjectURL(url), 100);
|
|
900
|
+
} catch(error) {
|
|
901
|
+
console.error('jmGraph: exportToSVG - 导出失败', error);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* 转换为SVG字符串
|
|
907
|
+
* 遍历所有图层和形状,生成SVG标记
|
|
908
|
+
*
|
|
909
|
+
* @method toSVG
|
|
910
|
+
* @return {string} SVG字符串
|
|
911
|
+
*/
|
|
912
|
+
toSVG() {
|
|
913
|
+
// SVG头部,包含命名空间和画布尺寸
|
|
914
|
+
let svg = `<svg width="${this.width}" height="${this.height}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${this.width} ${this.height}">`;
|
|
915
|
+
|
|
916
|
+
// 添加背景色(如果有)
|
|
917
|
+
if(this.style && this.style.fill) {
|
|
918
|
+
svg += `<rect width="100%" height="100%" fill="${this.style.fill}"/>`;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// 遍历所有图层
|
|
922
|
+
if(this.layers) {
|
|
923
|
+
this.layers.each((i, layer) => {
|
|
924
|
+
if(layer.visible) {
|
|
925
|
+
// 添加图层组,方便管理
|
|
926
|
+
svg += `<g id="${layer.name}" opacity="${layer.opacity || 1}">`;
|
|
927
|
+
|
|
928
|
+
// 遍历图层中的所有形状
|
|
929
|
+
layer.children.each((j, shape) => {
|
|
930
|
+
if(shape.toSVG) {
|
|
931
|
+
svg += shape.toSVG();
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
svg += '</g>';
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
else {
|
|
940
|
+
// 遍历直接添加的形状(兼容没有图层系统的情况)
|
|
941
|
+
this.children.each((i, shape) => {
|
|
942
|
+
if(shape.toSVG) {
|
|
943
|
+
svg += shape.toSVG();
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
svg += '</svg>';
|
|
949
|
+
return svg;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* 下载文件
|
|
954
|
+
* 创建临时链接元素触发浏览器下载
|
|
955
|
+
*
|
|
956
|
+
* @method downloadFile
|
|
957
|
+
* @private
|
|
958
|
+
* @param {string} url 文件URL或Data URL
|
|
959
|
+
* @param {string} fileName 文件名(不含扩展名)
|
|
960
|
+
* @param {string} extension 文件扩展名
|
|
961
|
+
*/
|
|
962
|
+
downloadFile(url, fileName, extension) {
|
|
963
|
+
// 创建临时链接元素
|
|
964
|
+
const link = document.createElement('a');
|
|
965
|
+
link.href = url;
|
|
966
|
+
link.download = `${fileName}.${extension}`;
|
|
967
|
+
|
|
968
|
+
// 添加到DOM并触发点击
|
|
969
|
+
document.body.appendChild(link);
|
|
970
|
+
link.click();
|
|
971
|
+
|
|
972
|
+
// 清理DOM
|
|
973
|
+
document.body.removeChild(link);
|
|
974
|
+
}
|
|
975
|
+
|
|
528
976
|
/**
|
|
529
977
|
* 自动刷新画版
|
|
530
978
|
* @param {function} callback 执行回调
|
|
@@ -572,4 +1020,5 @@ export {
|
|
|
572
1020
|
jmEvents,
|
|
573
1021
|
jmControl,
|
|
574
1022
|
jmPath,
|
|
1023
|
+
jmLayer,
|
|
575
1024
|
};
|
|
@@ -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 };
|