原创

react + d3.js + TS 堆叠条形图 报错解决

1. 结构

结构请看之前的文章,这里记录部分报错解决的方式。能力受限无法解决方式不会优雅。

2. 报错

2.1 stack生成器报错

var naiveStack = d3.stack().keys(naiveKeys).order(d3.stackOrderNone)(naiveDaa);

Argument of type 'IDatum[]' is not assignable to parameter of type '{ [key: string]: number; }[]'

解决方式,补全泛型参数

var naiveStack = d3.stack<IDatum, string>().keys(naiveKeys).order(d3.stackOrderNone)(naiveData);

index.d.ts 中 stack声明为:

export funcion stack<Datum, Key>(): Stack<any, Datum, Key>;

The first generic corresponds to the data type of an element in the data array passed into the stack generator.

The second generic corresponds to the data type of key used to identify a series.

Datum: 传递给stack生成器的数据数组的中元素的数据类型。
Key: 用于标识序列的键的数据类型。

2.2 accessor存储器报错

let maxDatumSubd = d3.max(naiveStack, (d: IDatum) =>
      //   d3.max(d, (subd: ) => subd[1])
      d3.max(d, (subd: IDatum) => subd[1])
    );

Argument of type 'Series<IDatum, string>[]' is not assignable to parameter of type 'ArrayLike'

Argument of type 'IDatum' is not assignable to parameter of type 'ArrayLike'

两个报错都是说value类型和accessor读取的参数类型不一致。

解决方法,根据Stack的数据类型正确指明参数类型。

let maxDatumSubd = d3.max(naiveStack, (d: d3.Series<IDatum, string>) =>
      //   d3.max(d, (subd: ) => subd[1])
      d3.max(d, (subd: SeriesPoint<IDatum>) => subd[1])
    );

index.d.ts下,关于stack重要的两个类型接口

2.2.1 SeriesPoint<Datum> extends Array<number>

export interface SeriesPoint<Datum> extends Array<number> {
    0: number;
    1: number;
    data: Datum;
}

Each series point j in a stack chart corresponds to the jth element in the input data

堆栈中的每个点j系列图对应于第j元素输入数据。

Each point is represented as an array [y0, y1] where y0 is the lower value(baseline) and y1 is the upper value(topline); the difference between y0 and y1 corresponds to the computed value for this point

每个点代表一个数组 [y0, y1],y1为下部值(基线),y2为上部值(背线)。y0和y1的差对应于这个点的计算值。

SeriesPoint is a [number, number] two-element Array with added data and index properties related to the data element which formed the basis for theSeriesPoint.

序列点是一个[number, number]两元素数组,其中添加了于数据元素相关的数据和索引属性,这些数据元素构成了该序列化点的基础。

2.2.2 Series<Datum, Key> exends Array<SeriesPoint>

export interface Series<Datum, key> extends Array<SeriesPoint<Datum>> {
    key: key;
    index: number;
}

The series are determined by the keys accessor; each series i in the returned array corrsponds to the ith key.

序列由键访问器决定,每个序列i对应第i个键。

Each series is an array of points, where each point j corresponds to the jth element in the input data.

每个序列是由点组成的数组,每个点j对应输出数据的第j个元素

The key for each series is available as series.key, and the index as series.index.

每个系列的键可作为系列键使用。索引为series.index。

所以对于IDatum【】而言, 第一层为Series<IDatum, string>,第二层为SeriesPoint

3. 代码

import React, { useState, useEffect, useRef } from "react";
import * as d3 from "d3";
import moment from "moment";
import { Series, SeriesPoint } from "d3";
interface IDatum {
  month: Date;
  apples: number;
  bananas: number;
  cherries: number;
  dates: number;
}
const StackBarChart: React.FC = () => {
  const [width] = useState(1600);
  const [height] = useState(800);
  const [margin] = useState({
    left: 160,
    top: 80,
    right: 80,
    bottom: 160,
  });
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;
  const svg = useRef<SVGSVGElement>(null);
  useEffect(() => {
    const svgSelection = d3
      .select(svg.current)
      .attr("width", width)
      .attr("height", height);
    const g = svgSelection
      .append("g")
      .attr("id", "maingroup")
      .attr("transform", `translate(${margin.left}, ${margin.right})`);
    const naiveData: IDatum[] = [
      {
        month: new Date(2015, 0, 1),
        apples: 3840,
        bananas: 1920,
        cherries: 960,
        dates: 400,
      },
      {
        month: new Date(2015, 1, 1),
        apples: 1600,
        bananas: 1440,
        cherries: 960,
        dates: 400,
      },
      {
        month: new Date(2015, 2, 1),
        apples: 640,
        bananas: 960,
        cherries: 640,
        dates: 400,
      },
      {
        month: new Date(2015, 3, 1),
        apples: 320,
        bananas: 480,
        cherries: 640,
        dates: 400,
      },
    ];
    const naiveKeys = ["apples", "banans", "cherries", "dates"];
    // remember the following apis are able to modify the 'offset' the data;
    // .offset(d3.stackOffsetNone)
    // .offset(d3.stackOffsetWiggle)
    var naiveStack = d3
      .stack<IDatum, string>()
      .keys(naiveKeys)
      .order(d3.stackOrderNone)(naiveData);
    const xValue = (d: IDatum) => {
      return moment(d.month.toISOString()).format("YYYY-M-D");
    };
    //
    let maxDatumSubd = d3.max(naiveStack, (d: d3.Series<IDatum, string>) =>
      //   d3.max(d, (subd: ) => subd[1])
      d3.max(d, (subd: SeriesPoint<IDatum>) => subd[1])
    );
    const yScale = d3
      .scaleLinear()
      .domain([0, maxDatumSubd || 0])
      .range([innerHeight, 0])
      .nice();
    const xScale = d3
      .scaleBand()
      .domain(naiveData.map((d: any) => xValue(d)))
      .range([0, innerWidth])
      .padding(0.5);
    const naiveAxes = function () {
      const xAxis = d3.axisBottom(xScale).tickSize(-innerHeight);
      const xAxisGroup = g
        .append("g")
        .attr("id", "xaxis")
        .call(xAxis)
        .attr("transform", `translate(0, ${innerHeight})`);
      const yAxis = d3
        .axisLeft(yScale)
        //.tickFormat(d3.format('.2r'))
        .tickFormat(d3.format(".1s"))
        //.tickFormat(d3.format('.2s'))
        //.tickFormat(d3.format('.2f'))
        .tickSize(-innerWidth);
      const yAxisGroup = g.append("g").attr("id", "yaxis").call(yAxis);
      return { xAxisGroup: xAxisGroup, yAxisGroup: yAxisGroup };
    };
    naiveAxes();
    const color = d3
      .scaleOrdinal<string, string>()
      .domain(naiveKeys)
      .range(d3.schemeSet3);
    // return;
    // console.log(naiveStack);
    const groupRect = g
      .append("g")
      .selectAll("g")
      .data(naiveStack)
      .enter()
      .append("g")
      .attr("fill", (d: any, i: any) => {
        return color(i);
      })
      .selectAll("rect")
      .data((d) => d)
      .enter()
      .append("rect")
      .attr("y", (d: any) => {
        return yScale(d[1]) || 0;
      })
      .attr("x", (d: any) => {
        return xScale(xValue(d.data)) || 0;
      })
      .attr("height", (d: any) => {
        let a = yScale(d[0]) || 0;
        let b = yScale(d[1]) || 0;
        return a - b;
      })
      .attr("width", xScale.bandwidth());
    return;
    // start to do data-join;
    groupRect
      .selectAll("rect")
      .data(naiveStack)
      .join("g")
      .attr("class", "maingroup")
      .attr("fill", (d: Series<IDatum, string>) => color(d.key))
      .selectAll(".datarect")
      .attr("y", (d: any) => {
        return yScale(d[1]) || 0;
      })
      .attr("x", (d: any) => {
        return xScale(xValue(d.data)) || 0;
      })
      .attr("height", (d: any) => {
        let a = yScale(d[0]) || 0;
        let b = yScale(d[1]) || 0;
        return a - b;
      })
      .attr("width", xScale.bandwidth());
    d3.selectAll(".tick text").attr("font-size", "2em");
    d3.selectAll("#xaxis text").attr("y", "10");
  });
  return (
    <>
      <svg ref={svg}></svg>
    </>
  );
};
export { StackBarChart };

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

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