FuruholmAnton
2/15/2017 - 1:34 PM

Animates a loading bar with GSAP

Animates a loading bar with GSAP

'use strict';

import gsap from 'gsap';

/**
 * IMPROVEMENTS: restart and stop instead of initialize and kill
 */


/*----------  Library  ----------*/
class LoadingAnimation  {

    /**
     * Sets the loading bars.
     * If no element where sent to the constructor, it will look for the first 2 elements with class 'header-loading'.
     *
     * @param  {Array} elements     An array of elements
     */
    constructor( elements = [] ) {

        /**
         * If a single DOM element was sent, put it in an array
         * Needs to be an array
         */
        if ( elements instanceof HTMLElement ) {
            elements = [elements];
        }
        else if ( !Array.isArray(elements) ) {
            console.warn('Param 1 needs to be an array');
            return;
        }

        let bars = [],
            _this = this,
            bar1,
            bar2;

        if (elements.length > 0) {
            elements.forEach(function(element, index) {
                bars.push(element);
            });
        }
        else {
            bars[0] = document.querySelector('.header-loading'),
            bars[1] = document.querySelectorAll('.header-loading')[1];

        }

        this.tl = new TimelineMax({repeat:0});
        this.tl2 = new TimelineMax({repeat:0});
        this.colorIndex = 0;
        this.count = 0;
        this.bars = bars;
        this.started = false;

    }

    start() {
        let _this = this;
        this.tl.fromTo(this.bars[0].querySelector('.header-loading__line'), 6, {xPercent:0}, { xPercent:60, ease:Power2.easeOut } );
        this.tl2.fromTo(this.bars[1].querySelector('.header-loading__line'), 6, {xPercent:0}, { xPercent:60, ease:Power2.easeOut } );
        this.started = true;

        this.timeout = setTimeout(function() {
            if (_this.started == true) {
                _this.stop();
            }
        }, (10 * 1000));
    }

    clear (timeline, line) {
        timeline.clear();
        line.style.transform = '';
    }

    stop() {
        let line1 = this.bars[0].querySelector('.header-loading__line'),
            line2 = this.bars[1].querySelector('.header-loading__line');

        this.tl.clear();
        this.tl.to(line1, 0.6, { xPercent:100, ease:Power2.easeIn } );
        this.tl.to(line1, 0.4, { opacity: 0 } );
        this.tl.set(line1, { xPercent:0 } );
        this.tl.set(line1, { opacity: 1 } );
        this.tl.call(this.clear, [this.tl, line1]);

        this.tl2.clear();
        this.tl2.to(line2, 0.6, { xPercent:100, ease:Power2.easeIn } );
        this.tl2.to(line2, 0.4, { opacity: 0 } );
        this.tl2.set(line2, { xPercent:0 } );
        this.tl2.set(line2, { opacity: 1 } );
        this.tl2.call(this.clear, [this.tl2, line2]);

        if (this.timeout) {
            clearTimeout(this.timeout);
        }

        this.started = false;
    }


}


/*----------  EXPORT  ----------*/

module.exports = LoadingAnimation;

'use strict';

import gsap from 'gsap';

let animationDuration = () => { return (Math.sqrt(window.innerWidth) / 9)};
let secoundStartValue;
let secoundStartFn = () => { return animationDuration() * 0.5 }
let colors = ['#daa9ae', '#98b8c1', '#d6deeb', '#acb198'];

/**
 * IMPROVEMENTS: restart and stop instead of initialize and kill
 */


/*----------  Library  ----------*/
class LoadingAnimation  {

    /**
     * Sets the loading bars. 
     * If no element where sent to the constructor, it will look for the first 2 elements with class 'header-loading'.
     *
     * @param  {Array} elements     An array of elements
     */
    constructor(elements = [], secoundStart = 'default',  ) {

        /**
         * If a single DOM element was sent, put it in an array
         * Needs to be an array
         */
        if ( elements instanceof HTMLElement ) {
            elements = [elements];
        } 
        else if ( !Array.isArray(elements) ) {
            throw 'Param 1 needs to be an array';
        }

        /* Allows numbers only */
        if ( /^-?\d+\.?\d*$/.test(secoundStart) ) {
            secoundStartFn = () => { return secoundStart };
        }
        else if (secoundStart == 'default') {
            // secoundStartFn = animationDuration();
        }
        else {
            throw 'Param 2 needs to be a number';
        }
        

        this.loadingBarTimeline  = new TimelineMax({repeat: -1});
        let lines = [];
        let bars = [];

        let _this = this,
         bar1,
         bar2;

        if (elements.length > 0) {
            elements.forEach(function(element, index) {
                bars.push(element);
            });
        } 
        else {
            bars[0] = document.querySelector('.header-loading'),
            bars[1] = document.querySelectorAll('.header-loading')[1];

        }

        bars.forEach(function(element, index) {
            lines[index] = [].slice.call(bars[index].querySelectorAll('.header-loading__line'));
        });

        this.colorIndex = 0;
        this.count = 0;
        this.bars = bars;
        this.lines = lines;

    }

    start() {
    	// this.loadingBarTimeline.duration(6);
        this.loadingBarTimeline.clear();
        this.loadingBarTimeline.timeScale(1);
        this.loadingBarTimeline.repeat( -1 );

        let _this = this,
            index = 0;

        // this.bars.forEach(function(element, index) {
        //     _this.animate.call(_this, index);
        // });
        for (index = 0; index < this.bars.length; index++) {
            _this.animate.call(_this, index);
        }

    }

    animate(barIndex) {
        let _this = this;
        let tweenOptionsFrom = {
            x: '-101%'
        };

        if (this.lines[barIndex] && this.lines[barIndex].length > 0) {

            this.lines[barIndex].forEach(function(el, lineIndex) {
                /**
                 * Time is backwords.
                 * The higher the number the earlier it starts
                 */
                
                let timeOfLineStart = lineIndex * secoundStartFn();
                let timeOfBarStart = barIndex * animationDuration();
                let timeOfStart = '-=' + ( timeOfLineStart + timeOfBarStart);

                /* Where the magic happens */
                _this.loadingBarTimeline.add(
                    TweenMax.fromTo(el, animationDuration(), tweenOptionsFrom, {
                        x: '100%',
                        ease: Power2.easeOut,

                        onRepeat: _this.tweenRepeat,
                        onRepeatScope: _this,
                        onRepeatParams: ['{self}'],

                        onStart: _this.tweenStart,
                        onStartScope: _this,
                        onStartParams: ['{self}'],

                        onUpdate: _this.setIndexHigh,

                        // onComplete: _this.pause

                    }), timeOfStart );

            });

        }
    }

    tweenRepeat(self) {
        this.setIndexLow.call(this, self.target);

    }

    tweenStart(self) {
        this.setIndexLow.call(this, self.target);
        this.changeColor.call(this, self);

    }

    changeColor(Tween) {
        Tween.target.style.backgroundColor = colors[this.colorIndex];
        this.count++;

        if ( this.colorIndex < (colors.length - 1) ) {
            if (this.count % 2 === 0) {
                this.colorIndex++;
            }
        } else {
            this.colorIndex = 0;

        }
    }

    /**
     * Set index to high towards animation end. So that it will always stay on top.
     */
    setIndexHigh() {

        var time = Math.round(this.time() * 10) / 10;

        if (time > 2) {
            this.target.style['z-index'] = 10;
        }
    }


    /**
     * Set index to Low at the start. So that it will always stay on below.
     */
    setIndexLow(self) {
        self.style['z-index'] = 9;

    }

    /**
     * Stops the animation
     * Is called externally
     *
     */
    stop() {
        this.loadingBarTimeline.repeat( 0 );

        let i = 0,
            _this = this,
            interval;

        /* If second line has not been activated yet then remove it */
        let children = this.loadingBarTimeline.getChildren(false, true, true, 0);
        children.forEach( function(tween, index) {
        	if (tween._active == false) {
        		tween.kill();
        	}
        });

        /* Gradually speeding ups by adding on interval */
        interval = setInterval(function(){ 
            let speed = 1 + (1 * i);
            _this.loadingBarTimeline.timeScale(speed);
            i++;
            if (i == 10) {
                clearInterval(interval);
            }
        }, 50);
    }

    pause() {
        this.pause(0, true);
    }

}


/*----------  EXPORT  ----------*/

module.exports = LoadingAnimation;