block by nitaku 8054295

Before/after image

Full Screen

A widget that can be used to compare two images by dragging.

Photos are taken from Google Earth, I do not own them. The grayscale one is from 2003, while the other is from 2013. By dragging the photos, you can see the bridge that has been destroyed in 2009 at the bottom of the picture.

Inspired in part by this ABC page on the Fukushima disaster.

The technique used leverages SVG image patterns. A hack (an overlayed gray rectangle) is used to hide the ugly progressive rendering of image loading.

index.js

(function() {

  window.main = function() {
    var WIDTH, drag, status;
    WIDTH = 535;
    status = {
      x: WIDTH - 135
    };
    /* do an intitial transition
    */
    d3.select('#after').transition().duration(1600).attr('x', status.x).attr('width', WIDTH - status.x);
    /* define a drag behavior
    */
    drag = d3.behavior.drag().origin(function(d) {
      return d;
    }).on('drag', function(d) {
      /* move the layer
      */      d.x = d3.event.x;
      return d3.select('#after').attr('x', Math.max(0, d.x)).attr('width', Math.max(0, WIDTH - Math.max(0, d.x)));
    });
    /* drag anywhere to move the slider
    */
    /* the slider is also used to hide image preloading
    */
    return d3.select('#overlay').datum(status).style('fill', 'transparent').call(drag);
  };

}).call(this);

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Before/after image</title>
        <link type="text/css" href="index.css" rel="stylesheet"/>
        <script src="//d3js.org/d3.v3.min.js"></script>
        <script src="index.js"></script>
    </head>
    <body onload="main()">
        <svg width="960" height="500">
            <defs>
                <pattern id="before_pattern" patternUnits="userSpaceOnUse" width="535" height="500">
                    <image xlink:href="before.png" x="0" y="0" width="535" height="500" />
                </pattern>
                <pattern id="after_pattern" patternUnits="userSpaceOnUse" width="535" height="500">
                    <image xlink:href="after.png" x="0" y="0" width="535" height="500" />
                </pattern>
            </defs>
            <g transform="translate(230,24) scale(0.9)">
                <rect id="before" fill="url(#before_pattern)" x="0" y="0" width="535" height="500"/>
                <rect id="after" fill="url(#after_pattern)" x="0" y="0" width="535" height="500"/>
                <rect id="overlay" fill="transparent"  x="-20" y="0" width="575" height="500"/>
            </g>
        </svg>
    </body>
</html>

index.coffee

window.main = () ->
    WIDTH = 535
    status = {x: WIDTH-135}
    
    ### do an intitial transition ###
    d3.select('#after')
        .transition().duration(1600)
            .attr('x', status.x)
            .attr('width', WIDTH-status.x)
            
    ### define a drag behavior ###
    drag = d3.behavior.drag()
        .origin((d) -> d)
        .on 'drag', (d) ->
            ### move the layer ###
            d.x = d3.event.x
            d3.select('#after')
                .attr('x', Math.max(0, d.x))
                .attr('width', Math.max(0, WIDTH-Math.max(0, d.x)))
                
    ### drag anywhere to move the slider ###
    ### the slider is also used to hide image preloading ###
    d3.select('#overlay')
        .datum(status)
        .style('fill', 'transparent')
        .call drag
        

index.css

#overlay {
  fill: #333333;
  cursor: move;
}

body {
  background: #333333;
}

index.sass

#overlay
    fill: #333
    cursor: move
    
body
    background: #333