使用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
相关产品推荐
相关产品推荐

