Typescript 让d3比例根据加入的数据动态更新?

Typescript 让d3比例根据加入的数据动态更新?,typescript,d3.js,scale,categorical-data,gantt-chart,Typescript,D3.js,Scale,Categorical Data,Gantt Chart,在下面的代码示例中(在中可见),我或多或少地按照自己的意愿呈现原始事件数据,但每次更改都会重新计算所有坐标和比例 此可视化的目的是显示在多个并发进程中随时间发生的事件(有开始和结束),其中每个进程都有自己的水平“泳道” 但是我想正确地使用d3比例原语。目前,我在每个渲染周期中对所有数据运行显式reduce 通过构建自己的reduce操作,我认为我并没有真正正确地使用D3。应执行添加单个新事件而不会导致重新缩放的情况。考虑到我使用D3的方式,我觉得我也可以使用React来创建SVG来渲染图形-我每

在下面的代码示例中(在中可见),我或多或少地按照自己的意愿呈现原始事件数据,但每次更改都会重新计算所有坐标和比例

此可视化的目的是显示在多个并发进程中随时间发生的事件(有开始和结束),其中每个进程都有自己的水平“泳道”

但是我想正确地使用d3比例原语。目前,我在每个渲染周期中对所有数据运行显式reduce

通过构建自己的reduce操作,我认为我并没有真正正确地使用D3。应执行添加单个新事件而不会导致重新缩放的情况。考虑到我使用D3的方式,我觉得我也可以使用React来创建SVG来渲染图形-我每次都在有效地重新计算所有内容,所以我没有利用D3选择的输入、退出逻辑

但是,我不知道如何将范围、行计算和时间刻度操作“连接”到数据选择。出于这个原因,我在外部进行这些计算,每次都重新渲染所有内容

Y轴:在我的代码中,您可以看到行高的计算和(分类数据)
事件的查找表。我确信一定有一个合适的d3模式,只有在考虑输入的数据时才进行延迟更新。我期待某种分类尺度,将唯一名称分配给y轴上的唯一行。我能用缩放键吗?如果是,我如何将其绑定到我的数据。我希望原语能够动态地分配新的类别(需要自己的行间距)并淘汰未使用的类别(不再需要自己的行间距)。是否有方法根据数据中的事件列表更新y轴编号

X轴:在创建刻度之前,我还必须明确计算时域的X坐标边界(以查找最早的
事件。开始
和最新的
事件。停止
)。我必须计算从最早开始的时间偏移量。由于这也是由事件数据决定的,我希望边界检测、扩散和缩放可以通过一些d3原语实现,但我不知道是什么。我在看d3的“区段”,但我如何确保区段在数据更改时得到更新,除非重新从头开始运行所有内容

数据看起来

我现有的(低效的)缩放和投影代码每次看起来都会重新运行

if(events.length>0){
常数svgWidth=300;
常数svgHeight=150;
const firstStart=events.reduce(
(min,event)=>Math.min(event.start,min),
Number.MAX\u安全\u整数
);
const lastStop=events.reduce(
(最大值,事件)=>数学最大值(事件停止,最大值),
Number.MIN\u安全\u整数
);
const duration=lastStop-firstStart;
const countByProcess=events.reduce(
(countMap,事件)=>{
countMap[event.process]=countMap[event.process]+1 | | 1;
返回计数图;
},
{}
);
如果(持续时间>0){
常量xScale=scaleLinear()
.domain([firstStart,lastStop])
.范围([0,svgWidth]);
const color=scaleOrdinal(SchemeCategory 10);
const rowHeight=svghight/Object.keys(countByProcess).length;
选择
.数据(事件)
.输入()
.append(“rect”)
.attr(“x”,(e)=>xScale(e.start-firstStart))
.attr(“宽度”(e)=>xScale(e.stop-e.start))
艾特先生(
“y”,
(e) =>Object.keys(countByProcess).indexOf(e.process)*行高
)
.attr(“高度”,行高)
.attr(“fill”,(u,i)=>color(i.toString());
返回;
}
}
选择数据([]);

我想你想用D3
enter/exit
模式渲染事件,并对X和Y使用比例,或者只对X使用比例?我想我了解你关于比例动态更新的问题。如果更改行数或帧范围,但图表的宽度和高度保持不变,则可以更改比例因子:
xScale.domain([newStart,newStop])
,然后重新渲染图表,但对于x轴,我必须自己计算newStart,newStop,而不是使用d3原语。我现在把d3.extent作为一个潜在的原语,尽管当一个新项目进入数据时如何触发重新计算比例仍然是一个谜。在yAxis上,我没有任何候选方法来消除countByProcess“reduce”。它只是计算数据流中不同进程ID的数量,从yAxis中为每个进程ID分配一个数字。再一次,d3应该为我这样做,我相信,因为它是由数据决定的。不知道怎么做。是的,我假设这意味着对X和Y都使用比例,并以一种自动更新先前渲染的数据和新输入的数据的方式连接它们。你说的是“事件”。。。因此,它可能是一个仅在新事件到达时才增长的数组。我认为在这种情况下你不需要重新缩放。您需要随着每个新事件同时更改域和范围,并向右滚动。如果我正确理解情况,请告诉我。
let events: Event[] = [
  {
    start: 0,
    stop: 3,
    process: "root"
  },
  {
    start: 1,
    stop: 5,
    process: "child2"
  },
  {
    start: 3,
    stop: 5,
    process: "root"
  },
  {
    start: 3,
    stop: 7,
    process: "child1"
  },
  {
    start: 5,
    stop: 6,
    process: "child2"
  },
  {
    start: 7,
    stop: 8,
    process: "root"
  },
  {
    start: 8,
    stop: 9,
    process: "root"
  },
  {
    start: 8,
    stop: 9,
    process: "child1"
  }
];
      if (events.length > 0) {
        const svgWidth = 300;
        const svgHeight = 150;

        const firstStart = events.reduce(
          (min, event) => Math.min(event.start, min),
          Number.MAX_SAFE_INTEGER
        );
        const lastStop = events.reduce(
          (max, event) => Math.max(event.stop, max),
          Number.MIN_SAFE_INTEGER
        );
        const duration = lastStop - firstStart;

        const countByProcess = events.reduce<Record<string, number>>(
          (countMap, event) => {
            countMap[event.process] = countMap[event.process] + 1 || 1;
            return countMap;
          },
          {}
        );

        if (duration > 0) {
          const xScale = scaleLinear()
            .domain([firstStart, lastStop])
            .range([0, svgWidth]);

          const color = scaleOrdinal(schemeCategory10);

          const rowHeight = svgHeight / Object.keys(countByProcess).length;

          selection
            .data(events)
            .enter()
            .append("rect")
            .attr("x", (e) => xScale(e.start - firstStart))
            .attr("width", (e) => xScale(e.stop - e.start))
            .attr(
              "y",
              (e) => Object.keys(countByProcess).indexOf(e.process) * rowHeight
            )
            .attr("height", rowHeight)
            .attr("fill", (_, i) => color(i.toString()));
          return;
        }
      }
      selection.data([]);