Skip to content

Instantly share code, notes, and snippets.

@mikefrey
Last active July 21, 2016 21:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikefrey/80772654c847cc80d49ce44da6a5445c to your computer and use it in GitHub Desktop.
Save mikefrey/80772654c847cc80d49ce44da6a5445c to your computer and use it in GitHub Desktop.
framed Example

This example demonstrates incorporating the d3 framed plugin with surface that can be zoomed and panned.

  • Click and drag on the grid to draw a frame to select nodes
  • Click and drag selected nodes to move them
  • Right-click and drag to to pan
  • Scroll to zoom

The framed plugin can be found here.

{
"nodes": [
{ "id": 1, "x": 40, "y": 40 },
{ "id": 2, "x": 80, "y": 40 },
{ "id": 3, "x": 120, "y": 40 },
{ "id": 4, "x": 160, "y": 40 },
{ "id": 5, "x": 200, "y": 40 },
{ "id": 6, "x": 240, "y": 40 },
{ "id": 7, "x": 280, "y": 40 },
{ "id": 8, "x": 320, "y": 40 },
{ "id": 9, "x": 360, "y": 40 },
{ "id": 10, "x": 400, "y": 40 },
{ "id": 11, "x": 440, "y": 40 },
{ "id": 12, "x": 40, "y": 80 },
{ "id": 13, "x": 80, "y": 80 },
{ "id": 14, "x": 120, "y": 80 },
{ "id": 15, "x": 160, "y": 80 },
{ "id": 16, "x": 200, "y": 80 },
{ "id": 17, "x": 40, "y": 120 },
{ "id": 18, "x": 80, "y": 120 },
{ "id": 19, "x": 120, "y": 120 },
{ "id": 20, "x": 160, "y": 120 },
{ "id": 21, "x": 200, "y": 120 },
{ "id": 22, "x": 320, "y": 120 },
{ "id": 23, "x": 360, "y": 120 }
]
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
rect.selection {
stroke: #54B0CB;
fill: rgba(84, 176, 203, 0.15);
shape-rendering: crispEdges;
vector-effect: non-scaling-stroke;
}
.node {
fill: steelblue;
stroke: black;
stroke-width:2px;
}
.node .selected {
fill: red;
}
.axis line {
fill: none;
stroke: #eee;
shape-rendering: crispEdges;
vector-effect: non-scaling-stroke;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
</svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
<script src="//rawgit.com/mikefrey/d3-framed/master/d3-framed.min.js"></script>
<script>
const nodeWidth = 16
const nodeHeight = 16
const margin = {top: -5, right: -5, bottom: -5, left: -5}
const width = 960 - margin.left - margin.right
const height = 500 - margin.top - margin.bottom
const zoom = d3.zoom()
.scaleExtent([0.5, 10])
.filter(() => event.button == 2 || event.type == 'wheel') // only pan on right-click
.on("zoom", zoomed)
.on('end', () => svg.classed('panning', false))
const drag = d3.drag()
.subject(d => d)
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
const frameSelect = d3.framed()
.items(() => d3.selectAll('.node circle'))
.on('end', nodesSelected)
const svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")")
.call(zoom)
.call(frameSelect)
.on('contextmenu', () => {
svg.classed('panning', true)
event.preventDefault() // disable context menu
})
const rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
const container = svg.append("g")
container.append("g")
.attr("class", "x axis")
.selectAll("line")
.data(d3.range(0, width, 20))
.enter().append("line")
.attr("x1", d => d)
.attr("y1", 0)
.attr("x2", d => d)
.attr("y2", height);
container.append("g")
.attr("class", "y axis")
.selectAll("line")
.data(d3.range(0, height, 20))
.enter().append("line")
.attr("x1", 0)
.attr("y1", d => d)
.attr("x2", width)
.attr("y2", d => d);
d3.json("data.json", function(error, data) {
container.append("g")
.attr("class", "node")
.selectAll("circle")
.data(data.nodes)
.enter().append("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", nodeWidth/2)
.call(drag)
.on('mouseover', d => d3.select(d3.event.target).classed('over', true))
.on('mouseout', d => d3.select(d3.event.target).classed('over', false))
});
function zoomed() {
const transform = d3.event.transform
container.attr("transform", `translate(${transform.x},${transform.y})scale(${transform.k})`);
}
let dragSelected
let dragPos = { x:0, y:0 }
function dragStarted(d) {
d3.event.sourceEvent.stopPropagation()
let selected = d3.selectAll('.node .selected')
if (!selected.size())
selected = d3.select(this)
selected.classed("dragging", true)
dragSelected = selected
dragPos.x = d3.event.x
dragPos.y = d3.event.y
}
function dragged(d) {
dragSelected
.each(n => {
n.x += d3.event.x - dragPos.x
n.y += d3.event.y - dragPos.y
})
.attr("cx", n => n.x)
.attr("cy", n => n.y)
dragPos.x = d3.event.x
dragPos.y = d3.event.y
}
function dragEnded(d) {
// d3.select(this)
dragSelected.classed("dragging", false)
}
function nodesSelected(nodes) {
if (nodes.size()) {
console.log(nodes)
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment