Coffee Animation with JavaScript's Canvas API

If you aren't seeing the animation above, please make sure JavaScript is enabled.

I created this small animation for my Computer Graphics class. At the time, we were learning about transformations such as translation, scale, rotation, etc. I decided to do an animation of coffee being poured into a cup and steam coming out of the cup. Although I used the built in transformations in Canvas, we did go over the math behind the transformations. Having already taken Linear Algebra prior, I was quite familiar with this topic. Anyways, this was one of my first assignments and I really loved the combination of math and programming together!

JavaScript code below!

// ------- variables -------
var canvas;    // DOM object corresponding to the canvas
var graphics;  // 2D graphics context for drawing on the canvas
var backgroundColor = "rgba(255, 153, 153, 255)";
var tableColor = "rgba(204, 229, 255, 255)";
var cupColor = "rgba(224, 224, 224, 255)";
var drinkColor = "rgba(102, 50, 0, 255)";
var timer = 0;

// --------- constructors ------------

// creates a filled rectangle
function Rectangle(x,y,h,w,color,s) {
    this.x = x; // x coord
    this.y = y; // y coord
    this.h = h; // height
    this.w = w; // width
    this.s = s; // scale factor
    this.color = color; // fill color
    
    // renders rectangle
    this.draw = function draw(graphics) { 
        graphics.fillStyle = this.color;
        graphics.fillRect(0,0,this.w,this.h);
    }

    // renders rectangle
    this.render= function start() {
        graphics.save();
        graphics.translate(this.x,this.y);
        graphics.scale(1,this.s);         
        this.draw(graphics);
        graphics.restore();
    }

    // updates stream size
    this.update = function update() {
        // increases stream's height
        
        if (this.s < 200) {
            this.s *= s;     
        }
        
    }
}

// creates a colored cup
function Cup(x,y,h,w,color) {
    this.x = x; // x coord
    this.y = y; // y coord
    this.h = h; // height
    this.w = w; // width
    this.color = color; // color of cup
    this.draw = function draw(graphics) { // draws cup and colors it
        graphics.beginPath();
        graphics.moveTo(this.x, this.y);
        graphics.lineTo(this.x+(this.w/2), this.y);
        graphics.lineTo(this.x+(3*this.w/4), this.y - this.h);
        graphics.lineTo(this.x-(this.w/4), this.y - this.h);
        graphics.fillStyle = this.color;
        graphics.fill();
    }
}

// creates the smoke puff
function Smoke(x,y,dx){
    this.x = x; // the x coord of center
    this.y = y; // the y coord of center
    this.s = 1; // scale factor
    this.dx = dx; // the change of x pos
    this.alpha = 80; // transparency val
    this.color = `rgb(255 255 255 / ${this.alpha}%)`; // the transparency of the smoke puff
    
    // draws smoke puff
    this.draw = function drawT(graphics) {
        graphics.beginPath();
        graphics.arc(0,0,18,0,2*Math.PI);
        graphics.fillStyle = this.color;
        graphics.fill();
    }

    // renders the smoke puff
    this.render = function render() {
        graphics.save();
        graphics.translate(this.x, this.y);
        graphics.scale(this.s, this.s);
        this.draw(graphics);
        
        graphics.restore();
    }

    // updates the transformation
    this.update = function updateT() {
        
        // makes smoke puff got left to right and right to left randomly
        if (timer < 20) {
            this.x += this.dx;

        } else {
            this.x -= this.dx;
        }
        
        timer++;
        timer %= 40;

        // updates y pos
        this.y -= 1;
        // updates scale factor relative to y pos
        this.s = (this.y - 67)/100;
        // decreases transparency
        this.alpha *= (this.y - 67)/100;

        // if scale is 0, then reset smoke puff
        if (this.s < 0) {
            this.reset();
        }
    }

    // resets the positon of smoke puff
    this.reset = function reset() {
        this.x = x; // the x coord of center
        this.y = y; // the y coord of center
        this.dx = -dx; // the change of x coord
        this.s = 1; // scale factor
        this.alpha = 80; // transparency
        this.color = `rgb(255 255 255 / ${this.alpha}%)`; // the transparency of the smoke puff
    }
    
}

// ------- objects ---------

// background
var background = new Rectangle(0,0,250, 450, backgroundColor, 1);
    
// table
var table = new Rectangle(0,250,50, 450, tableColor, 1);

// smoke puffs
var smokePuffs = [];
smokePuffs.push(new Smoke(225, 167, 1));
smokePuffs.push(new Smoke(248, 170, 0));
smokePuffs.push(new Smoke(205, 180, 1));
smokePuffs.push(new Smoke(200, 200, 0));
smokePuffs.push(new Smoke(226, 170, 0));
smokePuffs.push(new Smoke(220, 220, 1));
smokePuffs.push(new Smoke(205, 190, 0));

// cup
var coffeeCup = new Cup(190, 250, 114, 150, cupColor);

// coffee stream
var coffee = new Rectangle(220,10,1,10, drinkColor, 1.09); 

// the output of coffee stream
var coffeeMaker = new Rectangle(210,0,10,30, tableColor, 1);

// timer for smoke puff positions
var timer = 0;

// ------- animate ----------

function animate() {
    // -------prep canvas-------- 
    
    // render background
    background.render();
    table.render();
    coffeeMaker.render();

    
    // ----- setting up transformations ----

    // renders coffee stream
    coffee.render();

    // renders smoke puffs
    for (p in smokePuffs){
        smokePuffs[p].render();
    }

    //renders cup
    coffeeCup.draw(graphics);
    

    for (p in smokePuffs){
        smokePuffs[p].update();       
    }

    coffee.update();
    

}


function test() {
    canvas = document.getElementById("theCanvas");
    graphics = canvas.getContext("2d");
    setInterval(animate,30);
}