D3如何获取分段比例尺的反向值

19
我正在使用d3编写甘特图。 我有一个时间刻度尺xScale(Time)。
this.xScale = d3.scaleTime()
            .domain([this.startDate,this.endDate])
            .range([0,this.getWidth()]);

并将y轴比例尺调整为分段比例尺(资源)

this.yScale = d3.scaleBand()
        .domain(resources.map(function(res){
            return res.res_num;
        }))
        .rangeRound([0,this.getHeight()])
        .paddingInner(.3)

问题是我需要将任务(SVG矩形)从一个资源拖放到另一个资源。

当我拖动时,我使用transform使其在SVG上移动。

_onDrag : function(d)
    {
        var x = d3.event.dx+d3.event.x;
        var y = d3.event.dy+d3.event.y;
        d3.select(this).raise().attr("transform","translate(" + [x,y] + ")");
    },

但在放下时,我必须按照以下逻辑处理:

  1. 矩形不应该在资源之间,所以需要根据d3.event.y将其转换为任何一个区间。
    1. x轴上有反转比例尺,但y轴上没有。如何解决?
4个回答

32

我按照以下方法解决了这个问题,但我不知道这是否是标准方式,如果有其他方法,请回答此问题。

var eachBand = self.yScale.step();
var index = Math.round((d3.event.y / eachBand));
var val = self.yScale.domain()[index];

eachBand 返回每个带的像素大小, 如果我将y值(拖动事件)除以eachBand,我将得到索引,该索引可获得yScale值。


18

当图表左右两侧有填充时,如果值超出图表的条形,则答案会返回 undefined。可以使用以下函数将此填充考虑在内,并将索引夹紧到 domain 的有效值。

function scaleBandInvert(scale) {
  var domain = scale.domain();
  var paddingOuter = scale(domain[0]);
  var eachBand = scale.step();
  return function (value) {
    var index = Math.floor(((value - paddingOuter) / eachBand));
    return domain[Math.max(0,Math.min(index, domain.length-1))];
  }
}

它可以如下使用:

var xScale = d3.scaleBand().domain(...).rangeRound([a,b]);
....
var dvalX = scaleBandInvert(xScale)(value);  // single invert
....
var dRangeX = d3.event.selection.map(scaleBandInvert(xScale));  // in a brush event handler

1
谢谢您的代码:var paddingOuter = scale(domain[0]); - Llorenç Pujol Ferriol
我发现为了让二分法在条形图之间切换,我必须使用 Math.roundpaddingInnervar paddingInner = scale.step() * scale.paddingInner(); 然后 var index = Math.round((x - scaleStart - paddingInner / 2) / eachBand); - tgordon18
不错,但只需进行小的补充。对我而言,如果我使用 const paddingOuter = scale.paddingOuter(); 而不是 const paddingOuter = scale(domain[0]);,它会更精确地起作用。 - Omar Omeiri

12

对被接受的答案进行微调 - 你可能想使用 Math.floor 而不是 Math.round,因为前者将返回下一个较低的级别,如果你选择了项目的中心点以下。所以修改后的解决方案如下:

var eachBand = self.yScale.step();
var index = Math.floor((d3.event.y / eachBand));
var val = self.yScale.domain()[index];

0

@rioV8的答案对我有用。

但是,如果您正在更改比例尺的range以进行缩放或平移,则会出现问题。

对于那些需要缩放的人:

import type { ScaleBand } from 'd3';

function invertBand(scale: ScaleBand<string>) {
  const domain = scale.domain();
  const step = scale.step();
  const firstVisibleBand = Math.abs(scale.range()[0]) / step;

  return function (v: number) {
    const index = Math.floor(
      (
        (v / WIDTH)
        / (step / WIDTH)
      )
      + firstVisibleBand,
    );
    return domain[Math.max(0, Math.min(index, domain.length - 1))];
  };
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接