DEV Community

Cover image for Creating a Bouncing Logo with JavaScript โ›น๐Ÿฝโ€โ™€๏ธ
Max Mykhalchuk
Max Mykhalchuk

Posted on • Edited on

Creating a Bouncing Logo with JavaScript โ›น๐Ÿฝโ€โ™€๏ธ

So, recently I've seen a YouTube video about DVD bouncing logo and thought, why not create one using JavaScript and write a small tutorial about it? ๐Ÿค”

And here's the result:

Recipe

First of all, let's deal with our HTML. We're not gonna go with a <canvas> today (sorry) but we have an awesome CodePen logo, so why not using it?

<svg xmlns="http://www.w3.org/2000/svg" id="label" viewBox="0 0 138 26" fill="none" stroke="#26de81" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round">
  <path d="M80 6h-9v14h9 M114 6h-9 v14h9 M111 13h-6 M77 13h-6 M122 20V6l11 14V6 M22 16.7L33 24l11-7.3V9.3L33 2L22 9.3V16.7z M44 16.7L33 9.3l-11 7.4 M22 9.3l11 7.3 l11-7.3 M33 2v7.3 M33 16.7V24 M88 14h6c2.2 0 4-1.8 4-4s-1.8-4-4-4h-6v14 M15 8c-1.3-1.3-3-2-5-2c-4 0-7 3-7 7s3 7 7 7 c2 0 3.7-0.8 5-2 M64 13c0 4-3 7-7 7h-5V6h5C61 6 64 9 64 13z"/>
</svg>
Enter fullscreen mode Exit fullscreen mode

And that's it for HTML! This SVG image is the only thing we're going to operate with.

CSS

Now we spice it up a little bit by using CSS:

body {
  width: 100vw;
  height: 100vh;
  background-color: #0f0f13;
  overflow: hidden;
  position: relative;
}

#label {
  position: absolute;
  left: calc(50vw - 150px);
  top: calc(50vh - 28px);
  width: 300px;
  height: 56px;
}
Enter fullscreen mode Exit fullscreen mode

So now, that we have finally completed the warmup, we're ready for the hardest part!

JavaScript

Let's start by defining variables:

const body = document.querySelector('body')
const label = document.querySelector('#label')

let colors = [ '#26de81', '#fc5c65', '#fd9644', '#fed330', '#2bcbba', '#45aaf2', '#4b7bec', '#a55eea', '#ffc1f3', '#76ead7', '#ff9c71', '#32e0c4', '#d291bc', '#fa744f' ]

let FPS = 60

let width
  , height
  , velocityX = 1
  , velocityY = 1
  , pause = true
  , previousColor = 0
;
Enter fullscreen mode Exit fullscreen mode

Velocity is needed for the bounce movement:

  • The absolute number defines the speed (minimum is 1)
  • The sign defines the direction
    • For X-axis - positive number moves the logo right, negative - left
    • For Y-axis - positive number moves the logo down, negative - up

For storing our window size we use width and height.


Now we need a function that will keep our width & height variables up to date and pause the animation if the window is smaller than an image.

const reset = () => {
  width =
    window.innerWidth ||
    document.documentElement.clientWidth ||
    document.body.clientWidth;

  height =
    window.innerHeight ||
    document.documentElement.clientHeight ||
    document.body.clientHeight;

  pause =
    width <= label.getBoundingClientRect().width ||
    height <= label.getBoundingClientRect().height;

  label.style.left = 'calc(50vw - 150px)'
  label.style.top = 'calc(50vh - 28px)'
  label.style.stroke = colors[0]
}

reset()
Enter fullscreen mode Exit fullscreen mode

It needs to be fired on initialization and each time window size changes, so let's create an event listener:

window.addEventListener('resize', reset, true)
Enter fullscreen mode Exit fullscreen mode

Have you seen how the CodePen logo is changing colors? There is a separate function for doing that.

const getRandomColor = () => {
  let currentColor = -1

  do {
    currentColor = Math.floor(Math.random() * colors.length);
  } while (previousColor == currentColor);

  previousColor = currentColor

  return colors[currentColor]
}
Enter fullscreen mode Exit fullscreen mode

Ok, it's time for the main function that will make our image bounce. For that purpose let's create an interval function:

setInterval(() => {
  if (pause) return;

  let rect = label.getBoundingClientRect()

  let left = rect.x
  let top = rect.y

  if (left + rect.width >= width || left <= 0) {
    velocityX = -velocityX
    let randomColor = getRandomColor()
    label.style.stroke = randomColor

    if (left + 150 <= width / 2) {
      body.style.boxShadow = `inset 4px 0px 0px 0px ${randomColor}`
    } else {
      body.style.boxShadow = `inset -4px 0px 0px 0px ${randomColor}`
    }
  }

  if (top + rect.height >= height || top <= 0) {
    velocityY = -velocityY
    let randomColor = getRandomColor()
    label.style.stroke = randomColor

    if (top + 28 <= height / 2) {
      body.style.boxShadow = `inset 0px 4px 0px 0px ${randomColor}`
    } else {
      body.style.boxShadow = `inset 0px -4px 0px 0px ${randomColor}`
    }
  }

  label.style.left = rect.x + velocityX + 'px'
  label.style.top = rect.y + velocityY + 'px'
}, 1000 / FPS)
Enter fullscreen mode Exit fullscreen mode

Ta-da ๐ŸŽ‰! We have just created Bouncing Logo Screensaver!


But, let's take a closer look at the collision detection.

if (left + rect.width >= width || left <= 0) {
Enter fullscreen mode Exit fullscreen mode

The condition above gives true if:

  • the position of the right side of the logo (left + rect.width) is greater or equal than the position of the right side of the window (width) on the X-axis
  • the position of the left side of the logo (left) is less or equal than the position of the left side of the window (0) on the X-axis

So, if the condition fires true, the following things happen:

  • The direction of the movement is being inverted: velocityX = -velocityX
  • Color Randomizer chooses a random color, saves it to the variable and updates image color
  • If-statement determines which side of the window is closer to the central coordinate of the logo and, depending on this, creates an inset box-shadow for one of the sides

Vertical collisions are being processed the same way.


So, I hope you enjoyed this article. Feel free to check me on:

Top comments (6)

Collapse
 
intrnl profile image
intrnl

Very interesting, though you might want to check out requestAnimationFrame ^^

Collapse
 
s1mpson profile image
Max Mykhalchuk

Thanks, and Wow, I haven't seen this function before. I guess that was the last time I was using setInterval for animations ๐Ÿ˜…

Collapse
 
penge profile image
Pavel Bucka

Hehe, I like it! Could be a great screensaver!

Collapse
 
thinkverse profile image
Kim Hallberg

Jake Peralta saying cool cool cool

But what will happen when it hits two sides at the same time? ๐Ÿค”

Collapse
 
s1mpson profile image
Max Mykhalchuk

I will scream as loud as I can, buy a lottery ticket, go to Las Vegas (since I'm so lucky) and eat a piece of cake ๐Ÿฅณ๐Ÿฅณ

Monkey Puppet Meme

Collapse
 
nombrekeff profile image
Keff

Ohh man, this brought me back to the good old days of DVDs ๐Ÿ˜„