/** * @fileOverview facets of chart * @author dxq613@gmail.com */ const Global = require('../global'); const Util = require('../util'); const assign = Util.assign; const isNil = Util.isNil; const isArray = Util.isArray; const cloneDeep = Util.cloneDeep; // 绑定事件 const wrapBehavior = Util.wrapBehavior; // 获取绑定的事件 const getWrapBehavior = Util.getWrapBehavior; class Base { getDefaultCfg() { return { chart: null, group: null, /** * 是否默认显示每个分面的title * @type {Boolean} */ showTitle: true, /** * 是否自动修改坐标轴的信息 * @type {Boolean} */ autoSetAxis: true, /** * View 的内边框 * @type {Number|Array} */ padding: 10, /** * 遍历每个view 的回调函数 * @type {Function} */ eachView: null, /** * 分面的字段名列表 * @type {Array} */ fields: [], /** * 列值的的标题 * @type {Object} */ colTitle: { offsetY: -15, style: { fontSize: 14, textAlign: 'center', fill: '#666', fontFamily: Global.fontFamily } }, rowTitle: { offsetX: 15, style: { fontSize: 14, textAlign: 'center', rotate: 90, fill: '#666', fontFamily: Global.fontFamily } } }; } constructor(cfg) { const defaultCfg = this.getDefaultCfg(); assign(this, defaultCfg, cfg); this.init(); } init() { if (!this.chart) { throw new Error('Facets Error: please specify the chart!'); } this._bindEvent(); this.initContainer(); if (this.chart.get('data')) { this.initViews(); } } initContainer() { const chart = this.chart; const frontPlot = chart.get('frontPlot'); const group = frontPlot.addGroup(); this.group = group; } initViews() { const chart = this.chart; const data = chart.get('data'); const eachView = this.eachView; const facets = this.generateFacets(data); for (let i = 0; i < facets.length; i++) { const facet = facets[i]; const region = facet.region; const view = chart.view({ start: region.start, end: region.end, padding: this.padding }); view.source(facet.data); this.beforeProcessView(view, facet); if (eachView) { eachView(view, facet); } this.afterProcessView(view, facet); facet.view = view; } this.facets = facets; } /** * 处理 view 前 * @protected */ beforeProcessView(/* view, facet */) { } /** * 处理view * @param {Object} view 视图 * @param {Object} facet 分面信息 * @protected */ afterProcessView(view, facet) { if (this.autoSetAxis) { this.processAxis(view, facet); } } processAxis(view, facet) { const viewOptions = view.get('options'); const geoms = view.get('geoms'); if ((!viewOptions.coord.type || viewOptions.coord.type === 'rect') && geoms.length) { const field = geoms[0].get('attrOptions').position.field; const fields = isArray(field) ? field : field.split('*').map(str => str.trim()); const xField = fields[0]; const yField = fields[1]; if (isNil(viewOptions.axes)) { viewOptions.axes = {}; } const axes = viewOptions.axes; if (axes !== false) { if (xField && axes[xField] !== false) { axes[xField] = axes[xField] || {}; this.setXAxis(xField, axes, facet); } if (yField && axes[yField] !== false) { axes[yField] = axes[yField] || {}; this.setYAxis(yField, axes, facet); } } } } setXAxis(/* xField, axes, facet */) { } setYAxis(/* yField, axes, facet */) { } // 默认显示各列的标题 renderTitle(view, facet) { this.drawColTitle(view, facet); } getScaleText(field, value, view) { let rst; if (field) { const scales = view.get('scales'); let scale = scales[field]; if (!scale) { scale = view.createScale(field); } rst = scale.getText(value); } else { rst = value; } return rst; } drawColTitle(view, facet) { const text = this.getScaleText(facet.colField, facet.colValue, view); const colTextCfg = assign({ position: [ '50%', '0%' ], content: text }, this.colTitle); view.guide().text(colTextCfg); } drawRowTitle(view, facet) { const text = this.getScaleText(facet.rowField, facet.rowValue, view); const rowTextCfg = assign({ position: [ '100%', '50%' ], content: text }, cloneDeep(this.rowTitle)); view.guide().text(rowTextCfg); } /** * 数据过滤器 * @protected * @param {Array} conditions 过滤条件 * @return {Function} 过滤函数 */ getFilter(conditions) { const filter = obj => { let filtered = true; conditions.forEach(cond => { const field = cond.field; const value = cond.value; // const values = cond.values; let tmp = true; if (!isNil(value) && field) { tmp = obj[field] === value; } filtered = filtered && tmp; }); return filtered; }; return filter; } /** * 获取字段对应的值 * @protected * @param {String} field 字段名 * @param {Array} data 数据 * @return {Array} 字段对应的值 */ getFieldValues(field, data) { const rst = []; const tmpMap = {}; for (let i = 0; i < data.length; i++) { const obj = data[i]; const value = obj[field]; if (!isNil(value) && !tmpMap[value]) { rst.push(value); tmpMap[value] = true; } } return rst; } getRegion(rows, cols, xIndex, yIndex) { const xWidth = 1 / cols; // x轴方向的每个分面的偏移 const yWidth = 1 / rows; // y轴方向的每个分面的偏移 const start = { x: xWidth * xIndex, y: yWidth * yIndex }; const end = { x: start.x + xWidth, y: start.y + yWidth }; return { start, end }; } /** * 生成分面 * @protected * @return {Array} 多个分面集合 */ generateFacets(/* data */) { return []; } _bindEvent() { const chart = this.chart; chart.on('afterchangedata', wrapBehavior(this, 'onDataChange')); chart.on('beforeclear', wrapBehavior(this, 'onClear')); chart.on('beforedestroy', wrapBehavior(this, 'destroy')); chart.on('beforepaint', wrapBehavior(this, 'onPaint')); chart.on('setdata', wrapBehavior(this, 'onDataChange')); } _clearEvent() { const chart = this.chart; if (chart) { chart.off('afterchangedata', getWrapBehavior(this, 'onDataChange')); chart.off('beforeclear', getWrapBehavior(this, 'onClear')); chart.off('beforedestroy', getWrapBehavior(this, 'destroy')); chart.off('beforepaint', getWrapBehavior(this, 'onPaint')); chart.off('setdata', getWrapBehavior(this, 'onDataChange')); } } _clearFacets() { const facets = this.facets; const chart = this.chart; if (facets) { for (let i = 0; i < facets.length; i++) { const facet = facets[i]; chart.removeView(facet.view); } } this.facets = null; } onClear() { this.onRemove(); } onPaint() { if (this.showTitle) { const facets = this.facets; for (let i = 0; i < facets.length; i++) { const facet = facets[i]; const view = facet.view; this.renderTitle(view, facet); } } } onDataChange() { this._clearFacets(); this.initViews(); } onRemove() { this._clearFacets(); this._clearEvent(); this.group && this.group.remove(); this.chart = null; this.facets = null; this.group = null; } destroy() { this.onRemove(); this.destroyed = true; } } module.exports = Base;