block by nbremer e73086c0c3ebd2693a95962b561cbf05

Smooth Fisheye

Full Screen

Built with blockbuilder.org

index.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
</head>
<body>

	<!-- Based entirly on //www.nytimes.com/newsgraphics/2014/02/14/fashion-week-editors-picks/ but only the required bits for 1 canvas -->
  <div id="monthFisheye"></div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
  <script>

  var width = 1000,
      height = 500;

  var monthFisheye = d3.selectAll("#monthFisheye");
  var canvas = monthFisheye.append("canvas");

  var pixelRatio = 1,
      storeRatio = 1,
      enabled = false;

  if (window.devicePixelRatio >= 2
      && screen.availWidth >= 1280 // iPad can’t even handle it
      && canvas.node().getContext("2d").webkitBackingStorePixelRatio !== 2) { // Safari can’t even
    pixelRatio = 2;
    storeRatio = 2;
  }//if

  canvas
      .attr("height", height * storeRatio)
      .style("height", height + "px");

  var context = canvas.node().getContext("2d");
  context.scale(storeRatio, storeRatio);

  d3.select(window)
      .on("resize", resize);

  resize();

  // Recompute bounding boxes due to reflow.
  function resize() {
    width = parseInt(monthFisheye.style("width"));

    monthFisheye.select("canvas")
        .attr("width", width * storeRatio)
        .style("width", width + "px");

    enableFisheye(canvas);
  }//resize

  function enableFisheye() {
    var that = this,
        link = that.parentNode,
        touchtime;

    var image = new Image,
        desiredDistortion = 0,
        desiredFocus,
        idle = true;

    var numRows = 12,
        normalHeight = height / numRows,
        imageWidth = width,
        imageHeight = 300;

    var y = fisheye()
        .distortion(0)
        .extent([0, height]);

    canvas
        //.on("mousedown", mousedown)
        .on("mouseover", mouseover)
        .on("mousemove", mousemove)
        .on("mouseout", mouseout)
        .on("touchstart", touchstart)
        .on("touchmove", mousemove)
        .on("touchend", mouseout);

    render();

    function render() {

      context.clearRect(0, 0, width, height);

      for (var i = 0, n = numRows; i < n; ++i) {
        var y0 = y(i * normalHeight),
            y1 = y((i + 1) * normalHeight),
            dy = Math.min(imageHeight, y1 - y0);
        // context.drawImage(
        //   image, 
        //   0,
        //   Math.round((i * imageHeight + (imageHeight - dy) / 2) * pixelRatio),  
        //   imageWidth * pixelRatio, 
        //   dy * pixelRatio, 
        //   0, 
        //   y0, 
        //   width, 
        //   dy);

        context.fillStyle = ('#'+ parseInt(i % 255, 16) + parseInt(255 - (i % 255), 16) + "000000").substr(0,7);;
        context.fillRect(0,y0,width,dy);

        context.strokeStyle = "rgba(255,255,255,0.8)";
        context.beginPath();
        context.moveTo(0, y0);
        context.lineTo(width, y0);
        context.stroke();
      }

      context.strokeRect(0, 0, width, height);
    }//render

    function move() {
      if (idle) d3.timer(function() {
        var currentDistortion = y.distortion(),
            currentFocus = currentDistortion ? y.focus() : desiredFocus;
        idle = Math.abs(desiredDistortion - currentDistortion) < .01 && Math.abs(desiredFocus - currentFocus) < .5;
        y.distortion(idle ? desiredDistortion : currentDistortion + (desiredDistortion - currentDistortion) * .14);
        y.focus(idle ? desiredFocus : currentFocus + (desiredFocus - currentFocus) * .14);
        render();
        return idle;
      });
    }

    function mouseover() {
      desiredDistortion = imageHeight / normalHeight - 1;
      mousemove();
    }

    function mouseout() {
      desiredDistortion = 0;
      mousemove();
    }

    function mousemove() {
      desiredFocus = Math.max(0, Math.min(height - 1e-6, d3.mouse(this)[1]));
      move();
    }

    // function mousedown() {
    //   var m = Math.max(0, Math.min(height - 1e-6, d3.mouse(that)[1]));
    //   for (var i = 0, n = numRows; i < n && y(i * normalHeight) < m; ++i);
    //   link.href = "//www.nytimes.com/fashion/runway/" + d.slug + "/spring-2014-rtw/" + i + "?fingerprint=true";
    // }

    function touchstart() {
      d3.event.preventDefault();
      mouseover();
      if (d3.event.touches.length === 1) {
        var now = Date.now();
        if (now - touchtime < 500) mousedown(), link.click();
        touchtime = now;
      }
    }
  }

  function fisheye() {
    var min = 0,
        max = 1,
        distortion = 3,
        focus = 0;

    function G(x) {
      return (distortion + 1) * x / (distortion * x + 1);
    }

    function fisheye(x) {
      var Dmax_x = (x < focus ? min : max) - focus,
          Dnorm_x = x - focus;
      return G(Dnorm_x / Dmax_x) * Dmax_x + focus;
    }

    fisheye.extent = function(_) {
      if (!arguments.length) return [min, max];
      min = +_[0], max = +_[1];
      return fisheye;
    };

    fisheye.distortion = function(_) {
      if (!arguments.length) return distortion;
      distortion = +_;
      return fisheye;
    };

    fisheye.focus = function(_) {
      if (!arguments.length) return focus;
      focus = +_;
      return fisheye;
    };

    return fisheye;
  }

  </script>
</body>
</html>