【问题标题】:ScaleBand with Zoom带缩放的ScaleBand
【发布时间】:2018-03-17 10:21:51
【问题描述】:

我目前正在使用 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); 
}

【问题讨论】:

    标签: d3.js


    【解决方案1】:

    正如@gerardofurtado 解释的那样,rescale 函数对序数比例几乎没有影响。但这里有一个小技巧,我喜欢用重新缩放序数轴来很好地缩放图表。本质上,它独立缩放轴,然后反向缩放轴的组件(线条和文本),使它们不会扭曲:

    <!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>

    【讨论】:

    • 谢谢马克!!我花了整个星期天后昨晚开始滚动工作,但我将我的 x 比例从 scaleBand 转换为 linearScale,然后进行刻度格式化以显示标签。它按预期工作。但我会尝试你的答案。因为它比我的更干净:)
    【解决方案2】:

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

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

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

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

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

    这里是一个demo,我正在使用条形图的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>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-07
      • 2016-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多