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

用D3+Next.js从MongoDB取数绘折线图遇NaN错误

D3.js折线图报错:<path> attribute d: Expected number, "MNaN,450LNaN,0"

你遇到的错误是因为D3生成路径时拿到了NaN值,结合你的代码和排查信息,核心问题大概率出在日期格式不兼容上,而非avgPace的读取方式。

问题根源

d3.scaleTime()要求输入的是标准的Date对象,但如果你的原始data中date字段是字符串(比如"2024-05-01")或者其他非Date类型,x比例尺无法将其映射为有效数值,导致x(d.date)返回NaN,最终路径生成失败。

另外你的代码中还有一处冗余逻辑:创建line时先设置了.y(d => y(d.avgPace)),但在生成路径时又重新覆盖了y轴映射,虽然这不会直接导致错误,但可以简化。

修复步骤

  1. 转换日期为Date对象
    在处理chartData时,将字符串日期转为Date实例:

    const chartData = data.map(item => ({
      date: new Date(item.date), // 关键转换
      avgPace: item.avgPace,
    }))
    
  2. 验证比例尺有效性
    可以在代码中添加日志,确认x轴的domain是否为有效日期范围:

    console.log('X轴定义域:', x.domain()) // 应该输出两个Date对象
    console.log('Y轴定义域:', y.domain()) // 应该输出avgPace的最小/最大值
    
  3. 简化line生成逻辑(可选)
    既然在路径生成时会动态设置y轴映射,初始line可以只保留x轴配置:

    const line = d3.line()
      .x(d => x(d.date)) // 去掉多余的.y配置
    

修复后的完整代码示例

const RunGraph = ({ data }) => {
  const svgRef = useRef()

  useEffect(() => {
    // 关键:将日期转为Date对象
    const chartData = data.map(item => ({
      date: new Date(item.date),
      avgPace: item.avgPace,
    }))

    const margin = { top: 20, right: 20, bottom: 30, left: 50 }
    const width = 960 - margin.left - margin.right
    const height = 500 - margin.top - margin.bottom

    const svg = d3.select(svgRef.current)
    // 清空旧内容(防止多次渲染叠加)
    svg.selectAll('*').remove()

    const x = d3.scaleTime()
      .range([0, width])
      .domain(d3.extent(chartData, d => d.date))
      .nice()

    const y = d3.scaleLinear()
      .range([height, 0])
      .nice()

    const values = Object.keys(chartData[0]).filter(key => key !== "date");

    y.domain([
      d3.min(chartData, d => d3.min(values, value => d[value])),
      d3.max(chartData, d => d3.max(values, value => d[value]))
    ])

    // 简化line配置
    const line = d3.line()
      .x(d => x(d.date))

    const paths = values?.map(value => {
      return {
        value: value,
        path: svg
          .append('path')
          .datum(chartData)
          .attr('fill', 'none')
          .attr('stroke', 'steelBlue')
          .attr('stroke-width', 1.5)
          .attr('d', line.y(d => y(d[value])))
      }
    })

    svg
      .append('g')
      .attr('transform', `translate(${margin.left}, ${height + margin.top})`) // 修正偏移,包含margin
      .call(d3.axisBottom(x))

    svg.append('g')
      .attr('transform', `translate(${margin.left}, ${margin.top})`) // 修正偏移
      .call(d3.axisLeft(y))

  }, [data])

  return (
    <svg ref={svgRef} width={960} height={500}>
    </svg>
  )
}

export default RunGraph

额外补充:修复了svg的宽高设置和坐标轴的偏移计算,避免图表被margin截断。

内容的提问来源于stack exchange,提问作者Lofton Gentry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 12:05:28