This bottle of bubbles demonstrates the use of d3.timer for animation. The molecule reflections are computed using functions in the geoemetry_methods.js
file. See the previous Path-Line Intersection and Path-Line Reflection blocks for details.
xxxxxxxxxx
<meta charset="utf-8">
<style>
.molecule {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.button {
float: right;
}
.controls {
position: absolute;
width: 940px;
padding: 10px;
z-index: 1;
}
</style>
<body>
<div class="controls">
<input id="slider" type="range" min="0.1" max="10" value="2.5" step="0.1">Speed
<input class="button" id="button" type="button" name="updateButton" value="Add Molecule">
</div>
<svg width="960px" height="500px">
<path fill="none" stroke="#000000" stroke-miterlimit="10" d="M512.049,82.693c-8.46,3.561-5.522,11.094-5.522,20.17
c0,7.092,0.71,14.147,4.609,20.213c9.838,15.304,21.579,22.363,35.181,33.957c22.201,18.925,20.957,44.126,20.957,70.669
c0,47.12,0,94.24,0,141.36c0,18.958,0,37.916,0,56.874c0,5.832,2.606,22.086-7.904,22.086c-26.991,0-134.957,0-161.948,0
c-10.51,0-7.904-16.254-7.904-22.086c0-18.958,0-37.916,0-56.874c0-47.12,0-94.24,0-141.36c0-26.544-1.244-51.745,20.957-70.669
c13.601-11.594,25.343-18.654,35.181-33.957c3.899-6.066,4.609-13.121,4.609-20.213c0-9.077,2.938-16.609-5.522-20.17"/>
</svg>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="geometry_methods.js"></script>
<script>
var speed = 2.5;
var n_molecules = 17;
var n_segments = 100;
var svg = d3.select("svg");
var molecules_g = svg.append("g");
var highlights = svg.append("g");
// bottle path
var path = svg.select("path");
var pathEl = path.node();
var pathLength = pathEl.getTotalLength();
// uses functions in geoemetry_methods.js
// to find where and how the molecule will bounce next
function compute_next_bounce(d, pathEl, n_segments) {
var init_pt = {x: d.x, y: d.y};
var dir_line = {x1: d.x + 0.0001 * d.dx, y1: d.y + 0.0001 * d.dy,
x2: d.x + 2000 * d.dx, y2: d.y + 2000 * d.dy};
var intersection = first_path_line_intersection(pathEl, dir_line, n_segments);
if (typeof(intersection) != "string") {
var int_pt = intersection[0];
var int_seg = intersection[1];
var angle = angle_btwn_lines(dir_line, int_seg);
var reflection_vector = compute_reflection( int_pt, angle, int_seg );
var dir_length = dist_btwn_pts(init_pt, int_pt);
} else {
var int_pt = {x: 'out', y: 'out'};
var reflection_vector = {dx: 'out', dy: 'out'};
var dir_length = 1000;
}
d.int_x = int_pt.x;
d.int_y = int_pt.y;
d.refl_dx = reflection_vector.dx;
d.refl_dy = reflection_vector.dy;
d.dir_length = dir_length;
d.dir_length_run = 0;
}
// adding a new molecule and setting its initial values
// (for animation convenience, we also pre-compute its next bounce)
var molecule_count = 0;
function add_molecule() {
var init_pt = {x: 400 + 150 * Math.random(),
y: 200 + 150 * Math.random()};
var init_dir = {x: -1 + 2 * Math.random(),
y: -1 + 2 * Math.random()};
var init_dir_length = dist_btwn_pts({x: 0, y: 0}, init_dir);
init_dir.x = init_dir.x / init_dir_length;
init_dir.y = init_dir.y / init_dir_length;
var d = {'id': molecule_count,
'x': init_pt.x, 'y': init_pt.y,
'dx': init_dir.x, 'dy': init_dir.y};
compute_next_bounce(d, pathEl, n_segments);
molecule_data.push(d);
molecule_count += 1;
}
// initial molecule data
var molecule_data = [];
for (var i=0; i<n_molecules; i++) {
add_molecule();
}
// animation with d3.timer
var previous_time = 0;
d3.timer(function(elapsed) {
var t = elapsed - previous_time;
// update data
molecule_data.forEach(function(d) {
d.dir_length_run += speed;
if (d.dir_length_run >= d.dir_length) {
d.x = d.int_x;
d.y = d.int_y;
d.dx = d.refl_dx;
d.dy = d.refl_dy;
if (d.y > 0) {
compute_next_bounce(d, pathEl, n_segments);
}
}
d.x = d.x + speed * d.dx;
d.y = d.y + speed * d.dy;
});
molecule_data = molecule_data.filter(function(d){
return ((d.y > 0) && (d.x > 0) && (d.x < 960));
});
// update circles
var m = molecules_g.selectAll(".molecule")
.data(molecule_data, function(d){ return d.id; });
m.enter().append("circle")
.attr("class", "molecule")
.attr("r", 3);
m
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; });
m.exit().remove();
// loop
previous_time = elapsed;
});
// slider and button updates
d3.select("#slider").on("change", function() {
speed = +this.value;
});
d3.select("#button").on("click", function() {
add_molecule(molecule_count);
molecule_count += 1;
});
</script>
https://d3js.org/d3.v3.min.js