UNPKG

5.06 kBPlain TextView Raw
1import { debounce, each, isString, get } from '@antv/util';
2import { ChartCfg } from '../interface';
3import { GROUP_Z_INDEX, VIEW_LIFE_CIRCLE } from '../constant';
4import { getEngine } from '../engine';
5import { createDom, getChartSize, removeDom, modifyCSS } from '../util/dom';
6import View from './view';
7
8/**
9 * Chart 类,是使用 G2 进行绘图的入口。
10 */
11export default class Chart extends View {
12 /** Chart 的 DOM 容器 */
13 public ele: HTMLElement;
14
15 /** 图表宽度 */
16 public width: number;
17 /** 图表高度 */
18 public height: number;
19 /** 是否开启局部刷新 */
20 public localRefresh: boolean;
21 /** 是否自适应 DOM 容器宽高,默认为 false,需要用户手动指定宽高 */
22 public autoFit: boolean;
23 /** 图表渲染引擎 */
24 public renderer: 'canvas' | 'svg';
25
26 private wrapperElement: HTMLElement;
27
28 // @ts-ignore
29 constructor(props: ChartCfg) {
30 const {
31 container,
32 width,
33 height,
34 autoFit = false,
35 padding,
36 appendPadding,
37 renderer = 'canvas',
38 pixelRatio,
39 localRefresh = true,
40 visible = true,
41 supportCSSTransform = false,
42 defaultInteractions = ['tooltip', 'legend-filter', 'legend-active', 'continuous-filter', 'ellipsis-text'],
43 options,
44 limitInPlot,
45 theme,
46 syncViewPadding,
47 } = props;
48
49 const ele: HTMLElement = isString(container) ? document.getElementById(container) : container;
50
51 // 生成内部正式绘制的 div 元素
52 const wrapperElement = createDom('<div style="position:relative;"></div>');
53 ele.appendChild(wrapperElement);
54
55 // if autoFit, use the container size, to avoid the graph render twice.
56 const size = getChartSize(ele, autoFit, width, height);
57
58 const G = getEngine(renderer);
59
60 const canvas = new G.Canvas({
61 container: wrapperElement,
62 pixelRatio,
63 localRefresh,
64 supportCSSTransform,
65 ...size,
66 });
67
68 // 调用 view 的创建
69 super({
70 parent: null,
71 canvas,
72 // create 3 group layers for views.
73 backgroundGroup: canvas.addGroup({ zIndex: GROUP_Z_INDEX.BG }),
74 middleGroup: canvas.addGroup({ zIndex: GROUP_Z_INDEX.MID }),
75 foregroundGroup: canvas.addGroup({ zIndex: GROUP_Z_INDEX.FORE }),
76 padding,
77 appendPadding,
78 visible,
79 options,
80 limitInPlot,
81 theme,
82 syncViewPadding,
83 });
84
85 this.ele = ele;
86 this.canvas = canvas;
87 this.width = size.width;
88 this.height = size.height;
89 this.autoFit = autoFit;
90 this.localRefresh = localRefresh;
91 this.renderer = renderer;
92 this.wrapperElement = wrapperElement;
93
94 // 自适应大小
95 this.updateCanvasStyle();
96 this.bindAutoFit();
97 this.initDefaultInteractions(defaultInteractions);
98 }
99
100 private initDefaultInteractions(interactions) {
101 each(interactions, (interaction) => {
102 this.interaction(interaction);
103 });
104 }
105
106 /**
107 * 改变图表大小,同时重新渲染。
108 * @param width 图表宽度
109 * @param height 图表高度
110 * @returns
111 */
112 public changeSize(width: number, height: number) {
113 // 如果宽高一致,那么 changeSize 不执行任何操作
114 if (this.width === width && this.height === height) {
115 return this;
116 }
117
118 this.emit(VIEW_LIFE_CIRCLE.BEFORE_CHANGE_SIZE);
119
120 this.width = width;
121 this.height = height;
122 this.canvas.changeSize(width, height);
123
124 // 重新渲染
125 this.render(true);
126
127 this.emit(VIEW_LIFE_CIRCLE.AFTER_CHANGE_SIZE);
128
129 return this;
130 }
131
132 /**
133 * 销毁图表,同时解绑事件,销毁创建的 G.Canvas 实例。
134 * @returns void
135 */
136 public destroy() {
137 super.destroy();
138
139 this.unbindAutoFit();
140 this.canvas.destroy();
141
142 removeDom(this.wrapperElement);
143 this.wrapperElement = null;
144 }
145
146 /**
147 * 显示或隐藏图表
148 * @param visible 是否可见,true 表示显示,false 表示隐藏
149 * @returns
150 */
151 public changeVisible(visible: boolean) {
152 super.changeVisible(visible); // 需要更新 visible 变量
153 this.wrapperElement.style.display = visible ? '' : 'none';
154
155 return this;
156 }
157
158 /**
159 * 自动根据容器大小 resize 画布
160 */
161 public forceFit() {
162 // skip if already destroyed
163 if (!this.destroyed) {
164 // 注意第二参数用 true,意思是即时 autoFit = false,forceFit() 调用之后一样是适配容器
165 const { width, height } = getChartSize(this.ele, true, this.width, this.height);
166 this.changeSize(width, height);
167 }
168 }
169
170 private updateCanvasStyle() {
171 modifyCSS(this.canvas.get('el'), {
172 display: 'inline-block',
173 verticalAlign: 'middle',
174 });
175 }
176
177 private bindAutoFit() {
178 if (this.autoFit) {
179 window.addEventListener('resize', this.onResize);
180 }
181 }
182
183 private unbindAutoFit() {
184 if (this.autoFit) {
185 window.removeEventListener('resize', this.onResize);
186 }
187 }
188
189 /**
190 * when container size changed, change chart size props, and re-render.
191 */
192 private onResize = debounce(() => {
193 this.forceFit();
194 }, 300);
195}