关于 graphic-chart
这项技能可将 JSON 或 CSV 数据制作成 PNG 图表,使用 Playwright 在无头 Chromium 中渲染 Apache ECharts。它涵盖图表配置、样式预设、渲染检查和导出步骤。
主要功能
- 01支持柱状、折线、面积、饼图、散点、雷达和矩形树图
- 02解析 JSON 数组、对象数组及 CSV
- 03提供五种命名样式
- 04使用 Apache ECharts 和 Playwright 渲染 HTML
- 05以 2× 设备比例导出 PNG
适用场景
- 01将结构化数据转换为静态图表图片
- 02突出显示选定数值或类别
- 03按指定样式和尺寸导出图表
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ varnan-tech/ opendirectory --skill graphic-chart安装后在 Claude Code 中输入/graphic-chart确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdevals/evals.jsonreferences/chart-library.mdstyle-presets.mdscripts/export-chart.shscreenshot-chart.mjs详细介绍
graphic-chart
将数据可视化图表生成为 PNG。通过 Playwright 在无头 Chromium 中使用 Apache ECharts v6 渲染 HTML → 以 2× Retina 画质截图。
CDN:https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js
关键规则(每次生成前阅读)
area类型 →type: 'line'+areaStyle: {}——ECharts 没有type: 'area'。doughnut→type: 'pie'+radius: ['40%', '70%']——ECharts 没有type: 'doughnut'。- 就绪信号:在
chart.setOption()之前注册chart.on('finished', fn)——ECharts bug #14101/#17500:如果在 setOption 之后注册监听器,它会静默地永远不触发。务必在 setOption 之前注册finished和rendered两个事件。 - 必须显式设置
xAxis.type: 'category'——ECharts 不会根据数据推断此类型。忘记设置会生成空白图表。 - 类别标签放在
xAxis.data中,不要放在data.labels数组中。ECharts 结构是扁平的:{ xAxis, yAxis, series, grid, title, legend }——不嵌套在data或options下。 - 数据标签为内置功能——在任意系列上使用
label: { show: true }。无需插件。 - 通过单项
itemStyle突出显示特定柱形/数据点——直接在data数组中放入{ value: N, itemStyle: { color: '#...' } }。不要使用 Chart.js 风格的backgroundColor数组。 - ECharts 使用
<div>容器初始化,而非<canvas>——echarts.init(document.getElementById('chart'))。容器 div 必须显式设置尺寸。 - 在选项中设置
animation: false——禁用动画,实现即时渲染。仍须在 setOption 之前注册finished+rendered事件作为就绪信号。 - **切勿在聊天中输出 HTML。**保存到文件,只显示摘要。
- **标题应说明洞察,而非主题。**使用“Revenue grew 3× in 12 months”,而不是“Monthly Revenue”。
- 饼图/圆环图:使用正文
padding: 64px 80px和.chart-container { max-height: 860px }——在没有标题时可避免图表铺满整个区域。
步骤 1:收集信息
必填:chart_type、data
可选参数及默认值:
| 参数 | 默认值 | 说明 |
|---|---|---|
| chart_type | — | bar / line / area / pie / doughnut / scatter / radar / treemap |
| data | — | JSON 数组或 CSV——必填 |
| title | — | 说明洞察,不超过 10 个词 |
| subtitle | — | 1 句话的背景说明 |
| style | clean-slate | clean-slate / midnight-editorial / matt-gray / electric-burst / brutalist |
| dimensions | 1080x1080 | WxH 像素(输出 PNG 通过 deviceScaleFactor 达到 2×) |
| x_label | — | X 轴标签文本 |
| y_label | — | Y 轴标签文本 |
| source | — | 页脚中显示的数据来源 |
| highlight | — | 要突出显示的数据标签(例如 "Q4"、"Dec"、索引 3) |
如果缺少 chart_type 或 data,请原样询问:
"要创建图表,我需要:
- 图表类型——bar / line / area / pie / doughnut / scatter / radar / treemap
- 数据——请提供 JSON 数组或 CSV(例如
[12, 18, 22, 25, 31],标签为['Q1','Q2','Q3','Q4','Q5'])可选:标题、样式(默认:clean-slate)、尺寸(默认:1080×1080)、要突出显示的特定数据点"
如果两项都已提供 → 跳至步骤 2。
第 2 步:内部架构(绝不向用户显示)
1. 规范化图表类型:
area→line+ 在系列上设置areaStyle: {}doughnut→pie+ 在系列上设置radius: ['40%', '70%']horizontal bar→bar+ 交换 xAxis/yAxis(类目轴在 y 轴)- 其他类型:保持原样
2. 阅读 references/chart-library.md —— 加载此图表类型的完整配置规范。
3. 阅读 references/style-presets.md —— 加载所选样式的 CSS 令牌和数据调色板。
4. 确定设计方向:
| 决策 | 依据 |
|---|---|
| 基调 | 专业 / 编辑风 / 大胆 / 技术风——与数据受众相匹配 |
| 数据故事 | 此图表要证明的单一洞见(作为标题) |
| 强调策略 | 哪个数据点需要视觉强调,原因是什么? |
| 背景 | 浅色(clean-slate、matt-gray)或深色(midnight-editorial、electric-burst、brutalist) |
5. 解析数据:
- 简单数组
[12, 18, 22]→series.data,标签单独提供 - 对象数组
[{x: 'Jan', y: 12}]→ 从 x 键设置xAxis.data,从 y 值设置series.data - CSV:将标题行解析为
xAxis.data,将数值行解析为series.data - 多系列:使用多个
series条目,每个条目包含type、name、data - 散点图:使用
series.data: [[x1,y1], [x2,y2], ...]格式
6. 解析尺寸: "1080x1080" → W=1080,H=1080。主体尺寸 = WxH。输出 PNG = 2W × 2H。
第 3 步:生成 HTML
生成前阅读所有内容:
references/chart-library.md中此图表类型的完整 ECharts 配置规范references/style-presets.md中所选样式的 CSS 令牌和调色板
必需的 HTML 结构:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
[font CDN link from style preset]
<style>
:root {
[all CSS tokens from style preset]
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: [W]px; height: [H]px;
overflow: hidden;
background: var(--bg);
font-family: var(--font-body);
}
body {
display: flex;
flex-direction: column;
padding: 40px 48px 32px; /* pie/doughnut: use 64px 80px */
}
/* ECharts container must have explicit size */
.chart-container {
flex: 1;
min-height: 0;
/* pie/doughnut only: max-height: 860px; */
}
.chart-header { margin-bottom: 24px; }
.chart-title {
font-family: var(--font-display);
font-size: clamp(1.1rem, 2.5vw, 1.6rem);
font-weight: 700;
color: var(--text);
line-height: 1.2;
}
.chart-subtitle {
font-family: var(--font-body);
font-size: clamp(0.75rem, 1.2vw, 0.9rem);
color: var(--text-muted);
margin-top: 6px;
line-height: 1.5;
}
.chart-footer {
margin-top: 14px;
font-family: var(--font-body);
font-size: 10px;
color: var(--text-muted);
opacity: 0.65;
}
</style>
</head>
<body>
[if title or subtitle: <div class="chart-header"><div class="chart-title">...</div>...</div>]
<div id="chart" class="chart-container"></div>
[if source: <div class="chart-footer">Source: [source]</div>]
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
<script>
window.__chartReady = false;
document.fonts.ready.then(() => {
const container = document.getElementById('chart');
const chart = echarts.init(container, null, { renderer: 'canvas' });
// CRITICAL: register events BEFORE setOption — ECharts bug #14101/#17500
// 'finished' may silently not fire if registered after setOption with animation:false
chart.on('finished', () => {
window.__chartReady = true;
});
// Belt-and-suspenders fallback via 'rendered'
chart.on('rendered', () => {
clearTimeout(window.__renderDebounce);
window.__renderDebounce = setTimeout(() => { window.__chartReady = true; }, 100);
});
const palette = [palette from style preset];
const option = {
animation: false, // instant render for screenshot
backgroundColor: 'transparent', // body CSS handles bg color
textStyle: {
fontFamily: '[--font-body value]',
color: '[--text-muted value]',
},
[title config if title param provided],
[legend config per chart type],
[grid config per chart type],
[xAxis config per chart type],
[yAxis config per chart type],
series: [{
[full series config from chart-library.md for this type]
[palette colors applied per chart type]
[if highlight: per-item itemStyle on the highlighted data point]
}]
};
chart.setOption(option); // setOption ALWAYS comes after event registration
});
</script>
</body>
</html>
设计质量规则:
- 标题字体:
fontWeight: 'bold'、fontSize: 20–24—— 不使用细体标题 - 网格线:低不透明度(0.06–0.10)——弱化网格线,突出数据
- 柱形:
barMaxWidth: 60,通过itemStyle.borderRadius: [4,4,0,0]设置圆角 - 折线:使用
smooth: true绘制自然曲线,点的symbolSize: 8 - 饼图/环形图:使用
label.formatter: '{b}\n{d}%'显示内置扇区标签 - 深色预设:网格使用
rgba(255,255,255,0.07),坐标轴线/刻度颜色使用rgba(255,255,255,0.15) - Tooltip:
show: false—— 静态 PNG,无悬停交互 - 未提供标题时:跳过
.chart-header,并从 ECharts option 中省略标题
第 4 步:自检(在第 5 步前修复所有问题)
结构:
- 容器是
<div>,不是<canvas> - 在
document.fonts.ready之前声明window.__chartReady = false - 在
chart.setOption()之前注册chart.on('finished', ...) - 在
chart.setOption()之前注册chart.on('rendered', ...)防抖回退处理程序 - option 对象中设置
animation: false -
chart.setOption(option)是初始化代码块中的最后一次调用
特定类型:
- 面积图:
type: 'line'+areaStyle: {}—— 不使用type: 'area' - 环形图:
type: 'pie'+radius: ['40%', '70%']—— 不使用type: 'doughnut' - 柱状图:显式设置
xAxis.type: 'category' - 类目数据放在
xAxis.data中(而不是data.labels) - 饼图/环形图:主体
padding: 64px 80px+.chart-container { max-height: 860px } - 高亮:在数据数组中使用单项
{ value: N, itemStyle: { color } }
设计:
- 所有调色板颜色均来自
references/style-presets.md - 标题说明洞见(不只是主题)
-
tooltip: { show: false }或省略(静态 PNG 无悬停交互) - 深色预设:网格/坐标轴颜色使用
rgba(255,255,255,...) - 如果提供了
source参数,则在页脚注明来源 - 数据标签可见(在系列上启用内置
label: { show: true })
第 5 步:导出
根据标题或“图表类型 + 数据背景”确定 slug(kebab-case,≤30 个字符):
mkdir -p chart/[slug]
保存 HTML:chart/[slug]/chart.html
快速检查浏览器:
open chart/[slug]/chart.html
运行导出(将 [skill-root] 替换为此技能目录的实际路径):
bash [skill-root]/scripts/export-chart.sh \
chart/[slug]/chart.html \
chart/[slug]/chart.png \
--width [W] \
--height [H]
脚本首次运行时会安装 Playwright(下载约 200MB 的 Chromium),随后以 deviceScaleFactor: 2 截取图表。
第 6 步:输出摘要
## Chart: [title]
Date: [YYYY-MM-DD] | Type: [chart_type] | Style: [style]
Dimensions: [W×H]px → PNG: [2W×2H]px @2× retina
Files
Source: chart/[slug]/chart.html
Output: chart/[slug]/chart.png
Size: [X] KB
Checklist
- [ ] Title states the insight clearly
- [ ] Data labels legible at display size
- [ ] Highlight visible on correct data point
- [ ] Source attribution present in footer
提示词技巧(用户寻求指导时展示)
“提供结构化数据——使用 JSON 或 CSV,而非文字描述。”
“明确指定图表类型。写‘对比第 1 季度至第 4 季度的柱状图’,不要写‘展示各季度的图表’。”
“说明数据所表达的故事。‘突出表现优于其他季度的第 4 季度’能提供注释所需的背景。”
✅ 好:“创建折线图。标题:‘12 个月内 ARR 从 1.2 万美元增至 9.5 万美元’。数据:[12, 18, 22, 25, 31, 38, 44, 52, 61, 68, 78, 95](2024 年 1 月至 12 月)。突出显示 12 月。来源:内部 CRM。风格:electric-burst。”
❌ 不好:“制作一张展示公司增长的图表”