# crazy bubbles

In this example you can see a series of circles moving randomly and colliding with the edges of the box that contains them. It is an example of how user requestAnimationFrame() to perform a Javascript controlled animation.

edit in codepen.io (opens new window)

We will now explain step by step how to create this animation.

# Import gySVG

The first step, if we haven't already done so, is to import the gySVG library. In our case, we've chosen to include the code within a script type=module and we'll use the import expression.

see: load gySVG

# Create the SVG

To create the graph we are going to use gySVG() and we are going to configure the viewBox, the width and height attributes.

see: SVG Element, .viewBox,

# Edge rectangle

The edge limits are defined with simple rentable:

# Create circles

We put 10 circles with random values and put the circle object into an Array.

The custom attributes speed_x() and speed_y() work

The custom speed_y and speed_y attributes work just like any other gySVG member, but we have actually created them dynamically to store the information about the speed of the bubble movement in the X and Y axis. gySVG has the virtue of being able to create any custom attribute we need by simply calling the function with the name that we want to use. It will automatically create an attribute with the name we have chosen, and we can set its value with .name(value) and get it value with .name().

# Add animation

With requestAnimationFrame() we can update the element positions to perform an animation and request that the browser calls our function to update an animation before the next repaint.

# The complete code

If we put all these steps together this is the example's code:

import gySVG from 'https://cdn.graphery.online/0.1.5/svg/module/index.js';

const svg     = gySVG ().viewBox (0, 0, 500, 300).width (500).height (300);

const rect    = svg.add ('rect').x (1).y (1).width (498).height (298)
  .fill ('none').stroke ('grey').stroke_width (2);

const circles = [];
for (let n = 0; n < 10; n++) {
  circles.push (
    svg.add ('circle')
      .cx (100 + Math.random () * 150)
      .cy (50 + Math.random () * 100)
      .r (Math.random () * 20 + 5)
      .speed_x (Math.random () * (n % 2 === 0 ? -0.015 : 0.02))
      .speed_y (Math.random () * (n % 2 === 0 ? -0.02 : 0.015))
      .fill (`hsl(${ 255 * Math.random () }, 100%, 50%, 0.5`)
  );
}
svg.attachTo ('#content');

let prev = 0;
function step (timestamp) {
  if (prev === 0) {
    prev = timestamp;
  }
  const elapse = timestamp - prev > 66 ? 66 : timestamp - prev;
  prev         = timestamp;
  circles.forEach (c => {
    const cx   = c.cx ();
    const cy   = c.cy ();
    const r    = c.r ();
    let speedX = c.speed_x ();
    let speedY = c.speed_y ();
    let moveX  = r * speedX * elapse;
    let moveY  = r * speedY * elapse;
    if (
      (cx + r + moveX >= 500 && speedX > 0) ||
      (cx - r + moveX <= 0 && speedX < 0)
    ) {
      c.speed_x (-speedX);
    }
    if (
      (cy + r + moveY >= 300 && speedY > 0) ||
      (cy - r + moveY <= 0 && speedY < 0)
    ) {
      c.speed_y (-speedY);
    }
    c.cx (cx + moveX);
    c.cy (cy + moveY);
  });
  requestAnimationFrame (step);
}
step (0);