in src/Wpie/index.tsx [205:590]
init(chart: Chart, config: WpieConfig, data: any[]) {
const defs: Record<string, Types.ScaleOption> = {
type: {
type: 'cat',
},
};
if (config.autoSort) {
data.sort((a, b) => b.y - a.y);
}
// 计算得总数据
let totalData = 0;
data.forEach((d) => {
if (d.y < 0 && config.filterNegativeNumbers) {
d.extra.push({
rawNumber: d.y
});
d.y = 0;
}
totalData += d.y;
});
this.totalData = numberDecimal(totalData, config?.legend?.decimal ?? config?.tooltip?.decimal ?? 2);
// 处理后的原始数据
this.sourceData = data;
chart.scale(defs);
chart.data(this.sourceData);
// 重要:绘制饼图时,必须声明 theta 坐标系
const thetaConfig: Types.CoordinateCfg = {
// radius: 1, // 设置饼图的为100% 大小,具体大小改变在 beforeInit 中diameter的值,目前为0.8
radius: Math.max(Math.min(config.outerRadius, 1), 0.01),
};
if (config.cycle) {
thetaConfig.innerRadius = Math.max(Math.min(config.innerRadius, 1), 0);
}
if (config.startAngle !== undefined) {
thetaConfig.startAngle = config.startAngle;
}
if (config.endAngle !== undefined) {
thetaConfig.endAngle = config.endAngle;
}
// coordinate translate 操作会导致饼图变形,暂时换一种方式实现
/*const coord = */ chart.coordinate('theta', thetaConfig);
// if (config.coord) {
// const { transform } = config.coord || {};
//
// if (Array.isArray(transform)) {
// transform.forEach((t) => {
// transformCoord(coord, t);
// });
// } else if (transform && typeof transform === 'object') {
// transformCoord(coord, transform);
// }
// }
// const drawPadding = getDrawPadding(config.drawPadding, config.label, this.defaultConfig.drawPadding);
// 设置图例
rectLegend(
this,
chart,
config,
{
// autoCollapse: false,
// position: 'right',
// itemTpl: (value, itemColor, checked, index) => {
// const { nameFormatter, valueFormatter, showData = true } = config.legend || {};
//
// const item = (this.data && this.data[index]) || {};
// const raw = (this.rawData && this.rawData[0]) || {};
// const percent = numberDecimal(item.y / this.totalData, 4);
//
// const result = nameFormatter ? nameFormatter(value, {
// ...raw,
// percent,
// itemColor,
// checked,
// }, index) : value;
//
// if (showData) {
// const number = valueFormatter ? valueFormatter(item.y, {
// ...raw,
// percent,
// itemColor,
// checked,
// }, index) : item.y;
// return `${'<li class="g2-legend-list-item item-{index} {checked}" data-color="{originColor}" data-value="{originValue}">' +
// '<i class="g2-legend-marker" style="background-color:{color};"></i>' +
// '<span class="g2-legend-text">'}${result}</span>` + `<span class="g2-legend-value">${number}</span></li>`;
// }
//
// return `${'<li class="g2-legend-list-item item-{index} {checked}" data-color="{originColor}" data-value="{originValue}">' +
// '<i class="g2-legend-marker" style="background-color:{color};"></i>' +
// '<span class="g2-legend-text">'}${result}</span></li>`;
// },
// 'g2-legend': {
// ...legendHtmlContainer,
// position: 'static',
// overflow: 'auto',
// // inline flex items 不能使用百分比的margin/padding,设置为固定大小
// marginLeft: `${Math.max(pxToNumber(themes['widgets-font-size-4']) - drawPadding[1], 0)}px`,
// },
// 'g2-legend-list-item': {
// ...legendHtmlListItem,
// marginRight: 0,
// },
},
'single',
null,
true,
(item: G2Dependents.ListItem, index: number) => {
const { name } = item;
const raw = (this.rawData && this.rawData[0]) || {};
let value = 0;
raw.data &&
raw.data.forEach((r: any) => {
if (Array.isArray(r) && r[0] === name) {
value = r[1];
} else if (typeof r === 'object' && r.x === name) {
value = r.y;
}
});
const percent = this.totalData === 0 ? 0 : numberDecimal(value / this.totalData, 4);
return {
...raw,
percent,
...item,
};
},
);
// 饼图单独处理
// 进位相关配置项
let formatConfig: any;
// 当tooltip中配置了单位相关信息时,直接使用tooltip的配置项,否则使用y轴配置项
if (
typeof config?.tooltip === 'object' &&
(config?.tooltip?.valueType ||
config?.tooltip?.unit ||
config?.tooltip?.needUnitTransform ||
config?.tooltip?.unitTransformTo)
) {
formatConfig = config.tooltip;
} else {
formatConfig = config?.legend ?? {};
}
const defaultValueFormatter = customFormatter(formatConfig)
// tooltip
rectTooltip(
this,
chart,
config,
{
showTitle: false,
showMarkers: false,
showCrosshairs: false,
shared: false,
},
(ev: any) => {
const raw = (this.rawData && this.rawData[0]) || {};
const { items } = ev.data;
items.forEach((item: any, index: number) => {
const percent = numberDecimal(item.value / this.totalData, 4);
if (typeof config.tooltip === 'boolean') {
item.value = defaultValueFormatter(item.value)
}
if (typeof config.tooltip === 'object') {
if (config.tooltip.valueFormatter) {
item.value = config.tooltip.valueFormatter(
item.value,
{
...raw,
percent,
},
index,
items,
);
} else {
item.value = defaultValueFormatter(item.value)
}
if (config.tooltip.nameFormatter) {
item.name = config.tooltip.nameFormatter(
item.name,
{
...raw,
percent,
},
index,
items,
);
}
}
});
},
{
showTitle: false,
showMarkers: false,
showCrosshairs: false,
shared: false,
},
);
this.geom = chart.interval().position('y').adjust('stack');
this.geom = this.geom.color('x', config.colors);
if (typeof config.animate === 'object') {
this.geom.animate(config.animate);
}
if (config.select) {
chart.interaction('element-single-selected', {
start: [
{
isEnable(context: any) {
if (context.view.options.data.length > 1) {
return true;
}
return false;
},
trigger: 'element:click',
action: 'element-single-selected:toggle',
},
],
});
}
geomStyle(this.geom, config.geomStyle);
const labelField = 'y';
label({
geom: this.geom,
config: config,
field: labelField,
componentConfig: {
offset: 20,
content: ((v, item, index) => {
if (typeof config.label === 'boolean') {
return v[labelField];
}
if (config.label.labelFormatter) {
const percent = numberDecimal(v[labelField] / this.totalData, 4);
return config.label.labelFormatter(
v[labelField],
{
...item,
percent,
} as Types.MappingDatum,
index,
);
}
return v[labelField];
}) as Types.GeometryLabelContentCallback,
},
});
polarLegendLayout(chart);
// 空数据渲染效果
chart.on('beforepaint', () => {
if (this.totalData !== 0 && this.noDataShape) {
this.noDataShape.remove(true);
this.noDataShape = null;
}
});
circleAnnotation(chart, config, this.size, 'G2Pie');
// 环图中心内容
if (
config.cycle &&
config.innerContent &&
!this.props.children &&
!this.isEmpty &&
!this.props.loading &&
!this.props.errorInfo
) {
const container = document.createElement('div');
container.className = `${FullCrossName}-children`;
const firstChild = this.chartDom.firstChild;
this.chartDom.insertBefore(container, firstChild);
const content = (
<Wnumber
bottomTitle={config?.innerContent?.title ?? this.rawData?.[0]?.name}
unit={config?.innerContent?.unit ?? ''}
>
{config?.innerContent?.value ?? this.totalData}
</Wnumber>
);
ReactDOM.render(content, container);
} else if (!this.props.children) {
// 删去中心内容
const container = this.chartDom.getElementsByClassName(`${FullCrossName}-children`)?.[0];
if (container) {
this.chartDom.removeChild(container);
}
}
chart.on('afterpaint', () => {
// 默认选中效果
selectGeom(this.geom, config.selectData);
updateChildrenPosition(chart, this.chartDom);
if (this.totalData === 0) {
const bgGroup = chart.getLayer('bg' as any);
const coordinate = chart.getCoordinate();
const { radius, innerRadius } = coordinate;
const { x: centerX, y: centerY } = coordinate.getCenter();
const pieSize = Math.min(chart.coordinateBBox.width, chart.coordinateBBox.height) * radius;
const outerR = pieSize / 2;
const path = [
['M', centerX, centerY - outerR],
['A', outerR, outerR, 0, 1, 1, centerX, centerY + outerR],
['A', outerR, outerR, 0, 1, 1, centerX, centerY - outerR],
// ['Z'],
];
if (innerRadius > 0) {
const innerR = (pieSize * innerRadius) / 2;
path.push(
['M', centerX, centerY - innerR],
['A', innerR, innerR, 0, 0, 0, centerX, centerY + innerR],
['A', innerR, innerR, 0, 0, 0, centerX, centerY - innerR],
// ['Z'],
);
}
if (!this.noDataShape) {
this.noDataShape = bgGroup.addShape({
id: 'no-data-path',
name: 'no-data-path',
type: 'path',
attrs: {
path,
fill: themes['widgets-circle-stroke-background'],
},
});
} else {
this.noDataShape.attr('path', path);
}
// shape.set('tip', 'sdfhsjkdhk');
//
// registerInteraction('no-data-text', {
// start: [
// {
// trigger: 'no-data-path:mousemove',
// action: 'ellipsis-text:show',
// throttle: { wait: 50, leading: true, trailing: false },
// },
// {
// trigger: 'no-data-path:touchstart',
// action: 'ellipsis-text:show',
// throttle: { wait: 50, leading: true, trailing: false },
// },
// ],
// end: [
// { trigger: 'no-data-path:mouseleave', action: 'ellipsis-text:hide' },
// { trigger: 'no-data-path:touchend', action: 'ellipsis-text:hide' },
// ],
// });
}
});
}
destroy() {
this.noDataShape = null;
}
}