title: D3: slow zoomable heatmap
body: <p>I have this zoomable heatmap, which looks too slow when zooming-in or out. Is there anything to make it faster/smoother or it is just too many points and that is the best I can have. I was wondering if there is some trick to make it lighter for the browser please while keeping enhancements like tooltips. Or maybe my code handling the zoom feature is not great .</p>
  
  <p><div class="snippet" data-lang="js" data-hide="true" data-console="true" data-babel="false">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  &lt;html lang="en"&gt;
  
  &lt;head&gt;
      &lt;meta charset="utf-8"&gt;
      &lt;style&gt;
          .axis text {
              font: 10px sans-serif;
          }
  
          .axis path,
          .axis line {
              fill: none;
              stroke: #000000;
          }
  
          .x.axis path {
              //display: none;
          }
  
          .chart rect {
              fill: steelblue;
          }
  
          .chart text {
              fill: white;
              font: 10px sans-serif;
              text-anchor: end;
          }
          
          #tooltip {
            position:absolute;
            background-color: #2B292E;
            color: white;
            font-family: sans-serif;
            font-size: 15px;
            pointer-events: none; /*dont trigger events on the tooltip*/
            padding: 15px 20px 10px 20px;
            text-align: center;
            opacity: 0;
            border-radius: 4px;
          }
      &lt;/style&gt;
      &lt;title&gt;Bar Chart&lt;/title&gt;
  
      &lt;!-- Reference style.css --&gt;
      &lt;!--    &lt;link rel="stylesheet" type="text/css" href="style.css"&gt;--&gt;
  
      &lt;!-- Reference minified version of D3 --&gt;
      &lt;script src='https://d3js.org/d3.v4.min.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'&gt;&lt;/script&gt;
  &lt;/head&gt;
  
  &lt;body&gt;
      &lt;div id="chart" style="width: 700px; height: 500px"&gt;&lt;/div&gt;
      &lt;script&gt;
          var dataset = [];
          for (let i = 1; i &lt; 360; i++) {
              for (j = 1; j &lt; 75; j++) {
                  dataset.push({
                      day: i,
                      hour: j,
                      tOutC: Math.random() * 25,
                  })
              }
          };
  
  
          var days = d3.max(dataset, function(d) {
                  return d.day;
              }) -
              d3.min(dataset, function(d) {
                  return d.day;
              });
          var hours = d3.max(dataset, function(d) {
                  return d.hour;
              }) -
              d3.min(dataset, function(d) {
                  return d.hour;
              });
  
          var tMin = d3.min(dataset, function(d) {
                  return d.tOutC;
              }),
              tMax = d3.max(dataset, function(d) {
                  return d.tOutC;
              });
  
          var dotWidth = 1,
              dotHeight = 3,
              dotSpacing = 0.5;
  
          var margin = {
                  top: 0,
                  right: 25,
                  bottom: 40,
                  left: 25
              },
              width = (dotWidth * 2 + dotSpacing) * days,
              height = (dotHeight * 2 + dotSpacing) * hours; 
  
  
          var colors = ['#2C7BB6', '#00A6CA','#00CCBC','#90EB9D','#FFFF8C','#F9D057','#F29E2E','#E76818','#D7191C'];
  
          var xScale = d3.scaleLinear()
              .domain(d3.extent(dataset, function(d){return d.day}))
              .range([0, width]);
  
          var yScale = d3.scaleLinear()
              .domain(d3.extent(dataset, function(d){return d.hour}))
              .range([(dotHeight * 2 + dotSpacing) * hours, dotHeight * 2 + dotSpacing]);
  
          var colorScale = d3.scaleQuantile()
              .domain([0, colors.length - 1, d3.max(dataset, function(d) {
                  return d.tOutC;
              })])
              .range(colors);
  
          var xAxis = d3.axisBottom().scale(xScale);
  
  
  
          // Define Y axis
          var yAxis = d3.axisLeft().scale(yScale);
  
  
          var zoom = d3.zoom()
              .scaleExtent([dotWidth, dotHeight])
              .translateExtent([
                  [80, 20],
                  [width, height]
              ])
              .on("zoom", zoomed);
  
          var tooltip = d3.select("body").append("div")
          .attr("id", "tooltip")
          .style("opacity", 0);
  
          // SVG canvas
          var svg = d3.select("#chart")
              .append("svg")
              .attr("width", width + margin.left + margin.right)
              .attr("height", height + margin.top + margin.bottom)
              .call(zoom)
              .append("g")
              .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
          // Clip path
          svg.append("clipPath")
              .attr("id", "clip")
              .append("rect")
              .attr("width", width)
              .attr("height", height);
  
  
          // Heatmap dots
          svg.append("g")
              .attr("clip-path", "url(#clip)")
              .selectAll("ellipse")
              .data(dataset)
              .enter()
              .append("ellipse")
              .attr("cx", function(d) {
                  return xScale(d.day);
              })
              .attr("cy", function(d) {
                  return yScale(d.hour);
              })
              .attr("rx", dotWidth)
              .attr("ry", dotHeight)
              .attr("fill", function(d) {
                  return colorScale(d.tOutC);
              })
              .on("mouseover", function(d){
                  $("#tooltip").html("X: "+d.day+"&lt;br/&gt;Y:"+d.hour+"&lt;br/&gt;Value:"+Math.round(d.tOutC*100)/100);
                  var xpos = d3.event.pageX +10;
                  var ypos = d3.event.pageY +20;
                  $("#tooltip").css("left",xpos+"px").css("top",ypos+"px").animate().css("opacity",1);
              }).on("mouseout", function(){
                  $("#tooltip").animate({duration: 500}).css("opacity",0);
              }); 
  
          //Create X axis
          var renderXAxis = svg.append("g")
              .attr("class", "x axis")
              .attr("transform", "translate(0," + yScale(0) + ")")
              .call(xAxis)
  
          //Create Y axis
          var renderYAxis = svg.append("g")
              .attr("class", "y axis")
              .call(yAxis);
  
  
          function zoomed() {
              // update: rescale x axis
              renderXAxis.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));
  
              update();
          }
  
          function update() {
              // update: cache rescaleX value
              var rescaleX = d3.event.transform.rescaleX(xScale);
              svg.selectAll("ellipse")
                  .attr('clip-path', 'url(#clip)')
                  // update: apply rescaleX value
                  .attr("cx", function(d) {
                      return rescaleX(d.day);
                  })
  //                .attr("cy", function(d) {
  //                    return yScale(d.hour);
  //                })
                  // update: apply rescaleX value
                  .attr("rx", function(d) {
                      return (dotWidth * d3.event.transform.k);
                  })
                  .attr("fill", function(d) {
                      return colorScale(d.tOutC);
                  });
          }
          
  
          
          
      &lt;/script&gt;
  &lt;/body&gt;
  
  &lt;/html&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>Thanks</p>