UNPKG

d2recharts

Version:

data driven react components of echarts

257 lines (232 loc) 7.74 kB
'use strict'; /** * d2recharts module * @module d2recharts * @see module:index * @see https://github.com/hustcc/echarts-for-react/blob/master/src/echarts-for-react.js */ const React = require('react'); const echarts = require('echarts'); const elementResizeEvent = require('element-resize-event'); const _ = require('lodash'); const update = require('immutability-helper'); const propTypes = require('./prop-types'); const util = require('../util/index'); class Recharts extends React.Component { componentDidMount() { this.timer = setTimeout(() => { const me = this; const theme = this.props.option['data-theme'] || 'default'; echarts.init(me.wrapper, theme); const echartObj = me.renderEchartDom(); const props = me.props; const onEvents = props.onEvents || []; _.forIn(onEvents, (eventFunc, eventName) => { // ignore the event config which not satisfy if (_.isString(eventName) && _.isFunction(eventFunc)) { // binding event echartObj.on(eventName, (param) => { eventFunc(param, me, echartObj); }); } }); this.silent = true; echartObj.on('updated', () => { if (this.silent) return; }); // on chart ready if (_.isFunction(props.onChartReady)) { props.onChartReady(me, echartObj); } // on resize elementResizeEvent(me.wrapper, () => { echartObj.resize(); }); // on css width/height change if (MutationObserver) { // 监听样式容器高度变化,重置图表大小 if (this.observer && this.observer.disconnect) { this.observer.disconnect(); } this.observer = new MutationObserver((mutations) => { mutations.forEach(item => { if (item.type === 'attributes' && item.attributeName === 'style') { try { echartObj.resize(); } catch (e) { // 屏蔽一个奇怪的 echarts 错误: Cannot set property '__alive' of undefined } } }); }); this.observer.observe(me.wrapper, { attributes: true, attributeFilter : ['style'] }); } if (window.navigator.userAgent.indexOf('Mobile') > -1) { // 解决移动端滚动时,tootips 不消失的问题 document.body.onscroll = _.debounce(() => { echartObj.dispatchAction({ type: 'hideTip' }) }, 200); } }, 0); } componentWillReceiveProps(nextProps) { const newTheme = nextProps.option['data-theme']; const oldTheme = this.props.option['data-theme']; if (newTheme && newTheme !== oldTheme) { // https://github.com/ecomfe/echarts/issues/2539 echarts.dispose(this.wrapper); echarts.init(this.wrapper, newTheme); } } componentDidUpdate(prevProps) { // update const omitKeys = ['height', 'width']; if ( JSON.stringify(_.omit(prevProps.option, omitKeys)) !== JSON.stringify(_.omit(this.props.option, omitKeys)) ) { this.renderEchartDom(); } } componentWillUnmount() { // remove clearTimeout(this.timer); echarts.dispose(this.wrapper); if (this.observer && this.observer.disconnect) { this.observer.disconnect(); } } extendXaxis(u = {}) { // 需要感知宽度以后的设置 const echartObj = this.getEchartsInstance(); if (!echartObj) { return; } const { option, schema } = this.props; const opt = {}; let lineWidth = 78; if (_.isEmpty(_.get(option, 'xAxis'))){ // 如果没有xAxis,则不做处理 // update不能为undefined设置值 return; } const dimension = _.get(option, 'data-columns.0'); const dimensionMeta = _.find(schema, ['name', dimension]); const isString = _.get(dimensionMeta, 'type') === 'string'; if (_.get(option, 'xAxis.0.type') === 'value') { // x轴是数值的时候 // 默认值为5,估算值 if ((echartObj.getWidth() - 100) / 60 < 5) { _.set(option, 'xAxis.0.splitNumber', 4); } return; } if (isString) { // 文本类型全部展示 opt.interval = 0; const xData = _.get(option, 'xAxis.data') || []; let isOverflowed = false; if (xData.length) { // 横着放存在覆盖情况,0.8是推测值 const maxWidth = 0.8 * echartObj.getWidth() / xData.length; isOverflowed = xData.some(v => util.guessTextWidth(v.slice(0, 7)) > maxWidth); } if (isOverflowed) { // 斜过来以后最多展示4个字 lineWidth = 12 * 4; opt.rotate = 45; // 补丁: 当有缩略轴的时候,需要grid向上移 if(option['data-showDataZoomInside']){ option.grid.bottom = 65; } } } _.set(u, 'option.xAxis.axisLabel', { $merge: { ...opt, formatter: v => { const defaultWidth = util.guessTextWidth(v); if (defaultWidth > lineWidth) { const guessLen = Math.floor(lineWidth / 12); let start = guessLen; let first = v.toString().substr(0, start); while (util.guessTextWidth(`${first}...`) <= lineWidth) { first += v[start]; start++; } return `${first}...`; } return v; }, }, }); } extendDataZoom(u = {}) { const echartObj = this.getEchartsInstance(); if (!echartObj) { return; } const { option } = this.props; if (!_.isArray(option.dataZoom)) { return; } const h = echartObj.getHeight(); _.set(u, 'option.dataZoom', { $set: _.map(option.dataZoom, opt => { let newOpt = _.assign({}, opt); if (opt.type !== 'inside') { // 为什么 使用 echarts 高度来计算 top 不行? // 因为拖拽 rerender 图表有一个 debounce 限制,如果拖动太快,下一次渲染的时候,echarts 的高度还是之前的老的高度,这样减去 20 之后,导致缩略找很高 / 很低。 // 所以正确的做法是:在 echarts rerender 之后,再触发一个render,专门用来做缩略轴高度。 // newOpt.top = h - 20; // FIXME 这个 height 确实在 echarts 文档中没有,但是实际使用确实是可行的。 newOpt.height = 20; newOpt.bottom = 0; } return newOpt; }), }); } getEchartsInstance() { return echarts.getInstanceByDom(this.wrapper); } renderEchartDom() { const echartObj = this.getEchartsInstance(); if (!echartObj) { return; } const u = {}; this.extendXaxis(u); this.extendDataZoom(u); const props = update(this.props, u); // set the echart option const option = util.pickExcept(props.option, ['width', 'height']); this.silent = false; echartObj.setOption(option, props.notMerge, props.lazyUpdate); // on chart ready if (props.option && _.isFunction(props.option.onChartRendered)) { props.option.onChartRendered(this, echartObj); } return echartObj; } render() { const me = this; const props = me.props; const style = _.assign({ width: props.option.width || props.width, height: props.option.height || props.height, }, props.style); // for render return ( <div ref={(wrapper) => me.wrapper = wrapper} className={props.className} style={style}/> ); } } Recharts.propTypes = propTypes.recharts; Recharts.defaultProps = { lazyUpdate: true, notMerge: true, width: '100%', height: '300px', }; module.exports = Recharts;