Skip to content

Instantly share code, notes, and snippets.

@jstcki
Last active June 1, 2016 07:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jstcki/60ac4b1adc3cc1ecb1a8a36ff69ae165 to your computer and use it in GitHub Desktop.
Save jstcki/60ac4b1adc3cc1ecb1a8a36ff69ae165 to your computer and use it in GitHub Desktop.
Draggable Blob

Drag the blob to deform it. Press the shift key to show the underlying force simulation's nodes and links.

Uses d3-shape's line() and curveBasisClosed() functions to draw a blob from a graph.

Inspired by Mike Bostock's Force Dragging III example.

<!DOCTYPE html>
<meta charset="utf-8">
<canvas width="960" height="500"></canvas>
<script src="https://d3js.org/d3.v4.0.0-alpha.44.min.js"></script>
<script>
var canvas = document.querySelector("canvas"),
context = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height
debug = false;
var numPoints = 5
var radius = 200
var nodes = d3.range(numPoints).map(function(d) {
return {
id: d,
x: width / 2 + radius * Math.cos(Math.PI * 2 / numPoints * d) + Math.random() * 30 - 15,
y: height / 2 + radius * Math.sin(Math.PI * 2 / numPoints * d) + Math.random() * 30 - 15
};
});
var links = nodes.map(function(n) {
return {
source: n.id,
target: n.id + 1 === nodes.length ? 0 : n.id + 1
}
});
var drawShape = d3.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.curve(d3.curveBasisClosed)
.context(context)
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }).strength(0.1))
.force("charge", d3.forceManyBody().strength(-1000))
.force("x", d3.forceX(function(d) { return d.x; }).strength(0.05))
.force("y", d3.forceY(function(d) { return d.y; }).strength(0.05));
render({
nodes: nodes,
links: links
});
function render(graph) {
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
d3.select(canvas)
.call(d3.drag()
.container(canvas)
.subject(dragsubject)
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
d3.select("body")
.on("keydown", toggleDebug)
.on("keyup", toggleDebug);
function ticked() {
context.clearRect(0, 0, width, height);
context.beginPath();
// Draw the shape from all nodes
drawShape(graph.nodes);
context.fillStyle = "#f55";
context.fill();
if (debug) {
context.beginPath();
graph.links.forEach(drawLink);
context.strokeStyle = "#003B5C";
context.stroke();
context.beginPath();
graph.nodes.forEach(drawNode);
context.fillStyle = "#003B5C";
context.fill();
context.strokeStyle = "#fff";
context.stroke();
}
}
function toggleDebug() {
debug = d3.event.shiftKey;
ticked();
}
}
function dragsubject() {
return simulation.find(d3.event.x, d3.event.y);
}
function dragstarted() {
if (!d3.event.active) simulation.alphaTarget(0.5).restart()
simulation.fix(d3.event.subject);
}
function dragged() {
simulation.fix(d3.event.subject, d3.event.x, d3.event.y);
}
function dragended() {
if (!d3.event.active) simulation.alphaTarget(0);
simulation.unfix(d3.event.subject);
}
function drawLink(d) {
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
}
function drawNode(d) {
context.moveTo(d.x + 3, d.y);
context.arc(d.x, d.y, 3, 0, 2 * Math.PI);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment