You can overlap text div above the moving partial animations. The following example use HTML 5 canvas to achieve the animation.
Here is the example:
Example
<h2>
Binary Search Tree (BST)</h2>
<div>
<canvas id="canvas" width=800 height=600></canvas>
</div>
<div id="buttons">
<button id="explosion">size(), get() and put()</button>
<button id="fallout">min(), floor(), select() and rank()</button>
<button id="spin">deletMin() and delet()</button>
<button id="amoeba">print() and keys()</button>
<div id="colors">
<div class="red box"></div>
<div class="green box"></div>
<div class="blue box"></div>
</div>
<button style="display: none;">Number of particles:<br><i id="particles">0</i></button>
<input style="display: none;" type="range" min="1" max="10000" value="1000" id="slider">
</div>
<style>
#menu {
color: olive;
text-align: center;
margin-bottom: 0;
padding-bottom: 0;
line-height: 1em;
font-weight: bold;
}
body {
display: flex;
flex-direction: row;
}
#buttons {
margin: 10px;
/* padding: 10px; */
display: flex;
flex-direction: column;
}
#colors {
margin: 10px;
/* padding: 10px; */
display: flex;
flex-direction: row;
justify-content: space-evenly;
/* background: grey; */
}
.box {
width: 20px;
height: 20px;
}
.red {
background: red;
}
.green {
background: green;
}
.blue {
background: blue;
}
</style>
<script>
class PixelManipulation {
constructor(canvas) {
this.context = canvas.getContext("2d");
this.width = canvas.width;
this.height = canvas.height;
this.image = this.context.getImageData(0, 0, this.width, this.height);
}
getImage() {
this.image = this.context.getImageData(0, 0, this.width, this.height);
}
setImage() {
this.context.putImageData(this.image, 0, 0);
}
setPixel(x, y, red, green, blue) {
const pixelIndex = (y * this.width + x) * 4;
this.image.data[pixelIndex] = red;
this.image.data[pixelIndex + 1] = green;
this.image.data[pixelIndex + 2] = blue;
}
fillColor(red, green, blue, alpha = 255) {
for (let i = 0; i < this.width * this.height * 4; i+=4) {
this.image.data[i] = red;
this.image.data[i + 1] = green;
this.image.data[i + 2] = blue;
this.image.data[i + 3] = alpha;
}
}
fillText(choice) {
this.context.font = "30px Arial";
//this.context.fillStyle = "#ff0000";
this.context.fillStyle = "#ffea00";
this.context.fillText(choice,10,50);
}
}
class Particle {
constructor() {
this.x;
this.y;
this.direction;
this.speed;
this.init;
this.update;
}
}
class Swarm {
constructor(canvas, amount = 10000, color = 1) {
this.size = amount;
this.particles = [];
this.pixels = new PixelManipulation(canvas);
this.color = color;
this.style = this.initSpin;
this.update = this.updateSpin;
this.code = 'great code you are writing';
}
setPattern(pattern) {
switch(pattern) {
case "fallout": {
this.style = this.initFallout;
this.update = this.updateFallout;
this.code = 'code 1';
break;
}
case "spin": {
this.style = this.initSpin;
this.update = this.updateSpin;
this.code = 'code 2';
break;
}
case "amoeba": {
this.style = this.initAmoeba;
this.update = this.updateAmoeba;
this.code = 'code 3';
break;
}
case "explosion": {
this.style = this.initExplosion;
this.update = this.updateExplosion;
this.code = 'code 4';
break;
}
}
this.init();
};
setColor(color) {
switch(color) {
case 0: {
this.code = 'text 1';
break;
}
case 1: {
this.code = 'text 2';
break;
}
case 2: {
this.code = 'text 3';
break;
}
}
this.color = color;
};
setParticles(value) {
this.size = value;
this.init();
}
init() {
; // 0 = red, 1 = green, 2 = blue
this.particles = [];
for(let i = 0; i < this.size; i++) {
this.particles.push(new Particle);
// this.particles[i].update = this.particles[i].updateSpin;
this.particles[i].update = this.update;
this.particles[i].init = this.style;
this.particles[i].init();
}
this.pixels.fillColor(0, 0, 0);
this.pixels.setImage();
this.pixels.fillText(this.code);
}
initFallout() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
// this.update = this.updateFallout;
}
updateFallout(delta) {
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
this.x = -1.10;
this.y = -1.10
}
}
initExplosion() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
this.speed *= this.speed/2;
// this.update = this.updateExplosion;
}
updateExplosion(delta) {
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
// this.init();
this.x = -1.10;
this.y = -1.10
}
}
initSpin() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
this.speed *= this.speed/2;
// this.update = this.updateSpin;
}
updateSpin(delta) {
this.direction += 0.01;
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
// this.init();
this.x = -1.10;
this.y = -1.10
}
}
initAmoeba() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
this.speed *= this.speed/2;
// this.update = this.updateAmoeba;
}
updateAmoeba(delta) {
this.direction += Math.random();;
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
// this.init();
this.x = -1.10;
this.y = -1.10
}
}
render(delta) {
const width = 800;
const height = 600;
const oldPixels = this.pixels.context.getImageData(0, 0, width, height).data;
for(let y=0; y<height; y++) {
for(let x = 0; x<width; x++) {
let redTotal = 0;
for(let row = -1; row <=1; row++) {
for(let col = -1; col <=1; col++) {
let currentX = x + col;
let currentY = y + row;
if(currentX >= 0 && currentX < width && currentY >= 0 && currentY < height) {
let red = oldPixels[4*(currentY * width + currentX) + this.color]
redTotal += red;
}
}
}
this.pixels.image.data[4*(y * width + x) + this.color] = redTotal / 9;
}
}
for(let i = 0; i < this.size; i++) {
const p = this.particles[i];
p.update(delta);
const xPos = Math.floor((1 + p.x) * width/2);
const yPos = Math.floor(p.y * width/2 + height/2);
this.pixels.image.data[4 * (yPos * width + xPos) + this.color] = 255;
}
this.pixels.setImage();
this.pixels.fillText(this.code);
}
}
const fallout = document.querySelector("#fallout");
const explosion = document.querySelector("#explosion");
const spin = document.querySelector("#spin");
const amoeba = document.querySelector("#amoeba");
const red = document.querySelector(".red");
const green = document.querySelector(".green");
const blue = document.querySelector(".blue");
const particles = document.querySelector("#particles");
const slider = document.querySelector("#slider");
fallout.addEventListener("click", () => swarm.setPattern("fallout"));
explosion.addEventListener("click", () => swarm.setPattern("explosion"));
amoeba.addEventListener("click", () => swarm.setPattern("amoeba"));
spin.addEventListener("click", () => swarm.setPattern("spin"));
slider.addEventListener("input", () => {
particles.innerHTML = slider.value;
});
slider.addEventListener("mouseup", () => {
swarm.setParticles(slider.value);
});
red.addEventListener("click", () => swarm.setColor(0));
green.addEventListener("click", () => swarm.setColor(1));
blue.addEventListener("click", () => swarm.setColor(2));
// start
slider.value = 2000;
particles.innerHTML = slider.value;
let currentTime = Date.now();
let previousTime = Date.now();
let deltaTime = 0;
const swarm = new Swarm(canvas, slider.value);
swarm.init();
setInterval(() => {
currentTime = Date.now();
swarm.render(deltaTime);
deltaTime = currentTime - previousTime;
previousTime = currentTime;
}, 1000/48);
</script>
Binary Search Tree (BST)</h2>
<div>
<canvas id="canvas" width=800 height=600></canvas>
</div>
<div id="buttons">
<button id="explosion">size(), get() and put()</button>
<button id="fallout">min(), floor(), select() and rank()</button>
<button id="spin">deletMin() and delet()</button>
<button id="amoeba">print() and keys()</button>
<div id="colors">
<div class="red box"></div>
<div class="green box"></div>
<div class="blue box"></div>
</div>
<button style="display: none;">Number of particles:<br><i id="particles">0</i></button>
<input style="display: none;" type="range" min="1" max="10000" value="1000" id="slider">
</div>
<style>
#menu {
color: olive;
text-align: center;
margin-bottom: 0;
padding-bottom: 0;
line-height: 1em;
font-weight: bold;
}
body {
display: flex;
flex-direction: row;
}
#buttons {
margin: 10px;
/* padding: 10px; */
display: flex;
flex-direction: column;
}
#colors {
margin: 10px;
/* padding: 10px; */
display: flex;
flex-direction: row;
justify-content: space-evenly;
/* background: grey; */
}
.box {
width: 20px;
height: 20px;
}
.red {
background: red;
}
.green {
background: green;
}
.blue {
background: blue;
}
</style>
<script>
class PixelManipulation {
constructor(canvas) {
this.context = canvas.getContext("2d");
this.width = canvas.width;
this.height = canvas.height;
this.image = this.context.getImageData(0, 0, this.width, this.height);
}
getImage() {
this.image = this.context.getImageData(0, 0, this.width, this.height);
}
setImage() {
this.context.putImageData(this.image, 0, 0);
}
setPixel(x, y, red, green, blue) {
const pixelIndex = (y * this.width + x) * 4;
this.image.data[pixelIndex] = red;
this.image.data[pixelIndex + 1] = green;
this.image.data[pixelIndex + 2] = blue;
}
fillColor(red, green, blue, alpha = 255) {
for (let i = 0; i < this.width * this.height * 4; i+=4) {
this.image.data[i] = red;
this.image.data[i + 1] = green;
this.image.data[i + 2] = blue;
this.image.data[i + 3] = alpha;
}
}
fillText(choice) {
this.context.font = "30px Arial";
//this.context.fillStyle = "#ff0000";
this.context.fillStyle = "#ffea00";
this.context.fillText(choice,10,50);
}
}
class Particle {
constructor() {
this.x;
this.y;
this.direction;
this.speed;
this.init;
this.update;
}
}
class Swarm {
constructor(canvas, amount = 10000, color = 1) {
this.size = amount;
this.particles = [];
this.pixels = new PixelManipulation(canvas);
this.color = color;
this.style = this.initSpin;
this.update = this.updateSpin;
this.code = 'great code you are writing';
}
setPattern(pattern) {
switch(pattern) {
case "fallout": {
this.style = this.initFallout;
this.update = this.updateFallout;
this.code = 'code 1';
break;
}
case "spin": {
this.style = this.initSpin;
this.update = this.updateSpin;
this.code = 'code 2';
break;
}
case "amoeba": {
this.style = this.initAmoeba;
this.update = this.updateAmoeba;
this.code = 'code 3';
break;
}
case "explosion": {
this.style = this.initExplosion;
this.update = this.updateExplosion;
this.code = 'code 4';
break;
}
}
this.init();
};
setColor(color) {
switch(color) {
case 0: {
this.code = 'text 1';
break;
}
case 1: {
this.code = 'text 2';
break;
}
case 2: {
this.code = 'text 3';
break;
}
}
this.color = color;
};
setParticles(value) {
this.size = value;
this.init();
}
init() {
; // 0 = red, 1 = green, 2 = blue
this.particles = [];
for(let i = 0; i < this.size; i++) {
this.particles.push(new Particle);
// this.particles[i].update = this.particles[i].updateSpin;
this.particles[i].update = this.update;
this.particles[i].init = this.style;
this.particles[i].init();
}
this.pixels.fillColor(0, 0, 0);
this.pixels.setImage();
this.pixels.fillText(this.code);
}
initFallout() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
// this.update = this.updateFallout;
}
updateFallout(delta) {
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
this.x = -1.10;
this.y = -1.10
}
}
initExplosion() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
this.speed *= this.speed/2;
// this.update = this.updateExplosion;
}
updateExplosion(delta) {
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
// this.init();
this.x = -1.10;
this.y = -1.10
}
}
initSpin() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
this.speed *= this.speed/2;
// this.update = this.updateSpin;
}
updateSpin(delta) {
this.direction += 0.01;
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
// this.init();
this.x = -1.10;
this.y = -1.10
}
}
initAmoeba() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
this.speed *= this.speed/2;
// this.update = this.updateAmoeba;
}
updateAmoeba(delta) {
this.direction += Math.random();;
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
// this.init();
this.x = -1.10;
this.y = -1.10
}
}
render(delta) {
const width = 800;
const height = 600;
const oldPixels = this.pixels.context.getImageData(0, 0, width, height).data;
for(let y=0; y<height; y++) {
for(let x = 0; x<width; x++) {
let redTotal = 0;
for(let row = -1; row <=1; row++) {
for(let col = -1; col <=1; col++) {
let currentX = x + col;
let currentY = y + row;
if(currentX >= 0 && currentX < width && currentY >= 0 && currentY < height) {
let red = oldPixels[4*(currentY * width + currentX) + this.color]
redTotal += red;
}
}
}
this.pixels.image.data[4*(y * width + x) + this.color] = redTotal / 9;
}
}
for(let i = 0; i < this.size; i++) {
const p = this.particles[i];
p.update(delta);
const xPos = Math.floor((1 + p.x) * width/2);
const yPos = Math.floor(p.y * width/2 + height/2);
this.pixels.image.data[4 * (yPos * width + xPos) + this.color] = 255;
}
this.pixels.setImage();
this.pixels.fillText(this.code);
}
}
const fallout = document.querySelector("#fallout");
const explosion = document.querySelector("#explosion");
const spin = document.querySelector("#spin");
const amoeba = document.querySelector("#amoeba");
const red = document.querySelector(".red");
const green = document.querySelector(".green");
const blue = document.querySelector(".blue");
const particles = document.querySelector("#particles");
const slider = document.querySelector("#slider");
fallout.addEventListener("click", () => swarm.setPattern("fallout"));
explosion.addEventListener("click", () => swarm.setPattern("explosion"));
amoeba.addEventListener("click", () => swarm.setPattern("amoeba"));
spin.addEventListener("click", () => swarm.setPattern("spin"));
slider.addEventListener("input", () => {
particles.innerHTML = slider.value;
});
slider.addEventListener("mouseup", () => {
swarm.setParticles(slider.value);
});
red.addEventListener("click", () => swarm.setColor(0));
green.addEventListener("click", () => swarm.setColor(1));
blue.addEventListener("click", () => swarm.setColor(2));
// start
slider.value = 2000;
particles.innerHTML = slider.value;
let currentTime = Date.now();
let previousTime = Date.now();
let deltaTime = 0;
const swarm = new Swarm(canvas, slider.value);
swarm.init();
setInterval(() => {
currentTime = Date.now();
swarm.render(deltaTime);
deltaTime = currentTime - previousTime;
previousTime = currentTime;
}, 1000/48);
</script>