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

基于D3.js的趋势线图表定制:添加轴线、标记点及告警图标

折线图功能增强需求

需求概述

我现有一个折线图表,希望完成以下改造:

  • 样式调整
  • 创建趋势线
  • 添加展示当前时间的轴线及趋势线/圆形标记点
  • 在图表顶部生成与图表刻度对齐的图标/告警,并与图表集成

参考示例图

示例图1
示例图2
示例图3

现有实现资源

当前React+D3.js实现代码

import React from 'react'
import * as d3 from 'd3'
import './LineChart.scss'

class LineChart extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
    this.state = {
      data: this.props.data ? this.props.data : [],
      theme: this.props.theme
        ? this.props.theme
        : ['#bde0fe', '#2698f9', '#71bcfd', '#f1f8fe'],
    }
  }

  componentWillReceiveProps(nextProps) {
    // You don't have to do this check first, but it can help prevent an unneeded render
    if (nextProps.data !== this.state.data) {
      //console.log("PROPS HAVE CHANGED FOR CHART");
      this.setState({data: nextProps.data})
      this.buildChart()
    }
  }

  componentDidMount() {
    this.buildChart()
  }

  buildChart() {
    var $this = this.myRef.current

    d3.select($this).selectAll('svg').remove()

    var data = this.state.data

    //console.log("DATA", data)

    var hasLabels = false

    data.forEach((item, index) => {
      if (item.id.length > 0) {
        hasLabels = true
      }
    })

    var yAxisLabel = this.props.yAxisLabel
    //console.log("hasLabels", hasLabels)

    var width = parseInt(this.props.width, 10),
      height = parseInt(this.props.height, 10)

    var color = d3.scaleOrdinal().range(this.state.theme)

    var parseTime = d3.timeParse('%Y%m%d')

    var margin = {
        top: 20,
        right: hasLabels ? 95 : 25,
        bottom: 30,
        left: 45,
      },
      w = width - margin.left - margin.right,
      h = height - margin.top - margin.bottom

    var x = d3.scaleTime().range([0, w])
    var y = d3.scaleLinear().range([h, 0])

    var valueline = d3
      .line()
      .curve(d3.curveBasis)
      .x(function (d) {
        return x(new Date(d.date))
      })
      .y(function (d) {
        return y(d.value)
      })

    //valueline.curve(d3.curveBasis)

    x.domain([
      d3.min(data, function (c) {
        return d3.min(c.values, function (d) {
          return new Date(d.date)
        })
      }),
      d3.max(data, function (c) {
        return d3.max(c.values, function (d) {
          return new Date(d.date)
        })
      }),
    ])

    y.domain([
      d3.min(data, function (c) {
        return d3.min(c.values, function (d) {
          return d.value
        })
      }),
      d3.max(data, function (c) {
        return d3.max(c.values, function (d) {
          return d.value
        })
      }),
    ])

    var svg = d3
      .select($this)
      .append('svg')
      .attr('width', w + margin.left + margin.right)
      .attr('height', h + margin.top + margin.bottom)
      .append('g')
      .attr('class', 'linechart')
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')

    var lines = svg.append('g').attr('class', 'lines')

    var line = lines
      .selectAll('.line')
      .data(data)
      .enter()
      .append('g')
      .attr('class', 'line')

    line
      .append('path')
      .attr('d', function (d) {
        return valueline(d.values)
      })
      .attr('stroke', function (d, i) {
        return color(i)
      })

    line
      .append('text')
      .datum(function (d) {
        return {
          id: d.id,
          value: d.values[d.values.length - 1],
        }
      })
      .attr('transform', function (d) {
        return (
          'translate(' +
          x(new Date(d.value.date)) +
          ',' +
          y(d.value.value) +
          ')'
        )
      })
      .attr('x', 3)
      .attr('dy', '0.35em')
      .text(function (d) {
        return d.id
      })

    ////return c.date;
    var minDate = d3.min(data, function (c) {
      return d3.min(c.values, function (d) {
        return d.date
      })
    })
    var maxDate = d3.max(data, function (c) {
      return d3.max(c.values, function (d) {
        return d.date
      })
    })

    //console.log("minDate", minDate);
    //console.log("maxDate", maxDate);

    var difference = dateDiffInDays(
      new Date(minDate.toString()),
      new Date(maxDate.toString()),
    )
    //console.log(difference + ' days')

    let tick = d3.timeDay.every(1)
    if (difference < 1) {
      tick = d3.timeHour.every(2)
    }
    if (difference > 1 && difference < 7) {
      tick = d3.timeDay.every(1)
    }
    if (difference > 7 && difference < 30) {
      tick = d3.timeWeek.every(1)
    }
    if (difference >= 30 && difference < 90) {
      //console.log("jackpot")
      tick = d3.timeMonth.every(1)
    }

    lines
      .append('g')
      .attr('transform', 'translate(0,' + h + ')')
      .call(d3.axisBottom(x).ticks(tick))

    lines
      .append('g')
      .call(d3.axisLeft(y))
      .append('text')
      .attr('class', 'ylabel')
      .attr('transform', 'rotate(-90)')
      .attr('y', 6)
      .attr('dy', '0.71em')
      .text(yAxisLabel)

    function dateDiffInDays(a, b) {
      const _MS_PER_DAY = 1000 * 60 * 60 * 24
      // Discard the time and time-zone information.
      const utc1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate())
      const utc2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate())

      return Math.floor((utc2 - utc1) / _MS_PER_DAY)
    }
  }

  render() {
    return <div ref={this.myRef} className="LineChart" />
  }
}
export default LineChart

内容的提问来源于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.07.06 05:47:04