Public
Edited
Jan 5
10 forks
3 stars
Insert cell
Insert cell
Insert cell
circlesData = [
{ x: 50, y: 50, color: 'red' },
{ x: 100, y: 100, color: 'blue' },
{ x: 150, y: 150, color: 'green' },
]
Insert cell
Insert cell
{
// create outer svg element with size 200x200
const width = 200;
const height = 200;
const radius = 10;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
// add a background
svg.append('rect')
.attr('width', 200)
.attr('height', 200)
.attr('fill', '#F2ECF3');
// create one circle for each element in circlesData
// and set the position, color, and size
const circles = svg.selectAll('circle')
// copy the data so that we don't modify the original objects
.data(circlesData.map(d => ({...d})))
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('fill', d => d.color)
.attr('r', radius);
// new code for dragging
// when the drag starts
function onDragStart(event, d) {
// select the circle that's being dragged
d3.select(this)
// make the circle being dragged appear on top
// of other circles that it is dragged over
.raise()
// change the color and border of the circle
.attr('fill', 'yellow')
.attr('stroke', 'black');
}
// when the drag is in progress
function onDrag(event, d) {
// update d.x
d.x = event.x;
// move the circle
d3.select(this)
// update the position of the circle
.attr('cx', d.x)
// you can set d.y and return the value in one line
.attr('cy', d.y = event.y);
}
// when the drag ends
function onDragEnd(event, d) {
// reset the circle's color
d3.select(this)
.attr('fill', d.color)
.attr('stroke', 'none');
}
const drag = d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd);
// apply the drag behavior to the circles
circles.call(drag);
return svg.node();
}
Insert cell
Insert cell
{
// create outer svg element with size 200x200
const width = 200;
const height = 200;
const circleRadius = 10;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
// add a background
svg.append('rect')
.attr('width', 200)
.attr('height', 200)
.attr('fill', '#F2ECF3');
// create one circle for each element in circlesData
// and set the position, color, and size
const circles = svg.selectAll('circle')
// copy the data so that we don't modify the original
.data(circlesData.map(d => ({...d})))
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('fill', d => d.color)
.attr('r', circleRadius);
// new code for dragging
// when the drag starts
function onDragStart(event, d) {
d3.select(this)
// make the circle being dragged appear on top
// of other circles that it is dragged over
.raise()
// change the color and border of the circle
.attr('fill', 'yellow')
.attr('stroke', 'black');
}
// when the drag is in progress
function onDrag(event, d) {
d3.select(this)
// update the position of the circle
// in this example, we are not updating d.x and d.y
.attr('cx', event.x)
.attr('cy', event.y);
}
// when the drag ends
function onDragEnd(event, d) {
// reset the circle's color
d3.select(this)
.attr('fill', d.color)
.attr('stroke', 'none');
}
const drag = d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd);
// apply the drag behavior to the circle
circles.call(drag);
return svg.node();
}
Insert cell
Insert cell
{
const width = 200;
const height = 200;
const radius = 10;
// create outer svg element with size 200x200
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
// add a background
svg.append('rect')
.attr('width', 200)
.attr('height', 200)
.attr('fill', '#F2ECF3');
// create one circle for each element in circlesData
// and set the position, color, and size
const circles = svg.selectAll('circle')
.data(circlesData)
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('fill', d => d.color)
.attr('r', radius);
// apply the drag behavior to the circle
circles.call(circleDrag(width, height, radius));
return svg.node();
}
Insert cell
function circleDrag(width, height, radius) {
// when the drag starts
function onDragStart(event, d) {
d3.select(this)
// make the circle being dragged appear on top
// of other circles that it is dragged over
.raise()
// change the color and border of the circle
.attr('fill', 'yellow')
.attr('stroke', 'black');
}
// when the drag is in progress
function onDrag(event, d) {
d3.select(this)
// update the position of the circle
.attr('cx', d.x = event.x)
.attr('cy', d.y = event.y);
}
// when the drag ends
function onDragEnd(event, d) {
// reset the circle's color
d3.select(this)
.attr('fill', d.color)
.attr('stroke', 'none');
}
const drag = d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd);
return drag;
}
Insert cell
Insert cell
Insert cell
Insert cell
{
const networkWidth = 300;
const networkHeight = 300;
const nodeRadius = 10;

const svg = d3.create('svg')
.attr('width', networkWidth)
.attr('height', networkHeight);
// copy the data for nodes and links so that the original isn't modified
const nodesForce = nodes.map(d => ({...d}));
const linksForce = links.map(d => ({...d}));
const simulation = d3.forceSimulation(nodesForce)
.force('link', d3.forceLink(linksForce).id(d => d.id))
.force('center', d3.forceCenter(networkWidth / 2, networkHeight / 2))
.force('manyBody', d3.forceManyBody().strength(-100))
.force('collision', d3.forceCollide(nodeRadius));
// add lines for edges, we don't need to set their positions yet
const edges = svg.append('g')
.selectAll('line')
.data(linksForce)
.join('line')
.attr('stroke', '#d3d3d3')
.attr('fill', 'none')
.attr('stroke-width', 1)
// add a group for each node, we don't need to set their positions yet
const vertices = svg.append('g')
.selectAll('g')
.data(nodesForce)
.join('g');
// add circle to each group
vertices.append('circle')
.attr('r', nodeRadius)
.attr('fill', 'steelblue');
// add text to each group
vertices.append('text')
.attr('fill', 'white')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle')
.attr('font-family', 'sans-serif')
.attr('font-size', '16')
.attr('pointer-events', 'none')
.text(d => d.id);
// update the positions of the vertices and edges on each simulation tick
simulation.on('tick', () => {
vertices.attr('transform', d => `translate(${d.x},${d.y})`);
edges.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y);
})
// apply dragging behavior
vertices.call(networkDrag(simulation));

// this line is not needed off of observable
// it stops the current simulation when the cell is re-run
invalidation.then(() => simulation.stop());
return svg.node();
}
Insert cell
Insert cell
networkDrag = (simulation) => {
// when the drag starts
// note that d3.select(this) selects the <g> that contains
// the circle and the text
function onDragStart(event, d) {
}
// when the drag is in progress
// d contains the data element for this node
function onDrag(event, d) {

simulation.alpha(1).restart();
}
// when the drag ends
// note that d3.select(this) selects the <g> that contains
// the <circle> and the <text>
function onDragEnd(event, d) {

}
const drag = d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd);
return drag;
}
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more