Home Reference Source

src/svg-loader-es6.js

const xmlns = 'http://www.w3.org/2000/svg';
const defaultOptions = {
    containerId: 'loader-container',
    svgId: 'loader',
    fill: '#000000',
    size: 15,
    radius: 2,
    duration: 1000,
    maxOpacity: 0.75,
    minOpacity: 0.25,
    margin: 2,
    nbRects: 3
};
const svgEl = Symbol( 'svgEl' );
const settings = Symbol( 'settings' );
const createSVG = Symbol( 'createSVG' );
const createRectangles = Symbol( 'createRectangles' );
const createAnimateElement = Symbol( 'createAnimateElement' );

/**
 * SVGLoader class
 * @example
 * let myLoader = new SVGLoader( {
 *     containerId: 'my-container-id'
 * } );
 */
export class SVGLoader {
    /**
     * SVGLoader constructor
     * @param {Object} [options] - the settings given to the instance
     * @param {string} [options.containerId='loader-container'] The id of the element that will contain the SVG element
     * @param {string} [options.svgId='loader'] The id given to the created SVG element
     * @param {int} [options.nbRects=3] Number of rectangle shapes in the SVG
     * @param {int} [options.margin=2] Margin between the shapes (in px)
     * @param {string} [options.fill='#000000'] Color of the shapes in the SVG
     * @param {int} [options.size=15] Height and width of each shape (rectangle) of the SVG (in px)
     * @param {int} [options.radius=2] Value of the border radius of each rectangle shape of the SVG (in px)
     * @param {Number} [options.minOpacity=0.25] Opacity to give to each shapes at the begin of the animation
     * @param {Number} [options.maxOpacity=0.75] Opacity to give to each shapes at the end of the animation
     * @param {int} [options.duration=1000] Duration of the animation of each shape from minOpacity to maxOpacity (in ms)
     */
    constructor ( options = defaultOptions ) {
        /**
         * Merge settings
         * @ignore
         */
        this[settings] = Object.assign( {}, defaultOptions, options );
        this[settings].width = ( this[settings].size * this[settings].nbRects ) + ( ( this[settings].nbRects - 1 ) * this[settings].margin );

        // Container for the svg depending on given id
        const svgContainer = document.getElementById( this[settings].containerId );
        if ( !svgContainer ) {
            console.warn( `Oops, there's no dom element with "${ this[settings].containerId }" id` );
            return;
        }

        /**
         * Create the svg and append it to the container
         * @ignore
         */
        this[svgEl] = this[createSVG]();
        svgContainer.appendChild( this[svgEl] );

        // Create all shapes in a group appended in the svg
        const group = this[createRectangles]();
        this[svgEl].appendChild( group );
    }

    /**
     * Create SVG Element with size properties from settings
     * @access private
     * @returns {SVGElement} the svg element created
     */
    [createSVG] () {
        const el = document.createElementNS( xmlns, 'svg' );
        el.setAttributeNS( null, 'id', this[settings].svgId );
        el.setAttributeNS( null, 'viewBox', `0 0 ${ this[settings].width } ${ this[settings].size }` );
        el.setAttributeNS( null, 'width', this[settings].width );
        el.setAttributeNS( null, 'height', this[settings].size );
        return el;
    }

    /**
     * Create rectangle shapes depending on settings
     * @access private
     * @returns {SVGGElement} the group element containing all rectangles (SVGRectElement)
     */
    [createRectangles] () {
        const group = document.createElementNS( xmlns, 'g' );

        for ( let i = 0; i < this[settings].nbRects; i++ ) {
            const rect = document.createElementNS( xmlns, 'rect' );
            rect.setAttributeNS( null, 'id', `rect-${ i }` );
            rect.setAttributeNS( null, 'width', this[settings].size );
            rect.setAttributeNS( null, 'height', this[settings].size );
            rect.setAttributeNS( null, 'x', ( this[settings].size * i ) + ( i * this[settings].margin ) );
            rect.setAttributeNS( null, 'y', 0 );
            rect.setAttributeNS( null, 'rx', this[settings].radius );
            rect.setAttributeNS( null, 'ry', this[settings].radius );
            rect.setAttributeNS( null, 'fill', this[settings].fill );
            rect.setAttributeNS( null, 'fill-opacity', this[settings].maxOpacity );

            const animate = this[createAnimateElement]( i );

            rect.appendChild( animate );
            group.appendChild( rect );
        }

        return group;
    }

    /**
     * Create Animate element for a rectangle shape of the svg
     * @access private
     * @param {int} index
     * @returns {SVGAnimateElement} the animate element of a rectangle
     */
    [createAnimateElement] ( index ) {
        const animate = document.createElementNS( xmlns, 'animate' );
        animate.setAttribute( 'attributeName', 'opacity' );
        animate.setAttribute( 'values', `${ this[settings].maxOpacity };${ this[settings].minOpacity };${ this[settings].maxOpacity }` );

        if ( index === 0 ) {
            animate.setAttribute( 'begin', `${ 0 }ms` );
        }
        else {
            animate.setAttribute( 'begin', `${ this[settings].duration / ( ( this[settings].nbRects + 1 ) - index ) }ms` );
        }
        animate.setAttribute( 'dur', `${ this[settings].duration }ms` );
        animate.setAttribute( 'repeatCount', 'indefinite' );

        return animate;
    }

    /**
     * Hide or show the SVG Element
     * @access public
     * @returns {SVGLoader} the current instance
     */
    toggle () {
        window.getComputedStyle( this[svgEl] ).display === 'none' ? this.show() : this.hide();
        return this;
    }

    /**
     * Show the SVG Element
     * @access public
     * @returns {SVGLoader} the current instance
     */
    show () {
        this[svgEl].style.display = 'block';
        return this;
    }

    /**
     * Hide the SVG Element
     * @access public
     * @returns {SVGLoader} the current instance
     */
    hide () {
        this[svgEl].style.display = 'none';
        return this;
    }

    /**
     * @access public
     * Remove the SVG element from DOM and delete all properties or listeners
     */
    destroy () {
        const svgContainer = document.getElementById( this[settings].containerId );
        svgContainer.querySelector( 'svg' ).remove();
        delete this[settings];
        delete this[svgEl];
    }

    /**
     * The current settings of the instance
     * @type {Object}
     * @property {string} containerId The id of the element that will contain the SVG element
     * @property {string} svgId The id given to the created SVG element
     * @property {int} nbRects Number of rectangle shapes in the SVG
     * @property {int} margin Margin between the shapes (in px)
     * @property {string} fill Color of the shapes in the SVG
     * @property {int} size Height and width of each shape (rectangle) of the SVG (in px)
     * @property {int} radius Value of the border radius of each rectangle shape of the SVG (in px)
     * @property {Number} minOpacity Opacity to give to each shapes at the begin of the animation
     * @property {Number} maxOpacity Opacity to give to each shapes at the end of the animation
     * @property {int} duration Duration of the animation of each shape from minOpacity to maxOpacity (in ms)
     */
    get settings () {
        return this[settings];
    }

    /**
     * The default options for settings if there's no settings given to the constructor
     * @type {Object}
     * @property {string} containerId The id of the element that will contain the SVG element
     * @property {string} svgId The id given to the created SVG element
     * @property {int} nbRects Number of rectangle shapes in the SVG
     * @property {int} margin Margin between the shapes (in px)
     * @property {string} fill Color of the shapes in the SVG
     * @property {int} size Height and width of each shape (rectangle) of the SVG (in px)
     * @property {int} radius Value of the border radius of each rectangle shape of the SVG (in px)
     * @property {Number} minOpacity Opacity to give to each shapes at the begin of the animation
     * @property {Number} maxOpacity Opacity to give to each shapes at the end of the animation
     * @property {int} duration Duration of the animation of each shape from minOpacity to maxOpacity (in ms)
     */
    static get defaultOptions () {
        return defaultOptions;
    }
}