trees
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
</style> | |
<body> | |
<svg width=960 height=500></svg> | |
</body> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script> | |
/* | |
Influenced by Miguel Ferreira: | |
https://codepen.io/MigFerreira/pen/RPZpgd | |
The original tree generation code is theirs. | |
I simplified some of the branch generation code and then added | |
code to allow for the animation. | |
*/ | |
// Tree configuration | |
var branches = []; | |
var seed = {i: 0, x1: 480, y1: 700, x2: 480, y2: 425, a: 0, l: 75, d:0, children: []}; // a = angle, l = length, d = depth | |
var child_angle = 0.6; // Angle delta, all child branches will angle between 0 - 0.6 radians away from the parent angle | |
var child_length = 0.85; // Length delta (factor), all child branches will be 0.85 the length of the parent | |
var maxDepth = 10; | |
// Recursively create the tree | |
function create_branch(b) { | |
branches.push(b); | |
if (b.d === maxDepth) | |
return; | |
// two branches, flip the i from -1 to 1 to handle "left and right" branches | |
for(var i = -1; i < 2; i = i + 2) { | |
var ang = b.a + (Math.random() * child_angle * i); | |
var newB = { | |
i: branches.length, | |
x1: b.x2, | |
y1: b.y2, | |
x2: b.x2 + (b.l * child_length) * Math.sin( ang ), | |
y2: b.y2 - (b.l * child_length) * Math.cos( ang ), | |
a: ang, | |
orig_a: ang, // for book-keeping in animation | |
animating: false, | |
animate_counter: 0, | |
l: b.l * child_length, | |
d: b.d + 1, | |
children: [] | |
}; | |
b.children.push(newB.i) | |
create_branch(newB); | |
} | |
} | |
function draw() { | |
var color = d3.scale.linear() | |
.domain([0, maxDepth]) | |
.range(["sienna","darkolivegreen"]); | |
d3.select('svg').selectAll('line') | |
.data(branches) | |
.enter() | |
.append('line') | |
.attr('id', function(d) { return "branch" + d.i; }) | |
.attr('x1', function(d) { return d.x1; }) | |
.attr('y1', function(d) { return d.y1; }) | |
.attr('x2', function(d) { return d.x2; }) | |
.attr('y2', function(d) { return d.y2; }) | |
.style('stroke-width', function(d) { return parseInt((maxDepth - d.d + 2) * .5) + 'px'; }) | |
.style('stroke', function(d) { return color(d.d); }); | |
} | |
function animate() { | |
// this runs every second, | |
// every 10 seconds, stop triggering new animations for 10 seconds to allow things to settle | |
// this ends up giving the perception of "breezes" | |
animationCounter += 1; | |
if(animationCounter == 20) { animationCounter = 0; } | |
else if(animationCounter % 20 > 10) { return }; | |
branches.forEach(function(branch) { | |
if(branch.animating == false && Math.random() < (branch.d / 500.0)) { | |
branch.animating = true; | |
moveBranch(branch); | |
} | |
}); | |
} | |
function moveBranch(branch) { | |
modifier = branch.d / 15; | |
branch.a = branch.orig_a + (Math.random() * modifier - (modifier/2.0)); | |
branch.x2 = branch.x1 + branch.l * Math.sin( branch.a ); | |
branch.y2 = branch.y1 - branch.l * Math.cos( branch.a ); | |
// This is probably "bad" d3 with regards to the data selection, | |
// it might even be easier to do this in jQuery or vanilla JS, | |
// the one benefit is to take advantage of d3 duration/ease animation functionality. | |
d3.select("#branch" + branch.i) | |
.data([branch]) | |
.transition() | |
.duration(3000) | |
.ease('linear') | |
.attr('x1', function(d) { return d.x1; }) | |
.attr('y1', function(d) { return d.y1; }) | |
.attr('x2', function(d) { return d.x2; }) | |
.attr('y2', function(d) { return d.y2; }) | |
.each('end', function(d) { | |
if(d.animating == true) { | |
if(d.animate_counter < 3) { // keep "swaying" for 3 more frames | |
d.animate_counter += 1; | |
moveBranch(d); | |
} else { | |
d.animating = false; | |
d.animate_counter = 0; | |
} | |
} | |
}); | |
// make the children move | |
branch.children.forEach(function(childIdx) { | |
var currChild = branches[childIdx]; | |
currChild.x1 = branch.x2; | |
currChild.y1 = branch.y2; | |
moveBranch(currChild); | |
}); | |
} | |
create_branch(seed); | |
draw(); | |
var animationCounter = 0; | |
setInterval(animate, 1000); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment