Search code examples
javascriptcsstwitter-bootstrapjquery-waypointsonscroll

How to activate progress bar on scroll using waypoints?


I want to activate my progress bar on scroll and use waypoints.js for that. However it's not working and the progress bar is either 'filled' before I scroll over or (like in my current code) remains 'empty' on scroll as well as on page load.

html:

<div id="progress-bar-start" class="center-block progress-wrapper">
    <h4>UI/UX Design:</h4>
    <div class="progress">
        <div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="max-width: 60%">
            <span class="title">60%</span>
        </div>
    </div>
</div>

css:

#skills {
  margin-top: 0px;
  margin-bottom: 50px;
  .skills-title::first-letter {
    font-size: 60px;
    color: pink;
  }
  .progress-wrapper {
    width: 50%;
    margin-top: 30px;
    .progress-bar {
      background-color: pink;
      width: 0;
      animation: progress 1.5s ease-in-out forwards;
      .title {
        opacity: 0;
        animation: show 0.35s forwards ease-in-out 0.5s;
      }
    }
    .progress {
      height: 40px;
    }
  }
  @keyframes show {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
}

js:

$('#skills').waypoint(function(direction) {
    if (direction === 'down') {
        $('.progress-bar').addClass("show");
    } else {
        $('.progress-bar').removeClass("show");
    }
}, {
    offset: '50%'
});

Solution

  • There are a couple of things to note here.

    • First, let's start with Waypoint's offset option: By default a waypoint triggers when the top of the element hits the top of the window. The offset specifies the triggering distance between these top positions. In your example it is important to note, that the #skills section sticks to the top of the window, which means that with an offset >= 0 it triggers immediately at pageload, and only once (with 'down' direction).
    • Second, in order to show the progress bar progressing, you have to set the width of the bar, as opposed to the visibility/display. Also, setting the width should be done via the width instead of the max-width attribute, as that is needed to run the css animation.

    So, the solution is as follows:

    HTML

    <div class="progress">
        <!-- CHANGE style="max-width: 60%" TO data-score="60%" -->
        <div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" data-score="60%"></div>
    </div>
    

    JS

    $('#skills').waypoint(function(direction) {
        if (direction === 'down') {
            $('.progress-bar').width(function(){
                // this here refers to individual .progress-bar items
                return $(this).data('score');
            });
        } else {
            $('.progress-bar').width(0);
        }
    }, { offset: '10%' });
    

    SCSS

    #skills {
      margin-top: 100px;
      /* …the rest is the same… */
    }
    

    Working example available on Fiddle as well.

    UPDATE (answer for a comment):
    In case you would like the animation to happen every time the user scrolls down, but do not show the decreasing animation of the progress bar in the meantime you can alter the code as follows:

    $('#skills').waypoint(function(direction) {
        if (direction === 'down') {
            $('.progress-bar').width(function(){
                // this here refers to individual .progress-bar items
                return $(this).data('score');
            });
        }
    }, { offset: '10%' });
    
    $('#skills').waypoint(function(direction) {
        if (direction === 'up') {
            $('.progress-bar').width(0);
        }
    }, { offset: '100vh' });
    

    The code above still runs the decrease animation, but when not running in an iframe (like in Fiddle) that wont be visible as it got triggered when the #skills is out of the viewport. (In this case you might make use the visibility classes too.) In order to better showcase the functionality, I have also set margin-top: 100vh; on #skills in this Fiddle version.