0
\$\begingroup\$

This code basically generates a random number of bubbles that animate from bottom of the screen to the top and repeats. When the mouse is in a certain distance (proximity) of the bubbles it scales those bubble and changes their background color. This Demo can explain better, how it works.

Now the problem is that whenever this code is running my macbook fan starts making noise and battery runs down quickly but when i close the page everything cools down. That's what I mean by maximising CPU usage.

I am pretty new at this, so I'd be glad if someone could review the code and criticize and suggest better solution to reduce the usage of my CPU.

Here is a live demo of what this code does: DEMO

$(document).ready(function() {
  // Animate loader off screen
  $(".preloader").fadeOut("slow");;
});
var bubbles = $('.bubbles');

function renderBubbles() {
  // bubble properties
  var max_bubbles = 30,
    min_bubbles = 50,
    max_bubbleSize = 10,
    min_bubbleSize = 18,
    total_bubbles = max_bubbles + Math.floor(Math.random() * min_bubbles + 1);
  for (var i = 0; i < total_bubbles; i++) {
    bubbles.append("<div class='bubble-container'><div class='bubble'></div></div>");
  }
  //set css property of .bubbble and .bubble-container
  bubbles.find('.bubble-container').each(function() {
    var bubble_size = max_bubbleSize + Math.floor(Math.random() * min_bubbleSize + 1);
    var bubble_pos = Math.floor(Math.random() * 100 + 1);
    var blur_size = 0 + Math.floor(Math.random() * 3 + 1);
    var animation_delay = 10 + Math.floor(Math.random() * 18);
    var random_margin = 50 + Math.floor(Math.random() * 100)
    var $this = $(this);
    var mX, mY, distance, mX_stored, mY_stored;

    function calculateDistance(elem, mouseX, mouseY) {
      return Math.floor(Math.sqrt(Math.pow(mouseX - (elem.offset().left + (elem.width() / 2)), 2) + Math.pow(mouseY - (elem.offset().top + (elem.height() / 2)), 2)));
    }
    $(document).mousemove(function(e) {
      mX = e.pageX
      mY = e.pageY;
      distance = calculateDistance($this, mX, mY);
      if (distance < 50) {
        console.log('yes');
        $this.children('.bubble').css({
          'background': 'rgb(255, 214, 153)',
        });
      } else if (distance > 70) {
        $this.children('.bubble').css({
          'background': 'rgba(255,255,255,0.25)',
          'transform': 'scale(1)'
        });
      }
    });
    $this.css({
      'left': bubble_pos + "%",
      '-webkit-filter': 'blur(' + blur_size + 'px)',
      '-moz-filter': 'blur(' + blur_size + 'px)',
      'filter': 'blur(' + blur_size + 'px)',
      '-webkit-animation-duration': animation_delay + 's',
      '-moz-animation-duration': animation_delay + 's',
      'animation-duration': animation_delay + 's',
    });
    $this.children('.bubble').css({
      'width': bubble_size + 'px',
      'height': bubble_size + 'px'
    });
  });
}
renderBubbles();
.preloader {
  width: 100%;
  height: 100%;
  background: #000;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 999;
}
.content {
  width: 100%;
  height: 100vh;
  background: -webkit-linear-gradient(#1A3742, #9EA183);
}
.bubbles {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.bubble {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.5);
  background: rgba(255, 255, 255, 0.25);
  will-change: transform;
  transition: 2s;
  &: hover {
    transform: scale(2);
    background: #ffbf80;
  }
}
.bubble-container {
  position: absolute;
  bottom: 0;
  animation: bubbleRise 10s infinite ease-in;
  will-change: transform;
}
@keyframes bubbleRise {
  0% {
    opacity: 0;
    bottom: 0;
  }
  15% {
    bottom: 0;
    opacity: 0.1;
    margin-left: 0px;
  }
  25% {
    opacity: 0.5;
  }
  50% {
    opacity: 1;
    margin-left: 50px;
  }
  100% {
    bottom: 100%;
    opacity: 0;
  }
}
.spinner-2 {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  position: relative;
  margin: 50px auto;
  display: inline-block;
  border: 3px solid transparent;
  border-top-color: orange;
  animation: spinall 2s linear infinite;
  &: after, &: before {
    content: '';
    display: block;
    width: 85px;
    height: 85px;
    border-radius: 50%;
  }
}
.spinner4:after {
  position: absolute;
  top: 3%;
  left: 3.5%;
  border: 3px solid transparent;
  border-top-color: orange;
  animation: spinall 3s linear infinite;
}
.spinner4:before {
  width: 75px;
  height: 75px;
  position: absolute;
  top: 10%;
  left: 10%;
  border: 3px solid transparent;
  border-top-color: orange;
  animation: spinall 2s linear infinite;
}
@keyframes spinall {
  0% {
    transform: rotate(0deg);
  }
  50% {
    transform: rotate(90deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="preloader">
  <div class="spinner-2 spinner4"></div>
</div>
<div class="content">
  <div class="bubbles"></div>
</div>
<!--https://codepen.io/bh/pen/JBlCc?editors=1010-->

\$\endgroup\$
4
  • 4
    \$\begingroup\$ What's it supposed to do? Why do you think it uses 'too much' CPU? Is there even such a thing? What are your concerns? List what the code does in the question (and in short in the title), don't put the concerns in the title but explain those in the question as well. This could become a good question, but as it currently stands it's unclear. \$\endgroup\$
    – Mast
    Commented Sep 16, 2016 at 22:16
  • \$\begingroup\$ I have edited the question now, I hope it clarifies the problem a bit now. Thanks! \$\endgroup\$
    – bright
    Commented Sep 16, 2016 at 22:34
  • \$\begingroup\$ It's a bit better, but I suspect you didn't fully understand my comment. \$\endgroup\$
    – Mast
    Commented Sep 16, 2016 at 22:39
  • \$\begingroup\$ I took a stab at making the title fit this site. Titles should describe what the code is intended to do rather than what you want reviewed. If it is an incorrect description, please edit it forward. \$\endgroup\$
    – mdfst13
    Commented Sep 16, 2016 at 23:46

1 Answer 1

2
\$\begingroup\$

Animation uses cycles. However, you had a fair few event handlers assigned and they can cause greater cycle use.

I thought this looked quite a fun exercise, so I modified your code to see what I could come up with. I have taken a different approach with the event handling. I decided to use polling, which is switched on and off when you enter or leave the document.

A mouse move event grabs the location of the mouse, so if you stop moving then the bubbles are still affected. This also improves the hover, which only fired when you actually moved the mouse too.

I used some different colours and change the CSS a little. I also wrote it in ES6 and without jQuery, which may not be ideal for you, but you can convert it back.

On my old laptop there appears to be an improvement in CPU cycle use, though to be honest I haven't done any in depth comparisons.

Anyway, I thought you may like to have a look a what I did as a thankyou for giving me a little amusement. :)

// bubble properties
const minBubbles = 30;
const maxBubbles = minBubbles * 2;
const minSize = 10;
const maxSize = 28;
const minDelay = 10;
const maxDelay = 28;
const minPos = 0;
const maxPos = 100;
const minBlur = 0;
const maxBlur = 3;

function getRandomIntInclusive(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
const $bubbles = document.querySelector('[class="bubbles"]');
const totalBubbles = getRandomIntInclusive(minBubbles, maxBubbles);
const bubbleElements = Array(totalBubbles).fill(null).map(() => {
  const bubbleSize = getRandomIntInclusive(minSize, maxSize);
  const bubblePos = getRandomIntInclusive(minPos, maxPos);
  const blurSize = getRandomIntInclusive(minBlur, maxBlur);
  const animationDelay = getRandomIntInclusive(minDelay, maxDelay);
  const $container = document.createElement('div');
  $container.className = 'bubble-container';
  $container.style.left = `${bubblePos}%`;
  $container.style.filter = `blur(${blurSize}px)`;
  $container.style.animationDuration = `${animationDelay}s`;
  const $bubble = document.createElement('div');
  $bubble.className = 'bubble';
  $bubble.style.width = `${bubbleSize}px`;
  $bubble.style.height = `${bubbleSize}px`;
  $container.appendChild($bubble);
  $bubbles.appendChild($container);
  return {
    $container: $container,
    $bubble: $bubble
  };
});

let ready = true;
let mouseX = 0;
let mouseY = 0;
document.addEventListener('mousemove', e => {
  mouseX = e.pageX;
  mouseY = e.pageY;
}, false);

function calcDistance(x, y) {
  return Math.sqrt(x * x + y * y);
}

const minDistanceMult = 2;
const maxDistanceMult = 4;

function updateElement(elem) {
  const width = parseInt(elem.$bubble.style.width, 10);
  const css = getComputedStyle(elem.$container);
  const computedWidth = parseInt(css.width, 10);
  const x = mouseX - (elem.$container.offsetLeft + computedWidth / 2);
  const y = mouseY - (elem.$container.offsetTop + parseInt(css.height, 10) / 2);
  const distance = calcDistance(x, y);
  if (distance <= computedWidth) {
    elem.$bubble.classList.add('bubbleHover');
    elem.$bubble.classList.remove('minDistance');
    elem.$bubble.classList.remove('maxDistance');
  } else {
    elem.$bubble.classList.remove('bubbleHover');
    if (distance <= width * minDistanceMult) {
      elem.$bubble.classList.add('minDistance');
      elem.$bubble.classList.remove('maxDistance');
    } else if (distance <= width * maxDistanceMult) {
      elem.$bubble.classList.remove('minDistance');
      elem.$bubble.classList.add('maxDistance');
    } else {
      elem.$bubble.classList.remove('minDistance');
      elem.$bubble.classList.remove('maxDistance');
    }
  }
}

function update() {
  if (ready) {
    ready = false;
    bubbleElements.forEach(updateElement);
    ready = true;
  }
}

const interval = 100;
let intervalId = document.hasFocus() && setInterval(update, interval);
document.addEventListener('mouseenter', () => {
  if (!intervalId) {
    update();
    intervalId = setInterval(update, interval);
  }
}, false);
document.addEventListener('mouseleave', () => {
  ready = false;
  intervalId = clearInterval(intervalId);
  bubbleElements.forEach(elem => elem.$bubble.className = 'bubble');
  ready = true;
}, false);
.preloader {
  width: 100%;
  height: 100%;
  background: #000;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 999;
  animation-duration: 3s;
  animation-fill-mode: both;
  animation-name: fadeOut;
}
@keyframes fadeOut {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    visibility: hidden;
  }
}
.content {
  width: 100%;
  height: 100vh;
  background: linear-gradient(#1A3742, #9EA183);
}
.bubbles {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.bubble {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  border: 1px solid #fff;
  border: 1px solid rgba(255, 255, 255, 0.5);
  background: #fff;
  background: rgba(255, 255, 255, 0.25);
  will-change: transform;
  transition: 2s;
}
.bubbleHover {
  transform: scale(2);
  background: red;
}
.minDistance {
  background: darkorange;
}
.maxDistance {
  background: yellow;
}
.bubble-container {
  position: absolute;
  bottom: 0;
  animation: bubbleRise 10s infinite ease-in;
  will-change: transform;
}
@keyframes bubbleRise {
  0% {
    opacity: 0;
    bottom: 0;
  }
  15% {
    bottom: 0;
    opacity: 0.1;
    margin-left: 0;
  }
  25% {
    opacity: 0.5;
  }
  50% {
    opacity: 1;
    margin-left: 50px;
  }
  100% {
    bottom: 100%;
    opacity: 0;
  }
}
.spinner-2 {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  position: relative;
  margin: 50px auto;
  display: inline-block;
  border: 3px solid transparent;
  border-top-color: orange;
  animation: spinall 2s linear infinite;
}
.spinner-2:after,
.spinner-2:before {
  content: '';
  display: block;
  width: 85px;
  height: 85px;
  border-radius: 50%;
}
.spinner4:after {
  position: absolute;
  top: 3%;
  left: 3.5%;
  border: 3px solid transparent;
  border-top-color: orange;
  animation: spinall 3s linear infinite;
}
.spinner4:before {
  width: 75px;
  height: 75px;
  position: absolute;
  top: 10%;
  left: 10%;
  border: 3px solid transparent;
  border-top-color: orange;
  animation: spinall 2s linear infinite;
}
@keyframes spinall {
  0% {
    transform: rotate(0deg);
  }
  50% {
    transform: rotate(90deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
<div class="preloader">
  <div class="spinner-2 spinner4"></div>
</div>
<div class="content">
  <div id="" class="bubbles"></div>
</div>

\$\endgroup\$
1
  • \$\begingroup\$ thank you soo much for taking your time to review @Xotic750 ! I am especially excited that you added the feature where bubbles grab the location and affect it without moving the mouse. I tried to incoporate that but to no avail. And indeed the CPU usage has decreased however its still high when I run it.(using to activity monitor on mac) . Anyways I guess rendering with canvas is always better. Thanks again! \$\endgroup\$
    – bright
    Commented Sep 18, 2016 at 22:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.