Search code examples
javascriptd3.jszoomingscale

D3.js - How to multiply a time domain by a scalar?


I've got a regular time scale:

band.xScale = d3.time.scale()
  .domain([data.minDate, data.maxDate])

That I want to modify with a zoom:

var zoom = d3.behavior.zoom()
    .scaleExtent([1, 10])
    .on("zoom", zoomed);

function zoomed() {
    console.log(d3.event.scale);

    var oldDomain = bands[0].xScale.domain();
    var newDomain = ??? oldDomain * d3.event.scale ???
    bands[0].xScale.domain(newDomain);
    bands[0].redraw();
}

In other words, transform the [date1, date2] domain into a new array with new dates.

I could calculate the transformation myself, but I'd have to use something like Moment.js, and it really seems like the kind of thing d3 would have a built-in approach for...


Solution

    • d3v4/d3v5:

    d3-zoom provides a rescaleX function which updates a scale based on the zoom event (and which can be applied on a time scale):

    var updatedScale = d3.event.transform.rescaleX(scale);
    

    which can be applied for a zoom event on a time axis for instance:

    var scale = d3.scaleTime()
      .domain([new Date(2018, 7, 20), new Date(2018, 7, 30)])
      .range([0, 400]);
    
    var axis = d3.axisBottom(scale);
    
    var svg = d3.select("svg").append("g")
      .attr("transform", "translate(50,30)")
      .call(axis)
      .call(d3.zoom().on("zoom", zoomed));
    
    function zoomed() {
      var updatedScale = d3.event.transform.rescaleX(scale);
      svg.call(axis.scale(updatedScale));
    }
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <svg width="500" height="100"></svg>

    • d3v3:

    The scale is linked to the zoom and is updated by the zoom:

    d3.behavior.zoom().x(scale).on("zoom", zoomed)
    

    which gives for instance:

    var scale = d3.time.scale()
      .domain([new Date(2018, 7, 20), new Date(2018, 7, 30)])
      .range([0, 400]);
    
    var axis = d3.svg.axis().scale(scale);
    
    var svg = d3.select("svg").append("g")
      .attr("transform", "translate(50,30)")
      .call(axis)
      .call(d3.behavior.zoom().x(scale).on("zoom", zoomed));
    
    function zoomed() {
      axis.scale(scale);
      svg.call(axis.scale(scale));
    }
    <script src="https://d3js.org/d3.v3.min.js"></script>
    <svg width="500" height="100"></svg>