|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
.active { |
|
stroke: #000; |
|
stroke-width: 2px; |
|
} |
|
|
|
</style> |
|
<svg width="960" height="500"></svg> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
|
|
var CLICK_DISTANCE = 4, |
|
CLICK_DISTANCE_2 = CLICK_DISTANCE * CLICK_DISTANCE; |
|
|
|
var svg = d3.select("svg"), |
|
width = +svg.attr("width"), |
|
height = +svg.attr("height"), |
|
radius = 32; |
|
|
|
var circles = d3.range(20).map(function() { |
|
return { |
|
x: Math.round(Math.random() * (width - radius * 2) + radius), |
|
y: Math.round(Math.random() * (height - radius * 2) + radius) |
|
}; |
|
}); |
|
|
|
var color = d3.scaleOrdinal() |
|
.range(d3.schemeCategory20); |
|
|
|
svg.selectAll("circle") |
|
.data(circles) |
|
.enter().append("circle") |
|
.attr("cx", function(d) { return d.x; }) |
|
.attr("cy", function(d) { return d.y; }) |
|
.attr("r", radius) |
|
.style("fill", function(d, i) { return color(i); }) |
|
.on("click", clicked) |
|
.call(d3.drag() |
|
.clickDistance(CLICK_DISTANCE) |
|
.on("start", dragstarted) |
|
.on("drag", dragged) |
|
.on("end", dragended)); |
|
|
|
function dragstarted(d) { |
|
d.startX = d3.event.sourceEvent.clientX; |
|
d.startY = d3.event.sourceEvent.clientY; |
|
} |
|
|
|
function dragged(d) { |
|
var e = d3.select(this), |
|
dStartX = d.startX - d3.event.sourceEvent.clientX, |
|
dStartY = d.startY - d3.event.sourceEvent.clientY; |
|
|
|
if (dStartX * dStartX + dStartY * dStartY > CLICK_DISTANCE_2 && |
|
!e.classed("active")) { |
|
|
|
e.raise().classed("active", true); |
|
} |
|
|
|
e.attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y); |
|
} |
|
|
|
function dragended(d) { |
|
d3.select(this).classed("active", false); |
|
} |
|
|
|
function clicked(d) { |
|
d3.select(this).transition() |
|
.attr("r", 64) |
|
.transition() |
|
.attr("r", 32); |
|
} |
|
|
|
</script> |