127 lines
5.1 KiB
JavaScript
127 lines
5.1 KiB
JavaScript
import {
|
||
PALETTE, FONT, titleStyle, footnote, watermarks,
|
||
} from '../theme.js';
|
||
|
||
/**
|
||
* 泡泡玛特主要 IP 收入占比(2026H1)
|
||
* 饼图(非环图),图例放在右侧:每项上行 IP 名、下行小字显示收入和占比。
|
||
* 扇区外直接标注 IP 名 + 数值(两行)。源:ppmt.xlsx「IP收入和占比」2026H1 列,
|
||
* HACIPUPU、BUNNY、PUCKY 等该期无数据的 IP 不列入。
|
||
* 占比为衍生指标:各 IP 收入 ÷ 全部列入 IP 收入合计(源表占比列未填)。
|
||
*
|
||
* 配色:手写的一条色相渐变谱(红→橙→金→橄榄→翡翠→青→蓝→紫),
|
||
* 末位「外采及其他」用中性灰(残差项惯例)。冷色段靠明暗交替拉开
|
||
* (深蓝 → 浅蓝紫 → 紫 → 浅兰紫),避免同色系挤在一起分不清。
|
||
* 相邻扇区及全组两两 ΔE ≥ 24.6,与同页 ppmt_margins 四色及 wine ΔE ≥ 25。
|
||
*/
|
||
const COLORS = [
|
||
'#D64545', // THE MONSTERS 玫红
|
||
'#E8913C', // 星星人 琥珀橙
|
||
'#D9C043', // 授权IP(非独家) 金
|
||
'#7FA83C', // CRYBABY 橄榄绿
|
||
'#3E9E6E', // DIMOO 翡翠
|
||
'#3FB6D0', // SKULLPANDA 亮青
|
||
'#2E5FA8', // 其他艺术家IP 深蓝
|
||
'#8E86E0', // HIRONO-小野 浅蓝紫
|
||
'#8A3FA8', // MOLLY 紫
|
||
'#D081C4', // Zsiga 浅兰紫
|
||
'#8C8C8C', // 外采及其他 中性灰
|
||
];
|
||
|
||
export function ppmtIpShareChart(rows, { width, height }) {
|
||
const data = rows.map((row, i) => ({
|
||
name: row.ip,
|
||
value: row.revenue,
|
||
itemStyle: { color: COLORS[i % COLORS.length] },
|
||
}));
|
||
const total = data.reduce((s, d) => s + d.value, 0);
|
||
const byName = Object.fromEntries(data.map((d) => [d.name, d]));
|
||
|
||
// 把「饼图 + 两侧外标签」当成一个整体,与图例并成一组居中摆放:
|
||
// 图例紧跟在右侧标签外缘之后,不会被推到画布边缘留下大片空白
|
||
const LEGEND_WIDTH = 152; // 最宽一行「17.7 亿元 · 10.3%」约 120px + 色块
|
||
const LEGEND_GAP = 24; // 右侧标签外缘 → 图例
|
||
// 左右标签占位不对称:左侧文字向左延伸(text-anchor: end)要占满,
|
||
// 右侧只需容下引导线 + 首行文字起段,留太多会在图例前空出一大块
|
||
const LABEL_LEFT = 135;
|
||
const LABEL_RIGHT = 72;
|
||
const topPad = 78;
|
||
const bottomPad = 64;
|
||
const availH = height - topPad - bottomPad;
|
||
|
||
// 半径取高/宽两个方向的较小值;上下各留 35px 给最上、最下扇区的两行标签
|
||
// 末尾 ×0.85:实测 250 偏大,缩 15% 留出呼吸感
|
||
const rByHeight = availH / 2 - 35;
|
||
const rByWidth = (width - 48 - LEGEND_GAP - LEGEND_WIDTH - LABEL_LEFT - LABEL_RIGHT) / 2;
|
||
const radius = Math.max(80, Math.min(rByHeight, rByWidth, 250) * 0.85);
|
||
|
||
const blockW = radius * 2 + LABEL_LEFT + LABEL_RIGHT;
|
||
const leftMargin = Math.max(16, (width - blockW - LEGEND_GAP - LEGEND_WIDTH) / 2);
|
||
const centerX = leftMargin + LABEL_LEFT + radius;
|
||
const centerY = topPad + availH / 2;
|
||
const legendLeft = leftMargin + blockW + LEGEND_GAP;
|
||
|
||
return {
|
||
backgroundColor: '#fff',
|
||
title: {
|
||
...titleStyle,
|
||
text: '泡泡玛特主要IP收入占比(2026H1)',
|
||
subtext: `占比 = 各IP收入 ÷ 全部IP收入合计(合计 ${total.toFixed(1)} 亿元),单位:亿元`,
|
||
},
|
||
// 图例在右侧承载 IP 名 + 收入/占比明细
|
||
legend: {
|
||
orient: 'vertical',
|
||
left: legendLeft,
|
||
width: LEGEND_WIDTH,
|
||
top: 'middle',
|
||
itemGap: 14,
|
||
itemWidth: 14,
|
||
itemHeight: 14,
|
||
align: 'left',
|
||
data: data.map((d) => d.name),
|
||
formatter: (name) => {
|
||
const d = byName[name];
|
||
const pct = (d.value / total) * 100;
|
||
return `{name|${name}}\n{sub|${d.value.toFixed(1)} 亿元 · ${pct.toFixed(1)}%}`;
|
||
},
|
||
textStyle: {
|
||
rich: {
|
||
name: { fontFamily: FONT, fontSize: 13, fontWeight: 600, color: PALETTE.ink, lineHeight: 18 },
|
||
sub: { fontFamily: FONT, fontSize: 11, color: PALETTE.grey, lineHeight: 16 },
|
||
},
|
||
},
|
||
},
|
||
tooltip: {
|
||
trigger: 'item',
|
||
textStyle: { fontFamily: FONT },
|
||
formatter: (p) => `${p.marker}${p.name}:${p.value.toFixed(1)} 亿元(${p.percent.toFixed(1)}%)`,
|
||
},
|
||
series: [
|
||
{
|
||
name: 'IP收入占比',
|
||
type: 'pie',
|
||
radius,
|
||
center: [centerX, centerY],
|
||
data,
|
||
label: {
|
||
show: true,
|
||
formatter: (p) => `{name|${p.name}}\n{value|${p.value.toFixed(1)} 亿元}`,
|
||
rich: {
|
||
name: { fontFamily: FONT, fontSize: 12, color: PALETTE.ink, fontWeight: 600, lineHeight: 16 },
|
||
value: { fontFamily: FONT, fontSize: 11, color: PALETTE.grey, lineHeight: 15 },
|
||
},
|
||
},
|
||
labelLine: { length: 14, length2: 16, lineStyle: { color: PALETTE.axisLine } },
|
||
itemStyle: { borderColor: '#fff', borderWidth: 2 },
|
||
emphasis: {
|
||
itemStyle: { shadowBlur: 12, shadowColor: 'rgba(0,0,0,0.2)' },
|
||
},
|
||
},
|
||
],
|
||
graphic: [
|
||
...watermarks(width, height),
|
||
footnote('HACIPUPU、BUNNY、PUCKY 等 IP 2026H1 无收入数据,未列入 | @思考的Joey'),
|
||
],
|
||
};
|
||
}
|