Skip to content

Instantly share code, notes, and snippets.

@pepgma
Forked from lgersman/README.md
Last active August 3, 2019 13:59
Show Gist options
  • Save pepgma/0a56e4e52696cce017cb435386ebb79d to your computer and use it in GitHub Desktop.
Save pepgma/0a56e4e52696cce017cb435386ebb79d to your computer and use it in GitHub Desktop.
My mod: support d3.v4 (Tested on 4.9.1). Original text: improved version of my d3.js selection frame example (https://gist.github.com/lgersman/5310854) supporting draggable/selectable nodes
  • Click into the drawing area to start the selection frame
  • move the mouse to resize the selection frame
  • Release the mouse button to resize the selection frame

new features :

  • circles are draggable
  • circles can be selected (multiple selections possible by pressing CTRL while clicking a circle)
  • the selection frame selects all circles within the frame (by pressing CTRL the selected circles will be appended to current selection)
  • multiple selected circles will be dragged simultaneous

See it live : http://bl.ocks.org/lgersman/5311083

Modification: converted to support d3.v4

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;
}
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);
console.log(gStates);
var gState = gStates.enter().append("g")
.merge(gStates)
.attr("transform", function( d) {return "translate("+ [d.x,d.y] + ")";})
.attr('class','state');
console.log(gState);
var drag = d3.drag()
.on("drag", function( d, i) {
var selection = d3.selectAll( '.selected');
// If current state not in 'selected' class
// make it now the selection
if( selection.nodes().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)
.attr('class', 'outer');
gState.append( "circle")
.attr('r', radius)
.attr('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')
.attr('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)
.attr('ry', 6)
.attr('class', "selection")
.attr('x', p[0])
.attr('y', p[1])
.attr('width',0)
.attr('height',0);
})
.on( "mousemove", function() {
var s = svg.select( "rect.selection");
if( !s.empty()) {
let p = d3.mouse(this);
let d = {};
d.x = parseInt( s.attr('x'), 10);
d.y = parseInt( s.attr('y'), 10);
d.width = parseInt( s.attr('width'), 10);
d.height = parseInt( s.attr('height'), 10);
let move = {};
move.x = p[0] - d.x;
move.y = p[1] - d.y;
// Calculate new properties of selection rectangle
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('x', d.x)
.attr('y', d.y)
.attr('width', d.width)
.attr('height', d.height);
// 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() {
var s = svg.select( "rect.selection");
if( !s.empty() && 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);
}
});
<!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="https://d3js.org/d3.v4.min.js"></script>
<script src="app.js"></script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment