Canvas image clip animation

by sungsoonz

HTML

<canvas id="canvas" width="600" height="600"></canvas>

<div id="sources">
    <img id="base" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/101507/album1.jpg" />
    <img id="over" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/101507/album2.jpg" />
</div>

CSS

html {
    height: 100%;
    color: white;
}

body {
    background: teal linear-gradient(transparent, #ff0099);
    text-align: center;
}

canvas {
    border: 3px solid yellow;
}

#sources {
    display: none;
}

JavaScript

const NUM_CIRCLES = 60,
  MIN_SIZE = 50,
  MAX_SIZE = 100;

// Returns a random int between two numbers.
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

// Cache refs to our canvas, context and images.
const canvas = document.getElementById('canvas'),
  ctx = canvas.getContext('2d'),
  imgBase = document.getElementById('base'),
  imgOver = document.getElementById('over');

let timestamp;

class Circle {
	constructor() {
  	this.x = 0;
    this.y = 0;
    this.size = 0;
    this.needRandomized = false;
  }
  randomize() {
  	this.x = getRandomInt(50, canvas.width - 50);
    this.y = getRandomInt(50, canvas.height - 50);
    this.maxSize = getRandomInt(MIN_SIZE, MAX_SIZE);
  }
  /**
   * Animates the size up and down via a sine calculation against a passed-in
   * timestamp factor.  (See the main program update() method).
   * Accepts an offset so different instances will animate out-of-sync
   * (if ofs was 0 for all instances, they would synchronize).
   * When the circle is fully-shrunk, it randomizes its position and max size.
   **/
  update(timestamp, index) {
  	this.size = Math.abs(Math.round(Math.sin(timestamp + index) * this.maxSize));
    if (this.size < 2) {
   		if (this.needRandomized) {
      	this.randomize();
        this.needRandomized = false;
      }
    } else {
    	this.needRandomized = true;
    }
  }
  draw() {
  	ctx.moveTo(this.x, this.y);
    ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
  }
  
}

let circles = [];

for (let i = 0; i < NUM_CIRCLES; i++) {
  let circle = new Circle();
  circle.randomize();
  circles.push(circle);
}


function update() {
  timestamp = 0.001 * Date.now();
  circles.forEach(function(circle, index) {
    circle.update(timestamp, index);
  });
}

function render() {
  ctx.drawImage(imgBase, 0, 0);
  ctx.save();
  ctx.beginPath();
  circles.forEach(function(circle) {
    circle.draw();
  });
  ctx.closePath();
  ctx.clip();
  ctx.drawImage(imgOver, 0, 0);
 ...