UNPKG

bizcharts

Version:
249 lines (226 loc) 6.98 kB
import uniqueId from '@antv/util/lib/unique-id'; import _each from '@antv/util/lib/each'; import _isFunction from '@antv/util/lib/is-function'; import _isArray from '@antv/util/lib/is-array'; import _isEqual from '@antv/util/lib/is-equal'; import { Chart as G2Chart } from '../../core'; import warn from 'warning'; import shallowEqual from '../../utils/shallowEqual'; import pickWithout from '../../utils/pickWithout'; import cloneDeep from '../../utils/cloneDeep'; import { REACT_PIVATE_PROPS } from '../../utils/constant'; import { VIEW_LIFE_CIRCLE } from '@antv/g2/lib/constant'; import EventEmitter from '@antv/event-emitter'; import { IEvent } from '../../interface'; import { pickEventName } from './events'; const processData = (data) => { if (data && data.rows) { return data.rows; } return data; } class ChartHelper extends EventEmitter { public chart: G2Chart; public config: Record<string, any> = {}; private isNewInstance: boolean; public extendGroup: any; public key: string; createInstance(config) { this.chart = new G2Chart({ ...config }); this.key = uniqueId('bx-chart'); this.chart.emit('initialed'); this.isNewInstance = true; // 更新了实例的标记 this.extendGroup = { isChartCanvas: true, chart: this.chart, }; } render() { if (!this.chart) { return; } try { // 普通error 只能兜住react render周期里的error。 chart render周期的error 要单独处理 if (this.isNewInstance) { this.chart.render(); this.onGetG2Instance(); // @ts-ignore this.chart.unbindAutoFit(); // 不使用g2的监听 this.isNewInstance = false; // @ts-ignore } else if(this.chart.forceReRender) { // forceReRender 填部分g2更新不干净的坑 this.chart.render(); } else { this.chart.render(true); } // 处理elements状态 this.chart.emit('processElemens'); } catch(e) { this.emit('renderError', e); this.destory(); if(console) { console.error(e?.stack) } } } private onGetG2Instance() { // 当且仅当 isNewInstance 的时候执行。 if (_isFunction(this.config.onGetG2Instance)) { this.config.onGetG2Instance(this.chart); } } shouldReCreateInstance(newConfig) { // 如果上一个实例数据为空则直接销毁重建,以免影响动画 if (!this.chart || newConfig.forceUpdate) { return true; } const { data:preData, ...preOptions} = this.config; const { data, ...options } = newConfig; if (_isArray(this.config.data) && preData.length === 0 && _isArray(data) && data.length !== 0 ) { return true; } // scale 切换不需要重建实例 const unCompareProps = [...REACT_PIVATE_PROPS, 'scale', 'width', 'height', 'container', '_container', '_interactions', 'placeholder', /^on/, /^\_on/]; if (!_isEqual(pickWithout(preOptions, [...unCompareProps]), pickWithout(options, [...unCompareProps]))) { return true; } return false; } update(props) { const newConfig = cloneDeep(this.adapterOptions(props)); if (this.shouldReCreateInstance(newConfig)) { this.destory(); this.createInstance(newConfig); } // 重置 if (newConfig.pure) { // 纯画布 关闭 this.chart.axis(false); this.chart.tooltip(false); this.chart.legend(false); // @ts-ignore this.chart.isPure = true; } // 事件 const events = pickEventName(this.config); const newEvents = pickEventName(newConfig); // 配置 const { data, interactions, ...options } = newConfig; const { data: preData, interactions: preInteractions = [] } = this.config; if (!this.isNewInstance) { // 取消事件绑定 events.forEach(ev => { this.chart.off(ev[1], this.config[`_${ev[0]}`]) }); } /** 更新 */ // 绑定事件 newEvents.forEach(evName => { newConfig[`_${evName[0]}`] = (ev: IEvent) => { // 输入chart实例方便用户使用 newConfig[evName[0]](ev, this.chart); } this.chart.on(evName[1], newConfig[`_${evName[0]}`]) }); // 数据 更新 if(_isArray(preData) && preData.length) { // 数据只做2级浅比较 // fixme: 做4级比较 let isEqual = true; if (newConfig.notCompareData) { // 手动关闭对比 isEqual = false; } if (preData.length !== data.length) { // 长度不相等 isEqual = false; } else { preData.forEach((element, index) => { if (!shallowEqual(element, data[index])) { isEqual = false; } }); } if (!isEqual) { // @ts-ignore this.chart.isDataChanged = true; this.chart.emit(VIEW_LIFE_CIRCLE.BEFORE_CHANGE_DATA); // 1. 保存数据 this.chart.data(data); // 2. 最后再渲染 // 3. 遍历子 view 进行 change data const views = this.chart.views; for (let i = 0, len = views.length; i < len; i++) { const view = views[i]; // 子 view 有自己的数据, 会在执行view的配置时会覆盖 view.changeData(data); } this.chart.emit(VIEW_LIFE_CIRCLE.AFTER_CHANGE_DATA); } } else { this.chart.data(data); } // 比例尺 this.chart.scale(options.scale); // 动画 if (options.animate === false) { this.chart.animate(false); } else { this.chart.animate(true); } // 交互 interactions preInteractions.forEach(interact => { // 这里不做对比,无意义,都是要销毁重建,不如直接全量销毁 this.chart.removeInteraction(interact); }); interactions.forEach(interact => { this.chart.interaction(interact); }); // filter _each(this.config.filter, (it, index) => { // 销毁 if (_isArray(it)) { this.chart.filter(it[0], null); } else { this.chart.filter(index, null); } }); _each(newConfig.filter, (it, index) => { if (_isArray(it)) { this.chart.filter(it[0], it[1]); } else { this.chart.filter(index, it); } }) // 主题 this.chart.theme(newConfig.theme); // 缓存处理后的配置 this.config = newConfig; } adapterOptions({data, ...others}) { // 剔除 React 自身的属性 const options = pickWithout(others, [...REACT_PIVATE_PROPS]); if (options.forceFit) { warn(false, 'forceFit 已废弃,请使用`autoFit`替代'); } options.data = processData(data) || []; return options; } destory() { if (!this.chart) { return; } this.extendGroup = null; let { chart } = this; chart.destroy(); chart = null; this.chart = null; this.config = {}; } } export default ChartHelper;