Vince Darnell
Vince Darnell

Reputation: 13

Bootstrap counter start on display

So I found this codepen tutorial that helped me get number counters on my page. It works perfectly however the counter is found down in the footer and the counter actually starts on page load. So by the time you get to the bottom of the page, the counter animation has already taken place. How would i alter this javascript so the counter doesn't start till I hit the above target div or counter div. For example lets say I have

<div id="services">
    //Stuff Here
</div>

<div class="counter-wrap">
    <div class="container">
        <div class="row">
            <div class="col-sm-4">
                <h2 class="timer count-title count-number" data-to="300" data-speed="1500"></h2>
                <p class="count-text ">SomeText GoesHere</p>
            </div>
            <div class="col-sm-4">
                <h2 class="timer count-title count-number" data-to="800" data-speed="1500"></h2>
                <p class="count-text ">SomeText GoesHere</p>
            </div>
            <div class="col-sm-4">
                <h2 class="timer count-title count-number" data-to="950" data-speed="1500"></h2>
                <p class="count-text ">SomeText GoesHere</p>
            </div>
        </div>
    </div>
</div>

So the counter animation should start till i hit the "services" id. JS:

(function ($) {
    $.fn.countTo = function (options) {
        options = options || {};

        return $(this).each(function () {
            // set options for current element
            var settings = $.extend({}, $.fn.countTo.defaults, {
                from:            $(this).data('from'),
                to:              $(this).data('to'),
                speed:           $(this).data('speed'),
                refreshInterval: $(this).data('refresh-interval'),
                decimals:        $(this).data('decimals')
            }, options);

            // how many times to update the value, and how much to increment the value on each update
            var loops = Math.ceil(settings.speed / settings.refreshInterval),
                increment = (settings.to - settings.from) / loops;

            // references & variables that will change with each update
            var self = this,
                $self = $(this),
                loopCount = 0,
                value = settings.from,
                data = $self.data('countTo') || {};

            $self.data('countTo', data);

            // if an existing interval can be found, clear it first
            if (data.interval) {
                clearInterval(data.interval);
            }
            data.interval = setInterval(updateTimer, settings.refreshInterval);

            // initialize the element with the starting value
            render(value);

            function updateTimer() {
                value += increment;
                loopCount++;

                render(value);

                if (typeof(settings.onUpdate) == 'function') {
                    settings.onUpdate.call(self, value);
                }

                if (loopCount >= loops) {
                    // remove the interval
                    $self.removeData('countTo');
                    clearInterval(data.interval);
                    value = settings.to;

                    if (typeof(settings.onComplete) == 'function') {
                        settings.onComplete.call(self, value);
                    }
                }
            }

            function render(value) {
                var formattedValue = settings.formatter.call(self, value, settings);
                $self.html(formattedValue);
            }
        });
    };

    $.fn.countTo.defaults = {
        from: 0,               // the number the element should start at
        to: 0,                 // the number the element should end at
        speed: 1000,           // how long it should take to count between the target numbers
        refreshInterval: 100,  // how often the element should be updated
        decimals: 0,           // the number of decimal places to show
        formatter: formatter,  // handler for formatting the value before rendering
        onUpdate: null,        // callback method for every time the element is updated
        onComplete: null       // callback method for when the element finishes updating
    };

    function formatter(value, settings) {
        return value.toFixed(settings.decimals);
    }
}(jQuery));

jQuery(function ($) {
  // custom formatting example
  $('.count-number').data('countToOptions', {
    formatter: function (value, options) {
      return value.toFixed(options.decimals).replace(/\B(?=(?:\d{3})+(?!\d))/g, ',');
    }
  });

  // start all the timers
  $('.timer').each(count);  

  function count(options) {
    var $this = $(this);
    options = $.extend({}, options || {}, $this.data('countToOptions') || {});
    $this.countTo(options);
  }
});

Codepen example: http://codepen.io/syedrafeeq/pen/rcfsJ

Upvotes: 1

Views: 11890

Answers (1)

Jakob
Jakob

Reputation: 3546

You could check if element is visible in viewport with this:

$.fn.isOnScreen = function(){
    var win = $(window);
    var viewport = {
        top : win.scrollTop(),
        left : win.scrollLeft()
    };
    viewport.right = viewport.left + win.width();
    viewport.bottom = viewport.top + win.height();

    var bounds = this.offset();
    bounds.right = bounds.left + this.outerWidth();
    bounds.bottom = bounds.top + this.outerHeight();

    return (!(viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom));

};

And then start the timers on scroll like this:

$(document).on( 'scroll', function(){
  $('.timer').each(count).isOnScreen(); 
});

Something like this: https://jsfiddle.net/snb7be2c/

I set new <div> and added some css so it is in footer.

Update

Run animation just once: https://jsfiddle.net/_jakob/snb7be2c/1/

Upvotes: 4

Related Questions