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

基于D3.js的折线图改造:实现趋势面积图与增强功能

React+D3 折线图改造为趋势面积图实现方案

1. 改造为趋势面积图并添加趋势线

基于现有折线图的D3逻辑,新增面积生成器(d3.area),同时保留原折线作为趋势线:

import React, { useEffect, useRef } from 'react';
import * as d3 from 'd3';

const TrendAreaChart = ({ data }) => {
  const svgRef = useRef();
  
  useEffect(() => {
    const svg = d3.select(svgRef.current);
    svg.selectAll('*').remove(); // 清空重绘

    // 定义尺寸和边距
    const margin = { top: 60, right: 30, bottom: 50, left: 60 };
    const width = 800 - margin.left - margin.right;
    const height = 400 - margin.top - margin.bottom;

    // 构建比例尺
    const xScale = d3.scaleTime()
      .domain(d3.extent(data, d => d.time))
      .range([0, width]);
    
    const yScale = d3.scaleLinear()
      .domain([0, d3.max(data, d => d.value)])
      .range([height, 0]);

    // 定义面积生成器
    const areaGenerator = d3.area()
      .x(d => xScale(d.time))
      .y0(height) // 面积底部对齐y轴0点
      .y1(d => yScale(d.value))
      .curve(d3.curveMonotoneX); // 平滑曲线

    // 定义趋势线生成器(保留原折线逻辑)
    const lineGenerator = d3.line()
      .x(d => xScale(d.time))
      .y(d => yScale(d.value))
      .curve(d3.curveMonotoneX);

    // 添加绘图容器
    const g = svg.append('g')
      .attr('transform', `translate(${margin.left}, ${margin.top})`);

    // 绘制面积
    g.append('path')
      .attr('d', areaGenerator(data))
      .attr('fill', '#8884d8')
      .attr('opacity', 0.3);

    // 绘制趋势线
    g.append('path')
      .attr('d', lineGenerator(data))
      .attr('stroke', '#8884d8')
      .attr('stroke-width', 2)
      .attr('fill', 'none');

    // 添加坐标轴(复用原有逻辑)
    g.append('g')
      .attr('transform', `translate(0, ${height})`)
      .call(d3.axisBottom(xScale));
    
    g.append('g')
      .call(d3.axisLeft(yScale));
  }, [data]);

  return <svg ref={svgRef} width={800} height={400}></svg>;
};

export default TrendAreaChart;

核心要点:用d3.area定义填充区域,设置y0为容器底部,y1为数据值,搭配平滑曲线;趋势线复用原折线的lineGenerator,叠加在面积上方。

2. 添加当前时间轴线与标记

在上述代码基础上,计算当前时间在x轴的位置,添加垂直轴线和圆点标记:

// 在useEffect的g容器创建后添加以下代码
const currentTime = new Date();
const currentX = xScale(currentTime);

// 添加当前时间垂直轴线
g.append('line')
  .attr('x1', currentX)
  .attr('x2', currentX)
  .attr('y1', 0)
  .attr('y2', height)
  .attr('stroke', '#ff7300')
  .attr('stroke-width', 1)
  .attr('stroke-dasharray', '4,4');

// 添加当前时间点标记
const currentDataPoint = data.find(d => 
  Math.abs(d.time.getTime() - currentTime.getTime()) < 3600000 // 匹配最近1小时内的数据点
);
if (currentDataPoint) {
  const currentY = yScale(currentDataPoint.value);
  g.append('circle')
    .attr('cx', currentX)
    .attr('cy', currentY)
    .attr('r', 5)
    .attr('fill', '#ff7300')
    .attr('stroke', '#fff')
    .attr('stroke-width', 2);
}

// 可选:添加当前时间文本标注
g.append('text')
  .attr('x', currentX)
  .attr('y', margin.top - 20)
  .attr('text-anchor', 'middle')
  .attr('fill', '#ff7300')
  .text('当前时间');

核心要点:通过xScale(currentTime)获取当前时间对应的x坐标,绘制虚线轴线;匹配最近的数据点绘制高亮圆点,也可扩展为区域标记(用rect元素)。

3. 顶部匹配刻度的告警组件

利用D3的x轴刻度信息,在React层面渲染顶部告警图标,确保与下方图表刻度对齐:

// 在组件内部添加刻度获取逻辑
const [xTicks, setXTicks] = useState([]);

useEffect(() => {
  if (!data.length) return;
  const xScale = d3.scaleTime()
    .domain(d3.extent(data, d => d.time))
    .range([0, 800 - 60 - 30]); // 与图表宽度一致
  
  // 获取x轴刻度
  const ticks = xScale.ticks();
  setXTicks(ticks);
}, [data]);

// 在svg上方渲染告警组件
return (
  <div style={{ position: 'relative', width: '800px' }}>
    {/* 顶部告警图标栏 */}
    <div style={{ 
      position: 'absolute', 
      top: 10, 
      left: 60, 
      width: 800 - 60 - 30, 
      display: 'flex', 
      justifyContent: 'space-around' 
    }}>
      {xTicks.map((tick, idx) => {
        const tickX = d3.scaleTime()
          .domain(d3.extent(data, d => d.time))
          .range([0, 800 - 60 - 30])(tick);
        // 模拟告警数据,实际根据业务逻辑判断
        const hasAlert = idx % 3 === 0;
        return (
          <div 
            key={idx} 
            style={{ position: 'absolute', left: `${tickX}px` }}
          >
            {hasAlert && <span style={{ color: '#ff4d4f', fontSize: '20px' }}>⚠️</span>}
          </div>
        );
      })}
    </div>
    {/* 图表svg */}
    <svg ref={svgRef} width={800} height={400}></svg>
  </div>
);

核心要点:通过xScale.ticks()获取x轴的刻度时间,计算每个刻度对应的水平位置,在绝对定位的容器中渲染告警图标,确保与下方图表刻度完全对齐。

内容的提问来源于stack exchange,提问作者The Old County

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:23:36