block by armollica 97ed2acaada685157bfd

Transparency Gradient Border

Full Screen

Add an transparency gradient to the border of a map.

The map is drawn in svg using OSM’s vector tiles API and d3.geo.tile plugin as shown in this block. The gradient border is drawn in an overlaying canvas with the CanvasGradient interface.

Tiles copyright © OpenStreetMap contributors

index.html

<html>
  <head>
    <meta chartset="utf-8">
    <style>
      body {
        margin: 0;
        font-family: monospace;
      }
      
      .container > svg,
      .container > canvas {
        position: absolute;
      }
      
      path {
        fill: none;
        stroke: #000;
        stroke-linejoin: round;
        stroke-linecap: round;
      }
      
      .major_road { stroke: #776; }
      .minor_road { stroke: #ccb; }
      .highway { stroke: tomato; }
      .rail { stroke: #7de; }
      
      .annotation { 
        text-anchor: middle; 
        text-shadow: -1px 0 #fff, 0 1px #fff, 1px 0 #fff, 0 -1px #fff;
      }
      .annotation.large { font-size: 22px; }
      .annotation.medium { font-size: 16px; }
      .annotation.small { font-size: 12px; }
     
    </style>
  </head>
  <body>
    
    <div class="container"></div>
    
     <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
     <script src="d3.geo.tile.js"></script>
     <script>
      var width = 960,
          height = 500;

      var tiler = d3.geo.tile()
          .size([width, height]);

      var projection = d3.geo.mercator()
          .center([-87.95, 43.040150])
          .scale((1 << 20) / 2 / Math.PI)
          .translate([width / 2, height / 2]);

      var path = d3.geo.path()
          .projection(projection);
      
      var svg = d3.select(".container").append("svg")
          .attr("width", width)
          .attr("height", height)
          .call(drawMap);
        
      var canvas = d3.select(".container").append("canvas")
        .attr("width", width)
        .attr("height", height)
        .call(addGradientEdge, 75);
        
      function addGradientEdge(selection, borderWidth) {
        var context = selection.node().getContext("2d");
        
        var gradientData = [
          { 
            side: "left", x: 0, y: 0, dx: borderWidth, dy: height,
            gradient: context.createLinearGradient(0, 0, borderWidth, 0) 
          },
          { 
            side: "top", x: 0, y: 0, dx: width, dy: borderWidth,
            gradient: context.createLinearGradient(0, 0, 0, borderWidth) 
          },
          { 
            side: "right", x: width - borderWidth, y: 0, dx: borderWidth, dy: height,
            gradient: context.createLinearGradient(width, 0, width - borderWidth, 0) 
          },
          { 
            side: "bottom", x: 0, y: height - borderWidth, dx: width, dy: borderWidth,
            gradient: context.createLinearGradient(0, height, 0, height - borderWidth) 
          },
        ];
        
        gradientData.forEach(function(d) {
          d.gradient.addColorStop(0, "white");
          d.gradient.addColorStop(1, "rgba(255, 255, 255, 0)");
          context.fillStyle = d.gradient;
          context.fillRect(d.x, d.y, d.dx, d.dy);
        });
      }
      
      function drawMap(selection) {
        var annotation_data = [
          {
            text: "Milwaukee",
            coords: [-87.894746, 43.040746],
            size: "large"
          },
          {
            text: "Wauwatosa",
            coords: [-88.005609, 43.050873],
            size: "medium"
          },
          {
            text: "West Allis",
            coords: [-88.006764, 43.016687],
            size: "medium"
          }
        ];
        
        var tiles = selection.selectAll(".tile")
          .data(tiler
            .scale(projection.scale() * 2 * Math.PI)
            .translate(projection([0, 0])));
            
        tiles.enter().append("g")
          .attr("class", "tile")
          .each(function(d) {
            var tile = d3.select(this);
              d3.json("//" + ["a", "b", "c"][(d[0] * 31 + d[1]) % 3] + ".tile.openstreetmap.us/vectiles-highroad/" + d[2] + "/" + d[0] + "/" + d[1] + ".json", function(error, json) {
                tile.selectAll("path")
                    .data(json.features.sort(function(a, b) { return a.properties.sort_key - b.properties.sort_key; }))
                  .enter().append("path")
                    .attr("class", function(d) { return d.properties.kind; })
                    .attr("d", path);
              });
          });
          
          var annotations = selection.selectAll(".annotation").data(annotation_data);
          
          annotations.enter().append("text")
            .attr("class", function(d) { return "annotation " + d.size; })
            .attr("x", function(d) { return projection(d.coords)[0]; })
            .attr("y", function(d) { return projection(d.coords)[1]; })
            .text(function(d) { return d.text; });
        }
     
     </script>
  </body>
</html>

d3.geo.tile.js

d3.geo.tile = function() {
  var size = [960, 500],
      scale = 256,
      translate = [size[0] / 2, size[1] / 2],
      zoomDelta = 0;

  function tile() {
    var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
        z0 = Math.round(z + zoomDelta),
        k = Math.pow(2, z - z0 + 8),
        origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
        tiles = [],
        cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
        rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));

    rows.forEach(function(y) {
      cols.forEach(function(x) {
        tiles.push([x, y, z0]);
      });
    });

    tiles.translate = origin;
    tiles.scale = k;

    return tiles;
  }

  tile.size = function(_) {
    if (!arguments.length) return size;
    size = _;
    return tile;
  };

  tile.scale = function(_) {
    if (!arguments.length) return scale;
    scale = _;
    return tile;
  };

  tile.translate = function(_) {
    if (!arguments.length) return translate;
    translate = _;
    return tile;
  };

  tile.zoomDelta = function(_) {
    if (!arguments.length) return zoomDelta;
    zoomDelta = +_;
    return tile;
  };

  return tile;
};