|
<head> |
|
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.7.0/d3.min.js"></script> |
|
<link rel="stylesheet" href="style.css"> |
|
</head> |
|
|
|
<body> |
|
<svg id="canvas"> |
|
<g id="trails"></g> |
|
<g id="bodies"></g> |
|
</svg> |
|
|
|
<script> |
|
const width = window.innerWidth, height = window.innerHeight; |
|
|
|
const gravity = 0.00005, // Regulates mechanics speed |
|
bodies = [ |
|
{ id: 'sun', r: 20, initialState: { x: 0, y: 0 } }, |
|
{ id: 'planet', r: 8, initialState: { y: -height / 4, vx: 0.5 * height * Math.sqrt(gravity) } }, |
|
{ id: 'moon', r: 2, initialState: { y: -height * 3/8, vx: 0.25 * height * Math.sqrt(gravity * 100) } } |
|
], |
|
links = [ |
|
{ source: 'planet', target: 'sun-anchor', attraction: gravity }, |
|
{ source: 'moon', target: 'planet-anchor', attraction: gravity * 100 } // Higher attraction = more revolutions |
|
]; |
|
|
|
// Generate body anchors |
|
const anchors = bodies.map(body => { return { id: body.id + '-anchor', body: body } }); |
|
|
|
d3.forceSimulation() |
|
.alphaDecay(0) |
|
.velocityDecay(0) |
|
.nodes([...bodies, ...anchors]) |
|
.force("gravitate-around", d3.forceLink(links) |
|
.id(d => d.id) |
|
.distance(0) |
|
.strength(link => link.attraction || 0.1) |
|
) |
|
.on("tick", ticked); |
|
|
|
// Set initial states |
|
bodies.forEach(body => { |
|
for (let k in body.initialState) { |
|
body[k] = body.initialState[k]; |
|
} |
|
}); |
|
|
|
// Add orbit trails |
|
d3.timer(() => { |
|
d3.selectAll('g.trail') |
|
.append('circle') |
|
.attr('r', 1.5) |
|
.attr('cx', d => d.x) |
|
.attr('cy', d => d.y) |
|
.transition().duration(10000) |
|
.style('opacity', 0) |
|
.remove(); |
|
}); |
|
|
|
// Size canvas |
|
d3.select('#canvas') |
|
.attr('width', width) |
|
.attr('height', height) |
|
.attr('viewBox', `${-width/2} ${-height/2} ${width} ${height}`); |
|
|
|
// |
|
|
|
function ticked() { |
|
var body = d3.select('#bodies').selectAll('.body') |
|
.data(bodies); |
|
|
|
body.exit().remove(); |
|
body.merge( |
|
body.enter().append('circle') |
|
.attr('class', 'body') |
|
.attr('id', d => d.id) |
|
.attr('r', d => d.r) |
|
) |
|
.attr('cx', d => d.x) |
|
.attr('cy', d => d.y); |
|
|
|
// Add trail elements |
|
var trails = d3.select('#trails').selectAll('.trail') |
|
.data(bodies); |
|
|
|
trails.exit().remove(); |
|
trails.enter().append('g').attr('class', 'trail'); |
|
|
|
fixAnchors(); |
|
} |
|
|
|
function fixAnchors() { |
|
anchors.forEach(anchor => { |
|
anchor.fx = anchor.body.x; |
|
anchor.fy = anchor.body.y; |
|
}); |
|
} |
|
|
|
</script> |
|
</body> |