Skip to content

Instantly share code, notes, and snippets.

@dragosbulugean
Created May 7, 2017 09:48
Show Gist options
  • Select an option

  • Save dragosbulugean/1a26af78876716a3e6a38b0c40b2b2f1 to your computer and use it in GitHub Desktop.

Select an option

Save dragosbulugean/1a26af78876716a3e6a38b0c40b2b2f1 to your computer and use it in GitHub Desktop.

Revisions

  1. dragosbulugean created this gist May 7, 2017.
    250 changes: 250 additions & 0 deletions selection
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,250 @@
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>d3.js selection frame example</title>
    <link rel="stylesheet" href="app.css">
    </head>

    <body>
    </body>

    <script src="http://d3js.org/d3.v4.min.js"></script>
    <script src="app.js"></script>
    </html>
    app.css#

    rect.selection {
    stroke : gray;
    stroke-dasharray: 4px;
    stroke-opacity : 0.5;
    fill : transparent;
    }

    g.state circle {
    stroke : gray;
    cursor : pointer;
    }

    g.state circle.inner {
    fill : white;
    }

    g.state circle.outer {
    display : none;
    stroke-dasharray: 4px;
    stroke-opacity : 0.5;
    fill : transparent;
    }

    g.state.selected circle.outer {
    display : inline;
    }

    g.state text {
    font : 12px sans-serif;
    font-weight : bold;
    pointer-events : none;
    }

    /* disable text selection */
    svg *::selection {
    background : transparent;
    }

    svg *::-moz-selection {
    background:transparent;
    }

    svg *::-webkit-selection {
    background:transparent;
    }
    app.js#

    var radius = 40;

    window.states = [
    { x : 43, y : 67, label : "first" },
    { x : 340, y : 150, label : "second" },
    { x : 200, y : 250, label : "third" },
    { x : 300, y : 320, label : "fourth" },
    { x : 50, y : 250, label : "fifth" },
    { x : 90, y : 170, label : "last" }
    ]

    window.svg = d3.select("body")
    .append("svg")
    //.attr("viewBox", "0 0 " + 1000 + " " + 1000 )
    //.attr("preserveAspectRatio", "xMinYMin")
    .attr("width", "960px")
    .attr("height", "500px");

    var gStates = svg.selectAll( "g.state").data( states);

    var gState = gStates.enter().append( "g")
    .attr({
    "transform" : function( d) {
    return "translate("+ [d.x,d.y] + ")";
    },
    'class' : 'state'
    })
    ;

    var drag = d3.behavior.drag()
    .on("drag", function( d, i) {
    var selection = d3.selectAll( '.selected');

    if( selection[0].indexOf( this)==-1) {
    selection.classed( "selected", false);
    selection = d3.select( this);
    selection.classed( "selected", true);
    }

    selection.attr("transform", function( d, i) {
    d.x += d3.event.dx;
    d.y += d3.event.dy;
    return "translate(" + [ d.x,d.y ] + ")"
    })
    // reappend dragged element as last
    // so that its stays on top
    this.parentNode.appendChild( this);
    d3.event.sourceEvent.stopPropagation();
    });
    gState.call( drag);

    gState.append( "circle")
    .attr({
    r : radius + 4,
    class : 'outer'
    })
    ;
    gState.append( "circle")
    .attr({
    r : radius,
    class : 'inner'
    })
    .on( "click", function( d, i) {
    var e = d3.event,
    g = this.parentNode,
    isSelected = d3.select( g).classed( "selected");

    if( !e.ctrlKey) {
    d3.selectAll( 'g.selected').classed( "selected", false);
    }

    d3.select( g).classed( "selected", !isSelected);

    // reappend dragged element as last
    // so that its stays on top
    g.parentNode.appendChild( g);
    })
    .on("mouseover", function(){
    d3.select(this).style( "fill", "aliceblue");
    })
    .on("mouseout", function() {
    d3.select(this).style("fill", "white");
    });
    ;

    gState.append( "text")
    .attr({
    'text-anchor' : 'middle',
    y : 4
    })
    .text( function( d) {
    return d.label;
    })
    ;

    gState.append( "title")
    .text( function( d) {
    return d.label;
    })
    ;

    svg
    .on( "mousedown", function() {
    if( !d3.event.ctrlKey) {
    d3.selectAll( 'g.selected').classed( "selected", false);
    }

    var p = d3.mouse( this);

    svg.append( "rect")
    .attr({
    rx : 6,
    ry : 6,
    class : "selection",
    x : p[0],
    y : p[1],
    width : 0,
    height : 0
    })
    })
    .on( "mousemove", function() {
    var s = svg.select( "rect.selection");

    if( !s.empty()) {
    var p = d3.mouse( this),
    d = {
    x : parseInt( s.attr( "x"), 10),
    y : parseInt( s.attr( "y"), 10),
    width : parseInt( s.attr( "width"), 10),
    height : parseInt( s.attr( "height"), 10)
    },
    move = {
    x : p[0] - d.x,
    y : p[1] - d.y
    }
    ;

    if( move.x < 1 || (move.x*2<d.width)) {
    d.x = p[0];
    d.width -= move.x;
    } else {
    d.width = move.x;
    }

    if( move.y < 1 || (move.y*2<d.height)) {
    d.y = p[1];
    d.height -= move.y;
    } else {
    d.height = move.y;
    }

    s.attr( d);

    // deselect all temporary selected state objects
    d3.selectAll( 'g.state.selection.selected').classed( "selected", false);

    d3.selectAll( 'g.state >circle.inner').each( function( state_data, i) {
    if(
    !d3.select( this).classed( "selected") &&
    // inner circle inside selection frame
    state_data.x-radius>=d.x && state_data.x+radius<=d.x+d.width &&
    state_data.y-radius>=d.y && state_data.y+radius<=d.y+d.height
    ) {

    d3.select( this.parentNode)
    .classed( "selection", true)
    .classed( "selected", true);
    }
    });
    }
    })
    .on( "mouseup", function() {
    // remove selection frame
    svg.selectAll( "rect.selection").remove();

    // remove temporary selection marker class
    d3.selectAll( 'g.state.selection').classed( "selection", false);
    })
    .on( "mouseout", function() {
    if( d3.event.relatedTarget.tagName=='HTML') {
    // remove selection frame
    svg.selectAll( "rect.selection").remove();

    // remove temporary selection marker class
    d3.selectAll( 'g.state.selection').classed( "selection", false);
    }
    });