Search code examples
javascriptd3.jscanvasretina

d3: combining canvas + svg for retina


This question is particular to using D3 on a retina canvas, which is the target platform. I have a chart created using the canvas. I created the axes for the chart using svg. I am calling the d3 zoom function on an svg rectangle because I am trying to find a workaround to d3 zoom issues on retina canvas: https://github.com/d3/d3-zoom/issues/133

Here is my result so far: https://bl.ocks.org/interwebjill/cc5361ca1d1c5538fab19999785c6d01

I can capture the zoom event with an overlapping svg rectangle. The only adjustment that needs to be made is to reset the zoom center of the canvas to half that of the rectangle. But d3 v4 no longer has zoom.center() as v3 did.


Solution

  • You should use the width and height instead of the canvasWidth and canvasHeight values for the ranges:

    var xSVG = d3.scaleTime()
        .range([0, width]);
    
    var x = d3.scaleTime()
        .range([0, width]);
    
    var x2 = d3.scaleTime()   // for zooming
        .range([0, width]);
    
    var ySVG = d3.scaleLinear()
        .range([height, 0]);
    
    var y = d3.scaleLinear()
        .range([height, 0]);
    

    In the draw logic, you can add a context.scale(2, 2) to draw everything twice as big. Again, swap all canvasWidth & canvasHeight values with width & height, as the context.scale will take care of the rest

    function draw() {
      context.save();
      context.scale(2, 2);
      context.clearRect(0, 0, width, height);
    
       // clip path
       context.beginPath()
       context.rect(0, 0, width, height);
       context.clip();
    
       for (i=0; i<len; i++) {
         context.beginPath();
         area(sources[i].values);
         context.fillStyle = color(sources[i].id);
         context.fill();
       }
    
       context.restore();
    }