带缩放的ScaleBand

时间:2018-03-17 10:21:51

标签: d3.js

我目前正在使用D3 v4。我的x刻度是一个scaleBand,我在这里显示一个国家名称,我的y刻度是scaleTime。当我试图在缩放功能中获得新的xScale时,它会在rescaleX上抛出错误。我在网上查看了很多例子,但所有这些例子都有一个linearScale或scaleTime。

var countryArr = ["USA", "UK", "Poland", "Sweden"];
var firstDay = moment(day).subtract(1, 'days').toDate();
var lastDay = moment(day).add(1, 'days').toDate();

// define scale
var yScale = d3.scaleTime().range([0, width]); 
yScale.domain([firstDay, lastDay]);
var xScale = d3.scaleBand().range([0, height]);
xScale.domain(countryArr);

// define zoom
var zoom = d3.zoom()
    .scaleExtent([1, 1])
    .translateExtent([[margin.left, margin.top], [width, height]])
    .on("zoom", zoomed);

d3.select("#main-canvas").call(zoom);

function zoomed() {
    //console.log(d3.event.transform.y);
    var new_x = d3.event.transform.rescaleX(xScale);  // ERROR at this line
    var new_y = d3.event.transform.rescaleY(yScale); 
}

enter image description here

2 个答案:

答案 0 :(得分:3)

序数(定性)比例中使用rescaleX毫无意义。 API很清楚:

  

返回其域已转换的连续比例 x的副本。这是通过首先在比例范围上应用逆x变换,然后应用逆比例来计算相应的域来实现的。 (强调我的)

另外,Bostock(D3创作者)already explained ......

  

...缩放行为影响附加比例的域,但是在平移和缩放时修改序数比例的域是没有意义的,因为域是离散的。

您可以做的是创建几何缩放而不是语义缩放

这是一个演示,我使用条形图的this example来添加几何缩放:



var svg = d3.select("svg"),
  margin = {
    top: 20,
    right: 20,
    bottom: 30,
    left: 40
  },
  width = +svg.attr("width") - margin.left - margin.right,
  height = +svg.attr("height") - margin.top - margin.bottom;

var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
  y = d3.scaleLinear().rangeRound([height, 0]);

var g = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var data = [{
  "letter": "A",
  "frequency": 0.08167
}, {
  "letter": "B",
  "frequency": 0.01492
}, {
  "letter": "C",
  "frequency": 0.02782
}, {
  "letter": "D",
  "frequency": 0.04253
}, {
  "letter": "E",
  "frequency": 0.12702
}, {
  "letter": "F",
  "frequency": 0.02288
}, {
  "letter": "G",
  "frequency": 0.02015
}, {
  "letter": "H",
  "frequency": 0.06094
}, {
  "letter": "I",
  "frequency": 0.06966
}, {
  "letter": "J",
  "frequency": 0.00153
}, {
  "letter": "K",
  "frequency": 0.00772
}, {
  "letter": "L",
  "frequency": 0.04025
}, {
  "letter": "M",
  "frequency": 0.02406
}, {
  "letter": "N",
  "frequency": 0.06749
}, {
  "letter": "O",
  "frequency": 0.07507
}, {
  "letter": "P",
  "frequency": 0.01929
}, {
  "letter": "Q",
  "frequency": 0.00095
}, {
  "letter": "R",
  "frequency": 0.05987
}, {
  "letter": "S",
  "frequency": 0.06327
}, {
  "letter": "T",
  "frequency": 0.09056
}, {
  "letter": "U",
  "frequency": 0.02758
}, {
  "letter": "V",
  "frequency": 0.00978
}, {
  "letter": "W",
  "frequency": 0.0236
}, {
  "letter": "X",
  "frequency": 0.0015
}, {
  "letter": "Y",
  "frequency": 0.01974
}, {
  "letter": "Z",
  "frequency": 0.00074
}];

x.domain(data.map(function(d) {
  return d.letter;
}));
y.domain([0, d3.max(data, function(d) {
  return d.frequency;
})]);

g.append("g")
  .attr("class", "axis axis--x")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x));

g.append("g")
  .attr("class", "axis axis--y")
  .call(d3.axisLeft(y).ticks(10, "%"))
  .append("text")
  .attr("transform", "rotate(-90)")
  .attr("y", 6)
  .attr("dy", "0.71em")
  .attr("text-anchor", "end")
  .text("Frequency");

g.selectAll(".bar")
  .data(data)
  .enter().append("rect")
  .attr("class", "bar")
  .attr("x", function(d) {
    return x(d.letter);
  })
  .attr("y", function(d) {
    return y(d.frequency);
  })
  .attr("width", x.bandwidth())
  .attr("height", function(d) {
    return height - y(d.frequency);
  });

var rect = g.append("rect")
  .attr("width", width)
  .attr("height", height)
  .attr("opacity", 0)
  .call(d3.zoom()
    .scaleExtent([.1, 10])
    .on("zoom", zoom));

function zoom() {
  g.attr("transform", d3.event.transform);
}

.bar {
  fill: steelblue;
}

.bar:hover {
  fill: brown;
}

.axis--x path {
  display: none;
}

<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
&#13;
&#13;

答案 1 :(得分:2)

正如@gerardofurtado所解释的那样,重新缩放功能对于序数规模几乎没有什么作用。但这里有一个小技巧,我喜欢使用重新缩放序数轴在图形上进行精确缩放。基本上,它独立地缩放轴,然后反向缩放轴的组成部分(线和文本),以便它们不会扭曲:

&#13;
&#13;
<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
  <style>
    .bar {
      fill: steelblue;
    }
    
    .bar:hover {
      fill: brown;
    }
    
    .axis--x path {
      display: none;
    }
  </style>
</head>

<body>
  <svg width="400" height="400"></svg>
  <script>
    var svg = d3.select("svg"),
      margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 40
      },
      width = +svg.attr("width") - margin.left - margin.right,
      height = +svg.attr("height") - margin.top - margin.bottom;

    var zoom = d3.zoom()
      .scaleExtent([1, Infinity])
      .translateExtent([
        [0, 0],
        [width, height]
      ])
      .extent([
        [0, 0],
        [width, height]
      ])
      .on("zoom", zoom);

    svg.call(zoom);

    var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
      y = d3.scaleBand().rangeRound([height, 0]).padding(0.1);

    var g = svg.append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var defs = g.append('defs');
    
    defs
      .append('clipPath')
      .attr('id', 'clip')
      .append('rect')
      .attr('x', 0)
      .attr('y', 0)
      .attr('width', width)
      .attr('height', height);
      
    defs
      .append('clipPath')
      .attr('id', 'clipx')
      .append('rect')
      .attr('x', 0)
      .attr('y', height)
      .attr('width', width)
      .attr('height', margin.bottom);
      
    defs
      .append('clipPath')
      .attr('id', 'clipy')
      .append('rect')
      .attr('x', -margin.left)
      .attr('y', -10)
      .attr('width', margin.left+1)
      .attr('height', height+15);

    var data = [{
      "letter": "A",
      "frequency": 'Z'
    }, {
      "letter": "B",
      "frequency": 'Y'
    }, {
      "letter": "C",
      "frequency": 'X'
    }, {
      "letter": "D",
      "frequency": 'W'
    }, {
      "letter": "E",
      "frequency": 'V'
    }, {
      "letter": "F",
      "frequency": 'U'
    }, {
      "letter": "G",
      "frequency": 'T'
    }, {
      "letter": "H",
      "frequency": 'S'
    }, {
      "letter": "I",
      "frequency": 'R'
    }, {
      "letter": "J",
      "frequency": 'Q'
    }, {
      "letter": "K",
      "frequency": 'P'
    }, {
      "letter": "L",
      "frequency": 'O'
    }, {
      "letter": "M",
      "frequency": 'N'
    }, {
      "letter": "N",
      "frequency": 'M'
    }, {
      "letter": "O",
      "frequency": 'L'
    }, {
      "letter": "P",
      "frequency": 'K'
    }, {
      "letter": "Q",
      "frequency": 'J'
    }, {
      "letter": "R",
      "frequency": 'I'
    }, {
      "letter": "S",
      "frequency": 'H'
    }, {
      "letter": "T",
      "frequency": 'G'
    }, {
      "letter": "U",
      "frequency": 'F'
    }, {
      "letter": "V",
      "frequency": 'E'
    }, {
      "letter": "W",
      "frequency": 'D'
    }, {
      "letter": "X",
      "frequency": 'C'
    }, {
      "letter": "Y",
      "frequency": 'B'
    }, {
      "letter": "Z",
      "frequency": 'A'
    }];

    x.domain(data.map(function(d) {
      return d.letter;
    }));
    y.domain(data.map(function(d) {
      return d.frequency;
    }));

    var xAxis = g.append("g")
      .attr('clip-path', 'url(#clipx)')
      .append("g")
      .attr("class", "axis axis--x")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));
      
    var yAxis = g.append("g")
      .attr('clip-path', 'url(#clipy)')
      .append("g")
      .attr("class", "axis axis--y")
      .call(d3.axisLeft(y));
      
    yAxis
      .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", "0.71em")
      .attr("text-anchor", "end")
      .text("Letters Too");

    var bars = g.append("g")
      .attr('clip-path', 'url(#clip)')
      .selectAll(".bar")
      .data(data)
      .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) {
        return x(d.letter);
      })
      .attr("y", function(d) {
        return y(d.frequency) + y.bandwidth()/2;
      })
      .attr("width", x.bandwidth())
      .attr("height", function(d) {
        return height - y(d.frequency);
      });

    function zoom() {
      var t = d3.event.transform;
      bars.attr("transform", t);
      
      xAxis.attr("transform", d3.zoomIdentity.translate(t.x, height).scale(t.k));
      xAxis.selectAll("text")
        .attr("transform",d3.zoomIdentity.scale(1/t.k));
      xAxis.selectAll("line")
        .attr("transform",d3.zoomIdentity.scale(1/t.k));
        
      yAxis.attr("transform", d3.zoomIdentity.translate(0, t.y).scale(t.k));
      yAxis.selectAll("text")
        .attr("transform",d3.zoomIdentity.scale(1/t.k));
      yAxis.selectAll("line")
        .attr("transform",d3.zoomIdentity.scale(1/t.k));
    }
  </script>
</body>

</html>
&#13;
&#13;
&#13;