Skip to content

Instantly share code, notes, and snippets.

@eesur
Last active May 22, 2017 19:43
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 eesur/b8a5d5c6ccd92bfc4f113ae1137c50ad to your computer and use it in GitHub Desktop.
Save eesur/b8a5d5c6ccd92bfc4f113ae1137c50ad to your computer and use it in GitHub Desktop.
d3 | swarm with force
function render(nodes) {
var svg = d3.select('svg')
var width = +svg.attr('width')
var height = +svg.attr('height')
var vis = svg.select('g.vis')
var axis = svg.select('g.axis')
var f = d3.format('.2f')
var xScale = d3.scaleLinear()
.domain([0, 100])
.range([0, 900])
var simulation = d3.forceSimulation(nodes)
.force('x', d3.forceX().x(function (d) { return xScale(d.value); }).strength(1))
.force('y', d3.forceY().y(function (d) { return height/2; }).strength(1))
.force('collision', d3.forceCollide(7))//.radius(d => d.radius + 1))
simulation.stop()
for (var i = 0; i < nodes.length; ++i){
simulation.tick()
}
//append circles
var u = vis.selectAll('circle')
.data(nodes)
u.enter()
.append('circle')
// .attr('r', d => d.radius + 1))
.attr('r', 5)
.style('fill', function (d) { return d.color; })
.merge(u)
.attr('cx', function (d) { return d.x; })
.attr('cy', function (d) { return d.y; })
.on('mouseover', function(d) {
d3.select('#js-info').text(("size: " + (f(d.radius)) + " | x: \n " + (f(d.value))))
})
u.exit().remove()
// render an axis
var xAxis = d3.axisBottom().scale(xScale)
axis.call(xAxis)
}

testing a simple swarm chart with force to see how much accuracy is lost with increase of nodes

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link href="https://unpkg.com/basscss@8.0.2/css/basscss.min.css" rel="stylesheet">
<style>
body{
font-family: Consolas, monaco, monospace;
position: relative;
padding: 20px;
color: #FDBB30;
background: #130C0E;
}
circle {
fill: #ec008c;
opacity: 0.6;
}
circle:hover {
opacity: 1;
}
.axis path, .axis line {
stroke: #FDBB30;
}
.axis text {
fill: #FDBB30;
font-family: Consolas, monaco, monospace;
}
input[type="range"] {
-webkit-appearance:none;
width: 100%;
height:2px;
background: #FDBB30;
background-position:center;
background-repeat:no-repeat;
margin: auto;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance:none;
width: 20px;
height: 20px;
border-radius: 100%;
background: #130C0E;
position:relative;
border: 3px solid #FDBB30;
z-index: 2;
cursor: pointer;
}
input:focus {
outline: none;
}
span {
letter-spacing: 3px;
color: #FDBB30;
}
</style>
</head>
<body>
<header class="fixed top-0 left-0 ml2 mt1">
<p class="caps">Number of points: <span id="slider-amount">34</span></p>
<input type="range" id="point-amount" value="34">
</header>
<svg width="960" height="400">
<g class="vis" transform="translate(15, 10)"></g>
<g class="axis" transform="translate(15, 260)"></g>
</svg>
<footer>
<span id="js-info"></span>
</footer>
<script src="//d3js.org/d3.v4.min.js" charset="utf-8"></script>
<!-- d3 code -->
<script src=".script-compiled.js" charset="utf-8"></script>
<script>
// render some dummy data
var nodes = function(value) {
return d3.range(value).map((d) => {
return {
radius: Math.sqrt(d3.randomUniform(10, 100)()),
value: d3.randomUniform(10, 100)()
}
})
}
// render swarm
render(nodes(34))
// set a min and max to to the range slider
document.getElementById('point-amount').max = '300';
document.getElementById('point-amount').min = '5';
// when the input range changes re-render
d3.select('#point-amount').on('input', function() {
var value = +this.value
d3.select('#slider-amount').text(value)
render(nodes(value));
})
</script>
</body>
</html>
function render(nodes) {
const svg = d3.select('svg')
const width = +svg.attr('width')
const height = +svg.attr('height')
const vis = svg.select('g.vis')
const axis = svg.select('g.axis')
const f = d3.format('.2f')
const xScale = d3.scaleLinear()
.domain([0, 100])
.range([0, 900])
let simulation = d3.forceSimulation(nodes)
.force('x', d3.forceX().x(d => xScale(d.value)).strength(1))
.force('y', d3.forceY().y(d => height/2).strength(1))
.force('collision', d3.forceCollide(7))//.radius(d => d.radius + 1))
simulation.stop()
for (let i = 0; i < nodes.length; ++i){
simulation.tick()
}
//append circles
const u = vis.selectAll('circle')
.data(nodes)
u.enter()
.append('circle')
// .attr('r', d => d.radius + 1))
.attr('r', 5)
.style('fill', d => d.color)
.merge(u)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.on('mouseover', function(d) {
d3.select('#js-info').text(`size: ${f(d.radius)} | x:
${f(d.value)}`)
})
u.exit().remove()
// render an axis
const xAxis = d3.axisBottom().scale(xScale)
axis.call(xAxis)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment