You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求基于D3.js的Canvas堆叠图表实现示例

D3.js Canvas 堆叠图表示例

为什么Canvas版本示例少见?

D3最初的设计更偏向SVG的声明式DOM操作,SVG元素可直接与数据绑定,上手门槛更低,社区示例也更丰富。而Canvas是命令式绘制,需要手动编写绘制逻辑,无法直接复用SVG版本的DOM绑定逻辑,所以相关示例相对稀缺。

完整可运行示例

下面是基于D3.js v7的Canvas堆叠图表示例,包含数据处理、比例尺定义、堆叠区域绘制和坐标轴渲染:

// 1. 模拟堆叠数据
const dataset = [
  { year: "2018", A: 30, B: 40, C: 20 },
  { year: "2019", A: 35, B: 45, C: 25 },
  { year: "2020", A: 40, B: 50, C: 30 },
  { year: "2021", A: 45, B: 55, C: 35 },
  { year: "2022", A: 50, B: 60, C: 40 }
];

// 2. 设置Canvas基础参数
const width = 600;
const height = 400;
const margin = { top: 20, right: 20, bottom: 50, left: 50 };
const canvas = document.getElementById("stacked-canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");

// 3. 处理堆叠数据
const keys = ["A", "B", "C"];
const stack = d3.stack().keys(keys);
const stackedData = stack(dataset);

// 4. 定义比例尺
const xScale = d3.scaleBand()
  .domain(dataset.map(d => d.year))
  .range([margin.left, width - margin.right])
  .padding(0.2);

const yScale = d3.scaleLinear()
  .domain([0, d3.max(stackedData, d => d3.max(d, d => d[1]))])
  .range([height - margin.bottom, margin.top]);

// 颜色比例尺
const colorScale = d3.scaleOrdinal()
  .domain(keys)
  .range(["#FF6B6B", "#4ECDC4", "#FFE66D"]);

// 5. 绘制堆叠柱形
stackedData.forEach((layer, index) => {
  ctx.fillStyle = colorScale(keys[index]);
  layer.forEach(d => {
    const x = xScale(d.data.year);
    const barWidth = xScale.bandwidth();
    const y = yScale(d[1]);
    const barHeight = yScale(d[0]) - yScale(d[1]);
    
    ctx.fillRect(x, y, barWidth, barHeight);
    // 可选:添加柱形边框
    ctx.strokeStyle = "#fff";
    ctx.strokeRect(x, y, barWidth, barHeight);
  });
});

// 6. 绘制坐标轴(利用临时SVG生成轴元素,再转绘到Canvas)
// X轴
const xAxis = d3.axisBottom(xScale);
const tempSvg = d3.create("svg");
const xAxisGroup = tempSvg.append("g").call(xAxis);
// 绘制X轴路径
const xPath = xAxisGroup.select("path").attr("d");
ctx.beginPath();
new Path2D(xPath).forEach(([cmd, ...args]) => {
  if (cmd === "M") ctx.moveTo(...args);
  else if (cmd === "L") ctx.lineTo(...args);
});
ctx.strokeStyle = "#333";
ctx.stroke();
// 绘制X轴刻度与文本
xAxisGroup.selectAll("g").forEach(g => {
  const tick = g.select("line");
  const text = g.select("text");
  const x = parseFloat(tick.attr("x1"));
  const y1 = parseFloat(tick.attr("y1"));
  const y2 = parseFloat(tick.attr("y2"));
  // 刻度线
  ctx.beginPath();
  ctx.moveTo(x, y1 + height - margin.bottom);
  ctx.lineTo(x, y2 + height - margin.bottom);
  ctx.strokeStyle = "#333";
  ctx.stroke();
  // 刻度文本
  ctx.fillStyle = "#333";
  ctx.font = "12px sans-serif";
  ctx.textAlign = "center";
  ctx.fillText(text.text(), x, y2 + height - margin.bottom + 15);
});

// Y轴
const yAxis = d3.axisLeft(yScale).ticks(5);
const yAxisGroup = tempSvg.append("g").call(yAxis);
const yPath = yAxisGroup.select("path").attr("d");
ctx.beginPath();
new Path2D(yPath).forEach(([cmd, ...args]) => {
  if (cmd === "M") ctx.moveTo(args[0] + margin.left, args[1]);
  else if (cmd === "L") ctx.lineTo(args[0] + margin.left, args[1]);
});
ctx.strokeStyle = "#333";
ctx.stroke();
// 绘制Y轴刻度与文本
yAxisGroup.selectAll("g").forEach(g => {
  const tick = g.select("line");
  const text = g.select("text");
  const x1 = parseFloat(tick.attr("x1"));
  const x2 = parseFloat(tick.attr("x2"));
  const y = parseFloat(tick.attr("y1"));
  // 刻度线
  ctx.beginPath();
  ctx.moveTo(x1 + margin.left, y);
  ctx.lineTo(x2 + margin.left, y);
  ctx.strokeStyle = "#333";
  ctx.stroke();
  // 刻度文本
  ctx.fillStyle = "#333";
  ctx.font = "12px sans-serif";
  ctx.textAlign = "right";
  ctx.fillText(text.text(), x1 + margin.left - 5, y + 4);
});

// 7. 添加图例
const legendX = width - margin.right - 80;
const legendY = margin.top;
keys.forEach((key, index) => {
  ctx.fillStyle = colorScale(key);
  ctx.fillRect(legendX, legendY + index * 20, 15, 15);
  ctx.fillStyle = "#333";
  ctx.font = "12px sans-serif";
  ctx.textAlign = "left";
  ctx.fillText(key, legendX + 20, legendY + index * 20 + 12);
});

使用说明

  1. 在HTML中添加Canvas元素:<canvas id="stacked-canvas"></canvas>
  2. 引入D3.js库:<script src="https://d3js.org/d3.v7.min.js"></script>(本地环境可下载后引入)
  3. 将上述代码放在DOM加载完成的回调中,或HTML页面底部

内容的提问来源于stack exchange,提问作者Aris381

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 23:14:55