HTML骨架
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://d3js.org/d3.v7.min.js"></script> <style> .line-path { fill: none; stroke: #2196F3; stroke-width: 2px; } .axis-label { font-size: 12px; fill: #666; } .tooltip { position: absolute; padding: 8px; background: rgba(0,0,0,0.8); color: white; border-radius: 4px; pointer-events: none; } </style> </head> <body> <div id="chart-container"></div> <script> // D3代码将在此处编写 </script> </body> </html>
假设使用时间序列数据,需确保时间格式正确解析:
const dataset = [ { date: "2025-01", value: 30 }, { date: "2025-02", value: 80 }, { date: "2025-03", value: 45 }, // 更多数据... ]; // 时间解析器 const parseTime = d3.timeParse("%Y-%m"); dataset.forEach(d => { d.date = parseTime(d.date); d.value = +d.value; // 确保数值类型 });
容器尺寸与边距
const margin = { top: 20, right: 30, bottom: 40, left: 50 }; const width = 800 - margin.left - margin.right; const height = 400 - margin.top - margin.bottom; const svg = d3.select("#chart-container") .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})`);
比例尺定义
const xScale = d3.scaleTime() .domain(d3.extent(dataset, d => d.date)) .range([0, width]); const yScale = d3.scaleLinear() .domain([0, d3.max(dataset, d => d.value)]) .range([height, 0]);
生成坐标轴
// X轴 svg.append("g") .attr("class", "x-axis") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(xScale).tickFormat(d3.timeFormat("%Y-%m"))); // Y轴 svg.append("g") .attr("class", "y-axis") .call(d3.axisLeft(yScale)); // 网格线(可选) svg.append("g") .attr("class", "grid") .call(d3.axisLeft(yScale) .tickSize(-width) .tickFormat("") );
折线生成器
const lineGenerator = d3.line() .x(d => xScale(d.date)) .y(d => yScale(d.value)) .curve(d3.curveMonotoneX); // 平滑曲线 svg.append("path") .datum(dataset) .attr("class", "line-path") .attr("d", lineGenerator);
添加数据点
svg.selectAll(".data-point") .data(dataset) .enter() .append("circle") .attr("class", "data-point") .attr("cx", d => xScale(d.date)) .attr("cy", d => yScale(d.value)) .attr("r", 4) .attr("fill", "#2196F3");
提示框交互
const tooltip = d3.select("body").append("div").attr("class", "tooltip"); svg.selectAll(".data-point") .on("mouseover", (event, d) => { tooltip .style("opacity", 1) .html(`日期:${d3.timeFormat("%Y-%m")(d.date)}<br>数值:${d.value}`) .style("left", (event.pageX + 10) + "px") .style("top", (event.pageY - 28) + "px"); }) .on("mouseout", () => tooltip.style("opacity", 0));
添加窗口大小监听,实现自适应:
window.addEventListener("resize", () => { const newWidth = document.getElementById("chart-container").clientWidth - margin.left - margin.right; xScale.range([0, newWidth]); svg.selectAll(".x-axis").call(d3.axisBottom(xScale)); svg.select(".line-path").attr("d", lineGenerator); svg.selectAll(".data-point").attr("cx", d => xScale(d.date)); });