src/Wfunnel/index.tsx (184 lines of code) (raw):

'use strict'; import { Chart, Types, BaseChartConfig, ChartData, Colors } from '../common/types'; import Base from '../common/Base'; import errorWrap from '../common/errorWrap'; import rectTooltip, { TooltipConfig } from '../common/rectTooltip'; import rectLegend, { LegendConfig } from '../common/rectLegend'; import { GuideConfig } from '../common/guide'; import label, { LabelConfig } from '../common/label'; import themes from '../themes/index'; import { pxToNumber, numberDecimal } from '../common/common'; import geomStyle, { GeomStyleConfig } from '../common/geomStyle'; import './index.scss'; // 3.x代码 export interface WfunnelConfig extends BaseChartConfig { colors?: Colors; legend?: LegendConfig | boolean; tooltip?: TooltipConfig | boolean; guide?: GuideConfig; pyramid?: boolean; label?: LabelConfig | boolean; direction?: string; align?: string; percent?: Types.LooseObject | boolean; geomStyle?: GeomStyleConfig; } export class Funnel extends Base<WfunnelConfig> { chartName = 'G2Funnel'; legendField = 'x'; getDefaultConfig(): WfunnelConfig { return { colors: themes.order_10, legend: { position: 'top', align: '', nameFormatter: null, // 可以强制覆盖,手动设置label }, tooltip: { titleFormatter: null, nameFormatter: null, valueFormatter: null, }, label: false, pyramid: false, // 主方向,从上到下(vertical)、从左到右(horizontal) direction: 'vertical', // 排列位置 start,center,end align: 'center', // 尖顶漏斗图 percent: true, }; } init(chart: Chart, config: WfunnelConfig, data: any) { const defs: Record<string, Types.ScaleOption> = { type: { type: 'cat', }, }; chart.scale(defs); chart.interaction('element-active'); chart.axis(false); chart.data(data); // 设置图例 rectLegend(this, chart, config, null, 'single'); // tooltip rectTooltip( this, chart, config, { showTitle: false, showMarkers: false, showCrosshairs: false, }, null, { showTitle: false, showMarkers: false, showCrosshairs: false, }, ); // 根据传入的 direction 和 align 设置坐标系,并绘制图形 const drawType = `${config.direction}-${config.align}`; let geom = null; const fontSize1 = pxToNumber(themes['widgets-font-size-1']); let percentOffsetX = 0; let percentOffsetY = 0; const funnelShape = config.align === 'center' && config.pyramid ? 'pyramid' : 'funnel'; switch (drawType) { case 'vertical-left': case 'vertical-start': chart.coordinate('rect').transpose().scale(1, -1); geom = chart.interval().position('x*y').shape(funnelShape).color('x', config.colors); percentOffsetX = 3 * fontSize1; break; case 'vertical-center': chart.coordinate('rect').transpose().scale(1, -1); geom = chart .interval() .adjust([ { type: 'symmetric', }, ]) .position('x*y') .shape(funnelShape) .color('x', config.colors); break; case 'vertical-right': case 'vertical-end': chart.coordinate('rect').transpose().scale(-1, -1); geom = chart.interval().position('x*y').shape(funnelShape).color('x', config.colors); percentOffsetX = -3 * fontSize1; break; case 'horizontal-top': case 'horizontal-start': chart.coordinate('rect').reflect('y'); geom = chart.interval().position('x*y').shape(funnelShape).color('x', config.colors); percentOffsetY = 3 * fontSize1; break; case 'horizontal-center': geom = chart .interval() .position('x*y') .shape(funnelShape) .color('x', config.colors) .adjust([ { type: 'symmetric', }, ]); break; // case 'horizontal-bottom': // case 'horizontal-end': // 和 default 时相同 default: geom = chart.interval().position('x*y').shape(funnelShape).color('x', config.colors); percentOffsetY = -3 * fontSize1; } geomStyle(geom, config.geomStyle); if (config.label) { label({ geom: geom, config: config, useCustomOffset: true, componentConfig: { labelLine: { style: { lineWidth: 1, stroke: themes['widgets-axis-line'], }, }, content: (v, item, index) => { if (typeof config.label === 'boolean') { return v['y']; } if (config.label.labelFormatter) { return config.label.labelFormatter(v['y'], item, index); } return v['y']; }, }, }); } // 绘制辅助线,辅助背景区域 renderGuide(chart, config, data, percentOffsetX, percentOffsetY); } } function renderGuide( chart: Chart, config: WfunnelConfig, data: ChartData, percentOffsetX: number, percentOffsetY: number, ) { // 中间标签文本 chart.annotation().clear(true); let configPercent = config.percent; if (!configPercent) { return; } if (configPercent === true) { configPercent = {}; } const { labelFormatter, offsetX = 0, offsetY = 0, top = true, style = {} } = configPercent; const positionY = config.align === 'center' ? 'center' : 'start'; data.forEach((d: { y: any; x: any }, i: any) => { let content = `${numberDecimal((100 * d.y) / data[0].y)}%`; if (labelFormatter) { content = labelFormatter(d.y / data[0].y, d, i); } chart.annotation().text({ top, position: [d.x, positionY], offsetX: percentOffsetX + offsetX, offsetY: percentOffsetY + offsetY, content: content, // 显示的文本内容 style: { fill: themes['widgets-label-text'], fontSize: pxToNumber(themes['widgets-font-size-1']), textAlign: 'center', shadowBlur: 2, shadowColor: 'rgba(255, 255, 255, .3)', ...style, }, }); }); } const Wfunnel: typeof Funnel = errorWrap(Funnel); export default Wfunnel;