原创

react + d3.js + ts 实现树图 报错解决

1. 结构

interface IGamesDatum {
    name: string;
    popularity: number;
}

interface IDatum {
    name: string;
    children: this | IGamesDatum[];
}

2. 类型优化

2.1 d3.hierarchy()

d3.json<IDatum>("./data/games.json").then((data: IDatum | undefined) => {
    if(data){
        let dataHierarchyNode: d3.HierarchyNode<IDatum> = d3.hierarchy<IDatum>(data);
    }
});

根据指定的层级数据创建一个根节点,类型为 HierarchyNode

2.2 d3.tree()

let root: d3.HierarchyPointNode<IDatum> = d3.tree<IDatum>().size([innerHeight, innerWidth])(dataHierarchyNode);

2.3 d3.linkHorizontal<>()

g.selectAll("path")
    .data(root.links())
    .attr("d", d3.linkHorizontal<d3.HierarchyPointLink<IDatum>, d3.HierarchyPointNode<IDatum>>()
    .x((d: d3.HierarchyPointNode<IDatum>) => d.y)
    .y((d: d3.HierarchyPointNode<IDatum>) => d.x))

3. 代码


import React, { useState, useRef, useEffect } from "react";
import * as d3 from "d3";
interface IGamesDatum {
  name: string;
  popularity: number;
}
interface IDatum {
  name: string;
  children: this | IGamesDatum[];
}
const Tree: React.FC = () => {
  const [width] = useState(1600);
  const [height] = useState(800);
  const [margin] = useState({
    left: 150,
    top: 60,
    right: 50,
    bottom: 50,
  });
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;
  const svg = useRef<SVGSVGElement>(null);
  useEffect(() => {
    let root: d3.HierarchyPointNode<IDatum>;
    const svgSelection = d3.select(svg.current);
    svgSelection.attr("width", width).attr("height", height);
    const g = svgSelection
      .append("g")
      .attr("transform", `translate(${margin.left}, ${margin.right})`);
    let color: any;
    const fill = (d: any) => {
      if (d.depth === 0) return color(d.data.name);
      while (d.depth > 1) d = d.parent;
      return color(d.data.name);
    };
    const render = (data: any) => {
      color = d3.scaleOrdinal(d3.schemeCategory10);
      // .domain(
      //   root
      //     .descendants()
      //     .filter((d) => d.depth <= 1)
      //     .map((d) => d.data.name)
      // )
      // .range(d3.schemeCategory10);
      g.selectAll("path")
        .data(root.links())
        .join("path")
        .attr("fill", "none")
        .attr("stroke", "black")
        .attr("stroke-width", 1.5)
        .attr(
          "d",
          d3
            .linkHorizontal<
              d3.HierarchyPointLink<IDatum>,
              d3.HierarchyPointNode<IDatum>
            >()
            .x((d: d3.HierarchyPointNode<IDatum>) => d.y)
            .y((d: d3.HierarchyPointNode<IDatum>) => d.x)
        )
        .text("1");
      g.selectAll("circle")
        .data(root.descendants())
        .join("circle")
        .attr("cx", (d: any) => d.y)
        .attr("cy", (d: any) => d.x)
        .attr("fill", fill)
        .attr("stroke-width", 3)
        .attr("r", 6);
      g.selectAll("text")
        .data(root.descendants())
        .join("text")
        .attr("text-anchor", (d) => (d.children ? "end" : "start"))
        // note that if d is a child, d.children is undefined which is actually false!
        .attr("x", (d: any) => (d.children ? -6 : 6) + d.y)
        .attr("y", (d: any) => d.x + 5)
        .text((d) => d.data.name);
    };
    d3.json<IDatum>("./data/games.json").then((data: IDatum | undefined) => {
      if (data) {
        let dataHierarchyNode: d3.HierarchyNode<IDatum> = d3.hierarchy<IDatum>(
          data
        );
        root = d3.tree<IDatum>().size([innerHeight, innerWidth])(
          dataHierarchyNode
        );
        render(root);
      }
    });
  });
  return (
    <>
      <svg ref={svg}></svg>
    </>
  );
};
export { Tree };


原文地址:https://www.cnblogs.com/xiaoxu-xmy/p/13771633.html
GitHub: https://github.com/lemon-Xu/Learning-d3.-Js
作者: lemon

原文地址:https://www.cnblogs.com/xiaoxu-xmy/p/13771633.html