题目
使用Canvas或SVG实现可交互的柱状图
信息
- 类型:问答
- 难度:⭐⭐
考点
Canvas/SVG绘图基础,事件处理,数据可视化基础
快速回答
实现要点:
- 绘图选择:Canvas适合动态数据,SVG适合需要DOM交互的场景
- 坐标计算:根据数据动态计算柱体位置和高度
- 交互实现:Canvas用isPointInPath检测点击,SVG直接绑定事件
- 性能优化:大数据集用Canvas,复杂交互用SVG
需求说明
实现一个柱状图,要求:
1. 根据数据数组动态渲染柱体
2. 鼠标悬停时高亮柱体并显示数值
3. 点击柱体触发回调函数
技术方案对比
| 特性 | Canvas | SVG |
|---|---|---|
| 渲染方式 | 像素位图 | 矢量DOM元素 |
| 事件绑定 | 需手动计算坐标 | 直接绑定DOM事件 |
| 性能优势 | 大数据量渲染快 | 少量元素交互性好 |
Canvas实现核心代码
// 绘制柱体
function drawBars(data) {
ctx.clearRect(0, 0, width, height);
data.forEach((item, i) => {
const barWidth = 40;
const barHeight = item.value * 2;
const x = 50 + i * 60;
const y = height - barHeight;
ctx.fillStyle = item.active ? '#ff6b6b' : '#4ecdc4';
ctx.fillRect(x, y, barWidth, barHeight);
// 存储柱体位置用于点击检测
bars[i] = { x, y, width: barWidth, height: barHeight };
});
}
// 点击检测
canvas.addEventListener('click', e => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
bars.forEach((bar, i) => {
if (x > bar.x && x < bar.x + bar.width &&
y > bar.y && y < bar.y + bar.height) {
console.log('Clicked:', data[i]);
}
});
});SVG实现核心代码
最佳实践
- 性能优化:Canvas动画用requestAnimationFrame,避免频繁重绘
- 响应式处理:监听resize事件重新计算坐标
- 辅助库推荐:大数据用ECharts(Canvas),定制交互用D3(SVG)
常见错误
- Canvas未清除画布导致重影(忘记调用clearRect)
- 坐标系统混淆(Y轴从顶部开始计算)
- SVG元素未设置viewBox导致拉伸失真
- 事件委托处理不当导致性能问题
扩展知识
- Canvas分层:将静态元素和动态元素分在不同canvas提升性能
- SVG复用:使用<defs>定义可复用图形元素
- 混合方案:用SVG做UI控件覆盖在Canvas上方实现复杂交互