0

I've been having some trouble running one function that slowly displays a message, waits, then fades out. However, you'll notice when the button is clicked to drop the confetti and display the message, the confetti doesn't stop.

I'm still learning Javascript, and am not that experienced with it yet. But I pieced this together to get what I'm looking for as an end result.

What I'd like to happen is when the button is clicked, the message fades in, confetti falls, message fades and confetti stops falling but allows the last bit of confetti to fall until its off screen...

It's probably because I don't understand Javascript to this level yet, but I'm having a really hard time getting the JS to do what I'm wanting...

Can anyone give me some pointers in my code to help guide me in the right direction?

Thanks!!

(function() {
  // globals
  var canvas;
  var ctx;
  var W;
  var H;
  var mp = 200; //max particles
  var particles = [];
  var angle = 0;
  var tiltAngle = 0;
  var confettiActive = false;
  var animationComplete = true;
  var deactivationTimerHandler;
  var reactivationTimerHandler;
  var animationHandler;

  // objects

  var particleColors = {
    colorOptions: ["Aqua", "Aquamarine ", "DarkViolet", "DodgerBlue", "Lime", "Yellow", "DeepPink", "SlateBlue", "AliceBlue", "Fuchsia", "PaleGreen", "SteelBlue", "SandyBrown", "Chocolate", "Crimson"],
    colorIndex: 0,
    colorIncrementer: 0,
    colorThreshold: 10,
    getColor: function() {
      if (this.colorIncrementer >= 10) {
        this.colorIncrementer = 0;
        this.colorIndex++;
        if (this.colorIndex >= this.colorOptions.length) {
          this.colorIndex = 0;
        }
      }
      this.colorIncrementer++;
      return this.colorOptions[this.colorIndex];
    }
  }

  function confettiParticle(color) {
    this.x = Math.random() * W; // x-coordinate
    this.y = (Math.random() * H) - H; //y-coordinate
    this.r = RandomFromTo(10, 30); //radius;
    this.d = (Math.random() * mp) + 10; //density;
    this.color = color;
    this.tilt = Math.floor(Math.random() * 10) - 10;
    this.tiltAngleIncremental = (Math.random() * 0.07) + .05;
    this.tiltAngle = 0;

    this.draw = function() {
      ctx.beginPath();
      ctx.lineWidth = this.r / 2;
      ctx.strokeStyle = this.color;
      ctx.moveTo(this.x + this.tilt + (this.r / 4), this.y);
      ctx.lineTo(this.x + this.tilt, this.y + this.tilt + (this.r / 4));
      return ctx.stroke();
    }
  }

  $(document).ready(function() {
    SetGlobals();
    InitializeButton();

    $(window).resize(function() {
      W = window.innerWidth;
      H = window.innerHeight;
      canvas.width = W;
      canvas.height = H;
    });

  });

  function InitializeButton() {
    $('#stopButton').click(DeactivateConfetti);
    $('#startButton').click(RestartConfetti);
  }

  function SetGlobals() {
    canvas = document.getElementById("canvas");
    ctx = canvas.getContext("2d");
    W = window.innerWidth;
    H = window.innerHeight;
    canvas.width = W;
    canvas.height = H;
  }

  function InitializeConfetti() {
    particles = [];
    animationComplete = false;
    for (var i = 0; i < mp; i++) {
      var particleColor = particleColors.getColor();
      particles.push(new confettiParticle(particleColor));
    }
    StartConfetti();
  }

  function Draw() {
    ctx.clearRect(0, 0, W, H);
    var results = [];
    for (var i = 0; i < mp; i++) {
      (function(j) {
        results.push(particles[j].draw());
      })(i);
    }
    Update();

    return results;
  }

  function RandomFromTo(from, to) {
    return Math.floor(Math.random() * (to - from + 1) + from);
  }


  function Update() {
    var remainingFlakes = 0;
    var particle;
    angle += 0.01;
    tiltAngle += 0.1;

    for (var i = 0; i < mp; i++) {
      particle = particles[i];
      if (animationComplete) return;

      if (!confettiActive && particle.y < -15) {
        particle.y = H + 100;
        continue;
      }

      stepParticle(particle, i);

      if (particle.y <= H) {
        remainingFlakes++;
      }
      CheckForReposition(particle, i);
    }

    if (remainingFlakes === 0) {
      StopConfetti();
    }
  }

  function CheckForReposition(particle, index) {
    if ((particle.x > W + 20 || particle.x < -20 || particle.y > H) && confettiActive) {
      if (index % 5 > 0 || index % 2 == 0) //66.67% of the flakes
      {
        repositionParticle(particle, Math.random() * W, -10, Math.floor(Math.random() * 10) - 10);
      } else {
        if (Math.sin(angle) > 0) {
          //Enter from the left
          repositionParticle(particle, -5, Math.random() * H, Math.floor(Math.random() * 10) - 10);
        } else {
          //Enter from the right
          repositionParticle(particle, W + 5, Math.random() * H, Math.floor(Math.random() * 10) - 10);
        }
      }
    }
  }

  function stepParticle(particle, particleIndex) {
    particle.tiltAngle += particle.tiltAngleIncremental;
    particle.y += (Math.cos(angle + particle.d) + 3 + particle.r / 2) / 2;
    particle.x += Math.sin(angle);
    particle.tilt = (Math.sin(particle.tiltAngle - (particleIndex / 3))) * 15;
  }

  function repositionParticle(particle, xCoordinate, yCoordinate, tilt) {
    particle.x = xCoordinate;
    particle.y = yCoordinate;
    particle.tilt = tilt;
  }

  function StartConfetti() {
    W = window.innerWidth;
    H = window.innerHeight;
    canvas.width = W;
    canvas.height = H;
    (function animloop() {
      if (animationComplete) return null;
      animationHandler = requestAnimFrame(animloop);
      return Draw();
    })();
  }

  function ClearTimers() {
    clearTimeout(reactivationTimerHandler);
    clearTimeout(animationHandler);
  }

  function DeactivateConfetti() {
    confettiActive = false;
    ClearTimers();
  }

  function StopConfetti() {
    animationComplete = true;
  }

  function RestartConfetti() {
    ClearTimers();
    StopConfetti();
    reactivationTimerHandler = setTimeout(function() {
      confettiActive = true;
      animationComplete = false;
      InitializeConfetti();
    }, 100);

  }


  window.requestAnimFrame = (function() {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
      return window.setTimeout(callback, 1000 / 60);
    };
  })();
})();
/* Styles go here */

html,
body,
canvas {
  height: 100%;
  width: 100%;
  margin: 0;
  background-color: #1a1a1a;
}

canvas {
  display: block;
  position: relative;
  zindex: 1;
  pointer-events: none;
}

button {
  padding: 5px 10px;
  font-size: 20px;
}

.scene {
  display: flex;
  position: relative;
  width: 100%;
  height: 100%;
  align-items: center;
  justify-content: center;
}

.scene>canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.scene-content {
  position: relative;
  text-align: center;
  width: auto;
  height: auto;
  font-size: 10rem;
  font-family: Roboto;
  font-weight: 200;
  color: hsla(255, 50%, 100%, 1);
  animation: hue-shift 2s infinite linear;
}

.scene-content1 {
  position: relative;
  text-align: center;
  width: auto;
  height: auto;
  font-size: 10rem;
  font-family: Roboto;
  font-weight: 200;
  color: hsla(255, 50%, 100%, 1);
  animation: hue-shift 2s infinite linear;
}

.scene-content2 {
  position: relative;
  text-align: center;
  width: auto;
  height: auto;
  font-size: 6rem;
  font-family: Roboto;
  font-weight: 100;
  color: hsla(255, 50%, 50%, 1);
  animation: hue-shift 2s infinite linear;
}

.devices {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
  color: hsla(255, 50%, 100%, 1);
  animation: hue-shift 2s infinite linear;
}

@keyframes hue-shift {
  0% {
    color: hsla(0, 80%, 80%, 1);
  }
  25% {
    color: hsla(120, 80%, 80%, 1);
  }
  75% {
    color: hsla(240, 80%, 80%, 1);
  }
  100% {
    color: hsla(360, 80%, 80%, 1);
  }
}

.buttonContainer {
  display: inline-block;
}

button {
  padding: 5px 10px;
  font-size: 20px;
}

.clicker+.circle {
  -webkit-animation: rotor 1.5s linear 0s infinite normal;
  -mox-animation: rotor 1.5s linear 0s infinite normal;
  -o-animation: rotor 1.5s linear 0s infinite normal;
  animation: rotor 1.5s linear 0s infinite normal;
}

.paused {
  -webkit-animation-play-state: paused !important;
  -moz-animation-play-state: paused !important;
  -o-animation-play-state: paused !important;
  animation-play-state: paused !important;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <link href='http://fonts.googleapis.com/css?family=Roboto:400,100,300,500,700,900' rel='stylesheet'>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <link href="https://unpkg.com/ionicons@4.2.0/dist/css/ionicons.min.css" rel="stylesheet">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
  <link rel="stylesheet" href="css/style.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>

<body>
  <audio id="bubble_pop">
  <source src="mp3/pop.mp3" type="audio/mp3">
  Your browser does not support the audio element.
</audio>
  <div class="scene">
    <canvas id="canvas"></canvas>
    <div class="scene-content">
      <div class="devices">
        <i class="icon ion-ios-phone-portrait" style="font-size: 1.8em;"></i>
        <i class="icon ion-ios-watch" style="font-size: 1.6em;"></i>
      </div>
      <p class="scene-content1">Success!</p>
      <p class="scene-content2">Way to go! You can now use your device!</p>
    </div>
    <!--Hiding the message on initial page load-->
    <script type="text/javascript">
      $('.scene-content').hide();
    </script>
  </div>
  <p>
    <button id="stopButton">Stop</button>
    <button id="startButton" onclick="playAudio();">Drop it like its hot</button>
  </p>

</body>
<script>
  var pop = document.getElementById("bubble_pop");

  function playAudio() {
    pop.play();
  }

  document.getElementById('startButton').addEventListener('click', function() {
    function complete() {
      $("<div>").text(this.class).appendTo(".scene-content");
    }
    $(".scene-content").fadeIn(500).delay(2500).fadeOut(500, "linear", function() {});
  });
</script>

</html>

1 Answers1

1

The fadeOut() has one callback where you pass what should happen when animation is over.You must have tried this but reason why it didn't work is that most of your js code is in a self executing function which forms a different scope so you cannot call any method inside this scope from outside.I have moved the stuff inside now it works.Not much change there just changed a few lines; marked with NEWLY ADDED

(function() {
  // globals
  var canvas;
  var ctx;
  var W;
  var H;
  var mp = 200; //max particles
  var particles = [];
  var angle = 0;
  var tiltAngle = 0;
  var confettiActive = false;
  var animationComplete = true;
  var deactivationTimerHandler;
  var reactivationTimerHandler;
  var animationHandler;

  // objects

  var particleColors = {
    colorOptions: ["Aqua", "Aquamarine ", "DarkViolet", "DodgerBlue", "Lime", "Yellow", "DeepPink", "SlateBlue", "AliceBlue", "Fuchsia", "PaleGreen", "SteelBlue", "SandyBrown", "Chocolate", "Crimson"],
    colorIndex: 0,
    colorIncrementer: 0,
    colorThreshold: 10,
    getColor: function() {
      if (this.colorIncrementer >= 10) {
        this.colorIncrementer = 0;
        this.colorIndex++;
        if (this.colorIndex >= this.colorOptions.length) {
          this.colorIndex = 0;
        }
      }
      this.colorIncrementer++;
      return this.colorOptions[this.colorIndex];
    }
  }

  function confettiParticle(color) {
    this.x = Math.random() * W; // x-coordinate
    this.y = (Math.random() * H) - H; //y-coordinate
    this.r = RandomFromTo(10, 30); //radius;
    this.d = (Math.random() * mp) + 10; //density;
    this.color = color;
    this.tilt = Math.floor(Math.random() * 10) - 10;
    this.tiltAngleIncremental = (Math.random() * 0.07) + .05;
    this.tiltAngle = 0;

    this.draw = function() {
      ctx.beginPath();
      ctx.lineWidth = this.r / 2;
      ctx.strokeStyle = this.color;
      ctx.moveTo(this.x + this.tilt + (this.r / 4), this.y);
      ctx.lineTo(this.x + this.tilt, this.y + this.tilt + (this.r / 4));
      return ctx.stroke();
    }
  }

  $(document).ready(function() {
    SetGlobals();
    InitializeButton();

    $(window).resize(function() {
      W = window.innerWidth;
      H = window.innerHeight;
      canvas.width = W;
      canvas.height = H;
    });

  });

  function InitializeButton() {
    $('#stopButton').click(DeactivateConfetti);
    $('#startButton').click(RestartConfetti);
  }

  function SetGlobals() {
    canvas = document.getElementById("canvas");
    ctx = canvas.getContext("2d");
    W = window.innerWidth;
    H = window.innerHeight;
    canvas.width = W;
    canvas.height = H;
  }

  function InitializeConfetti() {
    particles = [];
    animationComplete = false;
    for (var i = 0; i < mp; i++) {
      var particleColor = particleColors.getColor();
      particles.push(new confettiParticle(particleColor));
    }
    StartConfetti();
  }

  function Draw() {
    ctx.clearRect(0, 0, W, H);
    var results = [];
    for (var i = 0; i < mp; i++) {
      (function(j) {
        results.push(particles[j].draw());
      })(i);
    }
    Update();

    return results;
  }

  function RandomFromTo(from, to) {
    return Math.floor(Math.random() * (to - from + 1) + from);
  }


  function Update() {
    var remainingFlakes = 0;
    var particle;
    angle += 0.01;
    tiltAngle += 0.1;

    for (var i = 0; i < mp; i++) {
      particle = particles[i];
      if (animationComplete) return;

      if (!confettiActive && particle.y < -15) {
        particle.y = H + 100;
        continue;
      }

      stepParticle(particle, i);

      if (particle.y <= H) {
        remainingFlakes++;
      }
      CheckForReposition(particle, i);
    }

    if (remainingFlakes === 0) {
      StopConfetti();
    }
  }

  function CheckForReposition(particle, index) {
    if ((particle.x > W + 20 || particle.x < -20 || particle.y > H) && confettiActive) {
      if (index % 5 > 0 || index % 2 == 0) //66.67% of the flakes
      {
        repositionParticle(particle, Math.random() * W, -10, Math.floor(Math.random() * 10) - 10);
      } else {
        if (Math.sin(angle) > 0) {
          //Enter from the left
          repositionParticle(particle, -5, Math.random() * H, Math.floor(Math.random() * 10) - 10);
        } else {
          //Enter from the right
          repositionParticle(particle, W + 5, Math.random() * H, Math.floor(Math.random() * 10) - 10);
        }
      }
    }
  }

  function stepParticle(particle, particleIndex) {
    particle.tiltAngle += particle.tiltAngleIncremental;
    particle.y += (Math.cos(angle + particle.d) + 3 + particle.r / 2) / 2;
    particle.x += Math.sin(angle);
    particle.tilt = (Math.sin(particle.tiltAngle - (particleIndex / 3))) * 15;
  }

  function repositionParticle(particle, xCoordinate, yCoordinate, tilt) {
    particle.x = xCoordinate;
    particle.y = yCoordinate;
    particle.tilt = tilt;
  }

  function StartConfetti() {
    W = window.innerWidth;
    H = window.innerHeight;
    canvas.width = W;
    canvas.height = H;
    (function animloop() {
      if (animationComplete) return null;
      animationHandler = requestAnimFrame(animloop);
      return Draw();
    })();
  }

  function ClearTimers() {
    clearTimeout(reactivationTimerHandler);
    clearTimeout(animationHandler);
  }

  function DeactivateConfetti() {
    confettiActive = false;
    ClearTimers();
  }

  function StopConfetti() {
    animationComplete = true;
  }

  function RestartConfetti() {
    playAudio(); // <-- NEWLY ADDED

    ClearTimers();
    StopConfetti();
    reactivationTimerHandler = setTimeout(function() {
      confettiActive = true;
      animationComplete = false;
      InitializeConfetti();
    }, 100);

  }


  window.requestAnimFrame = (function() {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
      return window.setTimeout(callback, 1000 / 60);
    };
  })();


  /**** NEWLY ADDED ****/
  var pop = document.getElementById("bubble_pop");

  function playAudio() {
    pop.play();
  }

  document.getElementById('startButton').addEventListener('click', function() {
    function complete() {
      $("<div>").text(this.class).appendTo(".scene-content");
    }
    $(".scene-content").fadeIn(500).delay(2500).fadeOut(500, "linear", function() { DeactivateConfetti(); });
  });
  /**** *********** ****/


})();
/* Styles go here */

html,
body,
canvas {
  height: 100%;
  width: 100%;
  margin: 0;
  background-color: #1a1a1a;
}

canvas {
  display: block;
  position: relative;
  zindex: 1;
  pointer-events: none;
}

button {
  padding: 5px 10px;
  font-size: 20px;
}

.scene {
  display: flex;
  position: relative;
  width: 100%;
  height: 100%;
  align-items: center;
  justify-content: center;
}

.scene>canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.scene-content {
  position: relative;
  text-align: center;
  width: auto;
  height: auto;
  font-size: 10rem;
  font-family: Roboto;
  font-weight: 200;
  color: hsla(255, 50%, 100%, 1);
  animation: hue-shift 2s infinite linear;
}

.scene-content1 {
  position: relative;
  text-align: center;
  width: auto;
  height: auto;
  font-size: 10rem;
  font-family: Roboto;
  font-weight: 200;
  color: hsla(255, 50%, 100%, 1);
  animation: hue-shift 2s infinite linear;
}

.scene-content2 {
  position: relative;
  text-align: center;
  width: auto;
  height: auto;
  font-size: 6rem;
  font-family: Roboto;
  font-weight: 100;
  color: hsla(255, 50%, 50%, 1);
  animation: hue-shift 2s infinite linear;
}

.devices {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
  color: hsla(255, 50%, 100%, 1);
  animation: hue-shift 2s infinite linear;
}

@keyframes hue-shift {
  0% {
    color: hsla(0, 80%, 80%, 1);
  }
  25% {
    color: hsla(120, 80%, 80%, 1);
  }
  75% {
    color: hsla(240, 80%, 80%, 1);
  }
  100% {
    color: hsla(360, 80%, 80%, 1);
  }
}

.buttonContainer {
  display: inline-block;
}

button {
  padding: 5px 10px;
  font-size: 20px;
}

.clicker+.circle {
  -webkit-animation: rotor 1.5s linear 0s infinite normal;
  -mox-animation: rotor 1.5s linear 0s infinite normal;
  -o-animation: rotor 1.5s linear 0s infinite normal;
  animation: rotor 1.5s linear 0s infinite normal;
}

.paused {
  -webkit-animation-play-state: paused !important;
  -moz-animation-play-state: paused !important;
  -o-animation-play-state: paused !important;
  animation-play-state: paused !important;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <link href='http://fonts.googleapis.com/css?family=Roboto:400,100,300,500,700,900' rel='stylesheet'>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <link href="https://unpkg.com/ionicons@4.2.0/dist/css/ionicons.min.css" rel="stylesheet">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
  <link rel="stylesheet" href="css/style.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>

<body>
  <audio id="bubble_pop">
  <source src="mp3/pop.mp3" type="audio/mp3">
  Your browser does not support the audio element.
</audio>
  <div class="scene">
    <canvas id="canvas"></canvas>
    <div class="scene-content">
      <div class="devices">
        <i class="icon ion-ios-phone-portrait" style="font-size: 1.8em;"></i>
        <i class="icon ion-ios-watch" style="font-size: 1.6em;"></i>
      </div>
      <p class="scene-content1">Success!</p>
      <p class="scene-content2">Way to go! You can now use your device!</p>
    </div>
    <!--Hiding the message on initial page load-->
    <script type="text/javascript">
      $('.scene-content').hide();
    </script>
  </div>
  <p>
    <button id="stopButton">Stop</button>
    <button id="startButton">Drop it like its hot</button>
  </p>

</body>
<script>
 
</script>

</html>
Vinay
  • 7,442
  • 6
  • 25
  • 48
  • +1 Dude, thank you so much for pointing that out...I couldn't figure out why it wasn't working when I was using a callback function there as you'd mentioned. I thought I was doing something wrong, or somewhere in the JS I broke something, but that makes TOTAL sense why it wasn't working now. – Chris Gillett Jul 08 '18 at 05:26