loading...

Fun with JavaScript and CSS - random dots

codepo8 profile image Christian Heilmann ・5 min read

OK, I just had some fun putting a Codepen together of some random dot animation in JavaScript and CSS:

And here are some tips how that is done.

First of all, creating a dot on the screen with HTML and CSS is pretty straight forward:

<div></div>

DIV has no semantic meaning, so that makes sense to use. It doesn't make sense to use <div> when a <button> is needed, though.

div {
  position: absolute;
  top: 50px;
  left: 50px; 
  width: 100px;
  height: 100px;
  background: rgba(30,50,80,1);
  border-radius: 50% 50%;
}

This paints a 100x100 pixel dot 50 pixels from the top and the left of the top left of the screen. It would be a rectangle, but as we use 50% 50% for border-radius it becomes a dot.

Now, to paint a pink dot with other measurements somewhere else, we can tweak those numbers:

div {
  position: absolute;
  top: 200px;
  left: 500px; 
  width: 60px;
  height: 60px;
  background: rgba(230,50,80,1);
  border-radius: 50% 50%;
}

The rgba colour space defines a colour as red, green, blue and alpha which is how transparent the colour is. So, an rgba(0,0,255,0.5) would be a half-transparent blue.

If you look at this example, we can now toggle between the two states of the dot simply by adding a class called "other" and removing it (this is what the JavaScript in this example does):

Neat, but not smooth. In order to make a smooth change from one state to the other, CSS has a wonderful thing called transitions. All we need to do to have the size, position and colour of the dot change smoothly is to tell the CSS engine how long to transition from one state to the other. In this case, let's use 500ms (half a second):

/* blue dot */
div {
  position: absolute;
  top: 50px;
  left: 50px; 
  width: 100px;
  height: 100px;
  background: rgba(30,50,80,1);
  border-radius: 50% 50%;
  transition: 500ms;
}
/* pink dot */
div.other {
  position: absolute;
  top: 200px;
  left: 500px; 
  width: 60px;
  height: 60px;
  background: rgba(230,50,80,1);
  border-radius: 50% 50%;
  transition: 500ms;
}

Clicking the button now transitions smoothly between one and the other states.

Good so far? Now, with JavaScript, we can set all these CSS values also dynamically. The transition we leave to CSS, as it is much better at doing that that any of our code would do.

Here's the whole code of the demo:

// grab all DIV elements in the document
let divs = document.querySelectorAll('div');

// helper method to get a multitude of a
// random number as an integer 
const rand = (multi) => {
  return parseInt(multi * Math.random() ,10);
}

// get width and height of the window
let ww = window.innerWidth;
let wh = window.innerHeight;

// define biggest possible value as constraint
let constraint = Math.min(ww, wh);

// move the dots by changing the CSS values
function move(){

  // loop over all DIV elements
  divs.forEach((div) => {

    // Balls can be the width of the constraint 
    // or less
    let w = rand(constraint);

    // x and y position limited to screen space
    let x = rand((ww - w));
    let y = rand((wh - w));

    // apply styles
    div.style.width = w + 'px'; 
    div.style.height = w + 'px'; 
    div.style.top = y + 'px'; 
    div.style.left = x + 'px';

    // 'move' dot with 900ms or more
    div.style.transition = (rand(100) + 900) +'ms';

    // apply random colour
    div.style.background = `rgba(
      ${rand(255)},
      ${rand(255)},
      ${rand(255)},
      ${Math.random() + 0.5}
    )`;
  });
}

// change dots every second
window.setInterval(move, 1000);

That's a lot? OK. Let's go through it bit by bit:

// grab all DIV elements in the document
let divs = document.querySelectorAll('div');

This gets all the DIV elements in the document, and stores a reference to them in a collection called divs. We only need to do that once, and it makes no sense to keep reading these as they don't change.

// helper method to get a multitude of a
// random number as an integer 
const rand = (multi) => {
  return parseInt(multi * Math.random() ,10);
}

The Math.random() method of JavaScript returns a "random" value between 0 and 1. Computers are bad at doing random things, but for our purpose, this should be good enough. As we want values bigger than one we multiply it with a number. As we don't want to have numbers like 132.965324234223 we use parseInt() with a radix of 10 to turn this into 132. It doesn't make much sense to position a DIV at 132.965324234223 pixels to the left and turning that into 132 is also a bit faster for the CSS engine.

// get width and height of the window
let ww = window.innerWidth;
let wh = window.innerHeight;

// define biggest possible value as constraint
let constraint = Math.min(ww, wh);

We read the height and width of the window to make sure that our dots stay within the window constraints and don't cause any scrollbars. As the dots are as wide as they are high, we also want to know the largest possible size. We do that by finding out if the window is higher or wider (Math.min(n1, n2) returns the smaller number of n1 and n2 - good to avoid some silly if statement).

// move the dots by changing the CSS values
function move(){
...
}

// change dots every second
window.setInterval(move, 1000);

The rest of the functionality is in the move() function. By using window.setInterval we call this function once every second (or 1000 milliseconds).

Here's what the move() function does:

// move the dots by changing the CSS values
function move(){

  // loop over all DIV elements
  divs.forEach((div) => {

Every time we call the function we loop over all the DIV elements in the document and get a reference to the current one in the loop as div. We then calculate the size and position of the dot.


    // Balls can be the width of the constraint 
    // or less
    let w = rand(constraint);

    // x and y position limited to screen space
    let x = rand((ww - w));
    let y = rand((wh - w));

The largest a dot should be is either the full height or full width of the current screen. We found that out earlier and stored it in the constraint value. We get a "random" value between 0 and that value and store it as w. We then calculate the top and left position of the dot as a random number between 0 and the width of the screen minus the width of the dot and the height of the screen respectively.

    // apply styles
    div.style.width = w + 'px'; 
    div.style.height = w + 'px'; 
    div.style.top = y + 'px'; 
    div.style.left = x + 'px';

    // 'move' dot with 900ms or more
    div.style.transition = (rand(100) + 900) +'ms';

    // apply random colour
    div.style.background = `rgba(
      ${rand(255)},
      ${rand(255)},
      ${rand(255)},
      ${Math.random() + 0.5}
    )`;
  });
}

The only thing left to do is to change the style collection of the div, randomise the transition to 900ms or more and add a random colour. And Bob's your uncle.

Is this amazing code? No. Was it fun to do? For me, yes. I hope you also found something here that is of interest.

Posted on by:

codepo8 profile

Christian Heilmann

@codepo8

Open Web guy at Microsoft and long-time JavaScript user

Discussion

markdown guide
 

The amazing @anatudor did a fork with a few changes

Changes:
➡️ animates transform instead of offsets and dimensions
➡️ rand generates a random number between a min & max and has an integer flag
➡️ dots stay within viewport at all times/ no scrollbars

codepen.io/thebabydino/pen/ExxERJJ

twitter.com/anatudor/status/119197...

 

Very nice!

It's almost unbelievable how soothing random colors for the dots can look (I had to look at the source code to believe that they were random colors!)

In an era of "you are missing out if you don't follow the latest trends in software development!", seeing simple vanilla Javascript and some CSS doing magic is great. 😃