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 πŸ˜„