d3获得波段刻度的反转值

lyr7nygr  于 2023-06-23  发布在  其他
关注(0)|答案(4)|浏览(148)

我正在使用d3编写甘特
我有xScale和Time Scale(时间)

this.xScale = d3.scaleTime()
            .domain([this.startDate,this.endDate])
            .range([0,this.getWidth()]);

和yScale作为波段比例(参考资料)

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

问题是我需要将任务(SVG Rect)从一个资源拖放到另一个资源
当我拖动时,我使用transfrom,所以它在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] + ")");
    },

但是在drop上,我必须处理以下逻辑:

  1. rect不应该在资源之间,因此需要基于d3.event.y转换到任何频带
    1.在xAxis中,时间刻度具有反转,而yScale没有。如何做到这一点?
xxb16uws

xxb16uws1#

解决这个问题如下,但我不知道这是标准的方式,如果有任何其他方式,请回答这个问题。

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

eachBand以像素为单位给出波段的大小,如果我将y值(拖动事件)除以eachBand,我将获得给出yScale值的索引

7hiiyaii

7hiiyaii2#

当图的左右两边有填充时,如果值在图的条形图之外,则答案给予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
n8ghc7c1

n8ghc7c13#

对已接受的答案进行一个调整-您可能希望使用Math.floor而不是Math.round,因为如果您碰巧选择了项目中心点以下,前者将返回下一个级别。因此,修改的解决方案是:

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

nkkqxpd94#

@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))];
  };
}

相关问题