|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
canvas { |
|
margin: 1px; |
|
border-radius: 1000px; |
|
box-shadow: 2px 2px 6px grey; |
|
cursor: crosshair; |
|
} |
|
</style> |
|
<body> |
|
<script src="https://d3js.org/d3.v4.js"></script> |
|
<script> |
|
|
|
var cbw = 1, //canvas border width |
|
cm = 10, //canvas margin |
|
totalWidth = 500, |
|
totalHeight = 500, |
|
width = totalWidth-(cm+cbw)*2, |
|
height = totalHeight-(cm+cbw)*2, |
|
midWidth = width/2, |
|
midHeight = height/2, |
|
τ = 2 * Math.PI; |
|
var sampleRate = 3, |
|
pointCount = 360/sampleRate; |
|
initialRadius = height/2; |
|
|
|
var nodes = d3.range(pointCount).map(function(i) { |
|
var degree = sampleRate*i, |
|
radian = degree*τ/360; |
|
return { |
|
x: midWidth+initialRadius*Math.cos(radian), |
|
y: midHeight+initialRadius*Math.sin(radian), |
|
color: d3.hsl(degree, 1, 0.5) |
|
}; |
|
}); |
|
|
|
var links = nodes.slice(1).map( function (n,i) { |
|
return { |
|
source: nodes[i], |
|
target: n |
|
} |
|
}); |
|
|
|
var force = d3.forceSimulation() |
|
.nodes(nodes.slice()) |
|
.force("charge", d3.forceManyBody().strength(-25)) |
|
.force("x", d3.forceX()) |
|
.force("y", d3.forceY()) |
|
.force("dance", function(alpha) { |
|
var m = 50*alpha; |
|
for(var i=1; i < nodes.length; i++) { |
|
var d = Math.sqrt((nodes[i-1].x - nodes[i].x)*(nodes[i-1].x - nodes[i].x) + (nodes[i-1].y - nodes[i].y)*(nodes[i-1].y - nodes[i].y)); |
|
nodes[i].vx += m*(nodes[i-1].x - nodes[i].x)/d; |
|
nodes[i].vy += m*(nodes[i-1].y - nodes[i].y)/d; |
|
} |
|
}) |
|
.force("center", d3.forceCenter(width / 2, height / 2)) |
|
.on("tick", ticked); |
|
|
|
var canvas = d3.select("body").append("canvas") |
|
.attr("width", width) |
|
.attr("height", height) |
|
.on("ontouchstart" in document ? "touchmove" : "mousemove", moved); |
|
|
|
var context = canvas.node().getContext("2d"); |
|
|
|
function moved() { |
|
var p1 = d3.mouse(this); |
|
nodes[0].fx = p1[0]; |
|
nodes[0].fy = p1[1]; |
|
force.alpha(0.1).restart(); |
|
} |
|
|
|
function ticked() { |
|
context.lineWidth = 1; |
|
context.strokeStyle = "#bbb"; |
|
|
|
context.clearRect(0, 0, width, height); |
|
|
|
//begin: draw segments |
|
context.beginPath(); |
|
context.moveTo(links[0].source.x, links[0].source.y); |
|
for (var i = 1, n = links.length; i < n; ++i) { |
|
var link = links[i]; |
|
context.lineTo(link.target.x, link.target.y); |
|
} |
|
context.stroke(); |
|
//end: draw segments |
|
|
|
//begin: draw points |
|
for (var i = 0, n = nodes.length; i < n; ++i) { |
|
var node = nodes[i]; |
|
context.beginPath(); |
|
context.moveTo(node.x, node.y); |
|
context.arc(node.x, node.y, 1.5, 0, τ); |
|
context.fillStyle = node.color; |
|
context.stroke(); |
|
context.fill(); |
|
} |
|
//end: draw points |
|
|
|
} |
|
|
|
</script> |