303 lines
9.3 KiB
HTML

<!--
Component: chart-js
Chart.js 集成参考 — 适合复杂图表(多系列/需交互/大数据量)
Agent 参考此文件,在 slide--chart 的 .chart-container 中生成 canvas + 初始化脚本
前提:在 HTML 底部取消注释 Chart.js CDN:
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
⚡ 延迟初始化(核心模式):
Chart.js 动画在 new Chart() 时立即播放。如果图表不在第一页,
用户翻到时动画已结束,看到的是静态图表。
解决方案:通过 slideAnimations 注册表,在 Slide 激活时才创建 Chart 实例。
-->
<!-- ============================================================
核心:延迟初始化工具函数
Agent 必须将此函数放在 Chart.js CDN 之后、所有图表注册之前
============================================================ -->
<script>
/**
* 延迟创建 Chart.js 实例 — 在 Slide 切换到目标页时才执行 new Chart()
* @param {string} canvasId - canvas 元素的 id
* @param {object} config - Chart.js 完整配置对象
* @returns {function} 注册到 slideAnimations 的回调函数
*
* 用法(在 slideAnimations 注册表中):
* const slideAnimations = {
* 3: createChartLazy('barChart', { type: 'bar', data: {...}, options: {...} }),
* };
*/
function createChartLazy(canvasId, config) {
let instance = null;
return function () {
const canvas = document.getElementById(canvasId);
if (!canvas) return;
// 防止重复创建(用户前后翻页时)
if (instance) {
instance.destroy();
instance = null;
}
// ❌ 禁止 canvas.width = canvas.width — 会将 canvas 内在分辨率重置为 300×150,
// Chart.js responsive 模式检测到尺寸不匹配后二次渲染,导致"双重图表"抖动
instance = new Chart(canvas, config);
};
}
</script>
<!-- ============================================================
示例 1:柱状图(Bar Chart)— 延迟初始化版
============================================================ -->
<div class="chart-container">
<canvas id="barChart"></canvas>
</div>
<!--
Agent 在 slideAnimations 注册表中注册此图表:
const slideAnimations = {
3: createChartLazy('barChart', {
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [{
label: '营收(万元)',
data: [120, 190, 300, 250],
backgroundColor: '#2563eb',
borderRadius: 6
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 1000, easing: 'easeOutQuart' },
plugins: {
legend: { display: false },
tooltip: { enabled: true }
},
scales: {
y: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.06)' } },
x: { grid: { display: false } }
}
}
}),
};
-->
<!-- ============================================================
示例 2:折线图(Line Chart)— 延迟初始化版
============================================================ -->
<div class="chart-container">
<canvas id="lineChart"></canvas>
</div>
<!--
slideAnimations 注册:
N: createChartLazy('lineChart', {
type: 'line',
data: {
labels: ['1月', '2月', '3月', '4月', '5月', '6月'],
datasets: [
{
label: '2024',
data: [65, 78, 90, 81, 95, 110],
borderColor: '#1d4ed8',
backgroundColor: 'rgba(29,78,216,0.1)',
fill: true,
tension: 0.3,
pointRadius: 4
},
{
label: '2023',
data: [45, 52, 60, 55, 70, 80],
borderColor: '#94a3b8',
backgroundColor: 'transparent',
borderDash: [5, 5],
tension: 0.3,
pointRadius: 3
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 1200, easing: 'easeOutQuart' },
plugins: {
legend: { position: 'top', align: 'end' }
},
scales: {
y: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.06)' } },
x: { grid: { display: false } }
}
}
}),
-->
<!-- ============================================================
示例 3:饼图(Doughnut Chart)— 延迟初始化版
============================================================ -->
<div class="chart-container">
<canvas id="doughnutChart"></canvas>
</div>
<!--
slideAnimations 注册:
N: createChartLazy('doughnutChart', {
type: 'doughnut',
data: {
labels: ['移动端', '桌面端', '平板', '其他'],
datasets: [{
data: [55, 30, 10, 5],
backgroundColor: ['#1d4ed8', '#0ea5e9', '#64748b', '#cbd5e1'],
borderWidth: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 1000, easing: 'easeOutQuart' },
cutout: '60%',
plugins: {
legend: { position: 'right' }
}
}
}),
-->
<!-- ============================================================
示例 4:雷达图(Radar Chart)— 延迟初始化版
============================================================ -->
<div class="chart-container">
<canvas id="radarChart"></canvas>
</div>
<!--
slideAnimations 注册:
N: createChartLazy('radarChart', {
type: 'radar',
data: {
labels: ['性能', '安全', '可用性', '成本', '扩展性', '维护性'],
datasets: [
{
label: '方案 A',
data: [90, 75, 85, 60, 80, 70],
borderColor: '#1d4ed8',
backgroundColor: 'rgba(29,78,216,0.15)',
pointRadius: 4
},
{
label: '方案 B',
data: [70, 90, 60, 85, 65, 80],
borderColor: '#f59e0b',
backgroundColor: 'rgba(245,158,11,0.15)',
pointRadius: 4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 1000, easing: 'easeOutQuart' },
scales: {
r: { beginAtZero: true, max: 100, ticks: { stepSize: 25 } }
}
}
}),
-->
<!--
================================================================
重要提醒(Agent 必须注意)
================================================================
1. 延迟初始化(必须):
- 所有图表必须通过 createChartLazy() 注册到 slideAnimations
- 不要直接在页面底部写 new Chart(),否则动画在页面加载时就播放完了
- createChartLazy() 内置防重复创建,用户前后翻页不会报错
2. 颜色注入(必须):
- Chart.js 不支持 CSS 变量作为颜色值
- Agent 必须从所选主题中提取实际色值注入
- 浅色主题网格色:'rgba(0,0,0,0.06)'
- 深色主题网格色:'rgba(255,255,255,0.08)'
- 深色主题 tick/label 色:需显式设置 color 属性
3. 深色主题适配清单:
scales.x.ticks.color: '主题的 --color-text-secondary 实际色值'
scales.y.ticks.color: '同上'
scales.x.grid.color: 'rgba(255,255,255,0.08)'
scales.y.grid.color: 'rgba(255,255,255,0.08)'
plugins.legend.labels.color: '主题的 --color-text 实际色值'
4. 每个 canvas 需要唯一的 id
5. 设置 responsive: true + maintainAspectRatio: false 让图表适配容器
6. 建议关闭不需要的 plugin(legend/title),保持 Slide 简洁
7. 🔴 Canvas CSS 禁令:
- 禁止给 canvas 设置 CSS width/height(尤其是 !important)
- Chart.js responsive 模式通过 ResizeObserver 管理 canvas 尺寸,
外部 CSS 强制尺寸会与 ResizeObserver 互相打架,导致:
· 图表加载时抖动/从中心扩张
· hover tooltip 时图表再次扩张
- chart.html 模板已移除 canvas 的 CSS 尺寸覆盖,Agent 不要手动加回
8. 🔴 Tooltip 交互必须保留:
- 禁止设置 plugins.tooltip.enabled: false 或 interaction.mode: 'none'
- Chart.js 默认启用 tooltip,不要主动关闭
- 用户需要 hover 节点看到具体数值,这是基本交互预期
- 可以自定义 tooltip 的样式(backgroundColor/borderColor 等),但不能禁用
================================================================
深色主题完整示例(以 cyber-dark 为例)
================================================================
N: createChartLazy('barChartDark', {
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [{
label: '营收',
data: [120, 190, 300, 250],
backgroundColor: '#818cf8',
borderRadius: 6
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: { duration: 1000, easing: 'easeOutQuart' },
plugins: {
legend: {
display: false
},
tooltip: {
backgroundColor: '#1a2035',
titleColor: '#f0f4ff',
bodyColor: '#8b95ad',
borderColor: '#2a3150',
borderWidth: 1
}
},
scales: {
y: {
beginAtZero: true,
grid: { color: 'rgba(255,255,255,0.08)' },
ticks: { color: '#8b95ad' }
},
x: {
grid: { display: false },
ticks: { color: '#8b95ad' }
}
}
}
}),
-->