block by mbostock 2962761

Fisheye Grid

Full Screen

The above grid shows the effect of fisheye distortion. Move the mouse to change the focal point.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.background {
  fill: none;
  pointer-events: all;
}

path {
  fill: none;
  stroke: #333;
}

</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="fisheye.js"></script>
<script>

var width = 960,
    height = 500,
    xStepsBig = d3.range(10, width, 20),
    yStepsBig = d3.range(10, height, 20),
    xStepsSmall = d3.range(0, width + 6, 6),
    yStepsSmall = d3.range(0, height + 6, 6);

var fisheye = d3.fisheye();

var line = d3.svg.line();

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(-.5,-.5)");

svg.append("rect")
    .attr("class", "background")
    .attr("width", width)
    .attr("height", height);

svg.selectAll(".x")
    .data(xStepsBig)
  .enter().append("path")
    .attr("class", "x")
    .datum(function(x) { return yStepsSmall.map(function(y) { return [x, y]; }); });

svg.selectAll(".y")
    .data(yStepsBig)
  .enter().append("path")
    .attr("class", "y")
    .datum(function(y) { return xStepsSmall.map(function(x) { return [x, y]; }); });

var path = svg.selectAll("path")
    .attr("d", line);

svg.on("mousemove", function() {
  fisheye.center(d3.mouse(this));
  path.attr("d", function(d) { return line(d.map(fisheye)); });
});

</script>

fisheye.js

(function() {
  d3.fisheye = function() {
    var radius = 200,
        power = 2,
        k0,
        k1,
        center = [0, 0];

    function fisheye(d) {
      var dx = d[0] - center[0],
          dy = d[1] - center[1],
          dd = Math.sqrt(dx * dx + dy * dy);
      if (dd >= radius) return d;
      var k = k0 * (1 - Math.exp(-dd * k1)) / dd * .75 + .25;
      return [center[0] + dx * k, center[1] + dy * k];
    }

    function rescale() {
      k0 = Math.exp(power);
      k0 = k0 / (k0 - 1) * radius;
      k1 = power / radius;
      return fisheye;
    }

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

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

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

    return rescale();
  };
})();