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

使用D3.js制作两种格式的多线图:X1-Y1-X2-Y2与X1-Y1-Y2-Y3

下面是使用D3.js实现两种多线图的具体代码,参考目标图表的双轴/多轴样式设计:

一、双X双Y轴多线图(X1-Y1-X2-Y2)

这种图表包含两组独立的坐标轴(X1-Y1、X2-Y2),对应两条独立折线。

实现代码

// 1. 设置画布尺寸与边距
const margin = {top: 20, right: 120, bottom: 50, left: 50};
const width = 800 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;

// 创建SVG容器
const svg = d3.select("body")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", `translate(${margin.left},${margin.top})`);

// 2. 定义比例尺
// X1轴(底部)与Y1轴(左侧)
const x1Scale = d3.scaleLinear()
  .domain([d3.min(data1, d => d.x1), d3.max(data1, d => d.x1)])
  .range([0, width]);

const y1Scale = d3.scaleLinear()
  .domain([d3.min(data1, d => d.y1), d3.max(data1, d => d.y1)])
  .range([height, 0]);

// X2轴(顶部)与Y2轴(右侧)
const x2Scale = d3.scaleLinear()
  .domain([d3.min(data1, d => d.x2), d3.max(data1, d => d.x2)])
  .range([0, width]);

const y2Scale = d3.scaleLinear()
  .domain([d3.min(data1, d => d.y2), d3.max(data1, d => d.y2)])
  .range([height, 0]);

// 3. 创建坐标轴
// X1轴(底部)
svg.append("g")
  .attr("transform", `translate(0,${height})`)
  .call(d3.axisBottom(x1Scale))
  .append("text")
  .attr("x", width/2)
  .attr("y", 40)
  .style("text-anchor", "middle")
  .text("X1 轴");

// Y1轴(左侧)
svg.append("g")
  .call(d3.axisLeft(y1Scale))
  .append("text")
  .attr("transform", "rotate(-90)")
  .attr("x", -height/2)
  .attr("y", -40)
  .style("text-anchor", "middle")
  .text("Y1 轴");

// X2轴(顶部)
svg.append("g")
  .call(d3.axisTop(x2Scale))
  .append("text")
  .attr("x", width/2)
  .attr("y", -20)
  .style("text-anchor", "middle")
  .text("X2 轴");

// Y2轴(右侧)
svg.append("g")
  .attr("transform", `translate(${width},0)`)
  .call(d3.axisRight(y2Scale))
  .append("text")
  .attr("transform", "rotate(-90)")
  .attr("x", height/2)
  .attr("y", 40)
  .style("text-anchor", "middle")
  .text("Y2 轴");

// 4. 定义折线生成器
const line1 = d3.line()
  .x(d => x1Scale(d.x1))
  .y(d => y1Scale(d.y1));

const line2 = d3.line()
  .x(d => x2Scale(d.x2))
  .y(d => y2Scale(d.y2));

// 5. 绘制折线
svg.append("path")
  .datum(data1)
  .attr("fill", "none")
  .attr("stroke", "#2E86AB")
  .attr("stroke-width", 2)
  .attr("d", line1);

svg.append("path")
  .datum(data1)
  .attr("fill", "none")
  .attr("stroke", "#F24C4C")
  .attr("stroke-width", 2)
  .attr("d", line2);

// 6. 添加数据点
// Y1数据点
svg.selectAll(".dot1")
  .data(data1)
  .enter()
  .append("circle")
  .attr("class", "dot1")
  .attr("cx", d => x1Scale(d.x1))
  .attr("cy", d => y1Scale(d.y1))
  .attr("r", 4)
  .attr("fill", "#2E86AB");

// Y2数据点
svg.selectAll(".dot2")
  .data(data1)
  .enter()
  .append("circle")
  .attr("class", "dot2")
  .attr("cx", d => x2Scale(d.x2))
  .attr("cy", d => y2Scale(d.y2))
  .attr("r", 4)
  .attr("fill", "#F24C4C");
二、单X三Y轴多线图(X1-Y1-Y2-Y3)

这种图表共享一个X轴,搭配三个独立Y轴,对应三条折线。

实现代码

// 1. 设置画布尺寸与边距(右侧预留更多空间容纳三个Y轴)
const margin = {top: 20, right: 180, bottom: 50, left: 50};
const width = 800 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;

// 创建SVG容器
const svg = d3.select("body")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", `translate(${margin.left},${margin.top})`);

// 2. 定义比例尺
// 共享X轴(底部)
const xScale = d3.scaleLinear()
  .domain([d3.min(data2, d => d.x1), d3.max(data2, d => d.x1)])
  .range([0, width]);

// 三个Y轴:左侧Y1,右侧Y2、Y3
const y1Scale = d3.scaleLinear()
  .domain([d3.min(data2, d => d.y1), d3.max(data2, d => d.y1)])
  .range([height, 0]);

const y2Scale = d3.scaleLinear()
  .domain([d3.min(data2, d => d.y2), d3.max(data2, d => d.y2)])
  .range([height, 0]);

const y3Scale = d3.scaleLinear()
  .domain([d3.min(data2, d => d.y3), d3.max(data2, d => d.y3)])
  .range([height, 0]);

// 3. 创建坐标轴
// X轴(底部)
svg.append("g")
  .attr("transform", `translate(0,${height})`)
  .call(d3.axisBottom(xScale))
  .append("text")
  .attr("x", width/2)
  .attr("y", 40)
  .style("text-anchor", "middle")
  .text("X1 轴");

// Y1轴(左侧)
svg.append("g")
  .call(d3.axisLeft(y1Scale))
  .append("text")
  .attr("transform", "rotate(-90)")
  .attr("x", -height/2)
  .attr("y", -40)
  .style("text-anchor", "middle")
  .text("Y1 轴");

// Y2轴(右侧,偏移60px)
svg.append("g")
  .attr("transform", `translate(${width + 60},0)`)
  .call(d3.axisRight(y2Scale))
  .append("text")
  .attr("transform", "rotate(-90)")
  .attr("x", height/2)
  .attr("y", 60)
  .style("text-anchor", "middle")
  .text("Y2 轴");

// Y3轴(右侧,偏移120px)
svg.append("g")
  .attr("transform", `translate(${width + 120},0)`)
  .call(d3.axisRight(y3Scale))
  .append("text")
  .attr("transform", "rotate(-90)")
  .attr("x", height/2)
  .attr("y", 60)
  .style("text-anchor", "middle")
  .text("Y3 轴");

// 4. 定义折线生成器
const lineY1 = d3.line()
  .x(d => xScale(d.x1))
  .y(d => y1Scale(d.y1));

const lineY2 = d3.line()
  .x(d => xScale(d.x1))
  .y(d => y2Scale(d.y2));

const lineY3 = d3.line()
  .x(d => xScale(d.x1))
  .y(d => y3Scale(d.y3));

// 5. 绘制折线
svg.append("path")
  .datum(data2)
  .attr("fill", "none")
  .attr("stroke", "#2E86AB")
  .attr("stroke-width", 2)
  .attr("d", lineY1);

svg.append("path")
  .datum(data2)
  .attr("fill", "none")
  .attr("stroke", "#F24C4C")
  .attr("stroke-width", 2)
  .attr("d", lineY2);

svg.append("path")
  .datum(data2)
  .attr("fill", "none")
  .attr("stroke", "#F7DC6F")
  .attr("stroke-width", 2)
  .attr("d", lineY3);

// 6. 添加数据点
// Y1数据点
svg.selectAll(".dotY1")
  .data(data2)
  .enter()
  .append("circle")
  .attr("class", "dotY1")
  .attr("cx", d => xScale(d.x1))
  .attr("cy", d => y1Scale(d.y1))
  .attr("r", 4)
  .attr("fill", "#2E86AB");

// Y2数据点
svg.selectAll(".dotY2")
  .data(data2)
  .enter()
  .append("circle")
  .attr("class", "dotY2")
  .attr("cx", d => xScale(d.x1))
  .attr("cy", d => y2Scale(d.y2))
  .attr("r", 4)
  .attr("fill", "#F24C4C");

// Y3数据点
svg.selectAll(".dotY3")
  .data(data2)
  .enter()
  .append("circle")
  .attr("class", "dotY3")
  .attr("cx", d => xScale(d.x1))
  .attr("cy", d => y3Scale(d.y3))
  .attr("r", 4)
  .attr("fill", "#F7DC6F");

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 16:31:18