glafarge / flickity-lazyload

Enable lazyLoad option for Flickity
5 stars 0 forks source link

Lazy load and cell size #1

Open glafarge opened 9 years ago

glafarge commented 9 years ago

Cells overlap and some layout problems occurs before images are loaded.

As the images are not loaded at the beginning but on a progressive way or on-demand, the slide is not able to position cells properly.

How to correctly set the size of cells before image load ?

Possible answers :

RatGit commented 9 years ago

I've modified the code to fix a couple of bugs and also added functionality to calculate the number of preloaded images based on a new "imgWidth" Flickity option, (if undefined, default=640), and taking into account the "wrapAround" and "cellAlign" options. It has been tested for all combinations of these options in the "ondemand" mode.

Notes:

  1. All code changes have been commented with "KCW:"
  2. The mods currently require jQuery.
  3. Apologies in advance for using Allman style indentation, (I parse it better)

START OF CODE BLOCK

/*!
 * Flickity lazyLoad v1.0.0
 * enables lazyLoad option for Flickity
 * based on slick approach
 */

/*jshint browser: true, strict: true, undef: true, unused: true */

( function( window, factory ) {
    /*global define: false, module: false, require: false */
    'use strict';
    // universal module definition

    if ( typeof define == 'function' && define.amd ) {
        // AMD
        define( [
            'flickity/js/index',
            'fizzy-ui-utils/utils',
        ], function( Flickity, utils ) {
            return factory( window, Flickity, utils );
        });
    }
    else if ( typeof exports == 'object' ) {
        // CommonJS
        module.exports = factory(
            window,
            require('flickity'),
            require('fizzy-ui-utils')
        );
    }
    else {
        // browser global
        window.Flickity = factory(
            window,
            window.Flickity,
            window.fizzyUIUtils
        );
    }

}( window, function factory( window, Flickity, utils ) {
    'use strict';

    Flickity.createMethods.push('_createLazyLoad');

    Flickity.prototype._createLazyLoad = function() {
        this.on( 'activate', this.activateLazyLoad );
    }

    Flickity.prototype.activateLazyLoad = function() {
        if ( !this.options.lazyLoad ) {
            return;
        }

        this.previousIndex = null; // Keep it to avoid bad behavior

        this.lazyLoad();
        this.previousIndex = this.selectedIndex;

        function onSelect() {
            if(this.selectedIndex==this.previousIndex)
                return;

            this.lazyLoad();
            this.previousIndex = this.selectedIndex;
        }
        this.on('cellSelect', onSelect);
    }

    Flickity.prototype.lazyLoad = function() {
        var _this = this;

        function imageLoaded(img) {
            img.removeAttribute('data-lazy');
            classie.remove(img, 'flickity-loading');

            var cell = _this.getParentCell( img );
            _this.cellSizeChange( cell && cell.element );
        }

        function onImageLoaded(e) {
            var img = e.target;
            eventie.unbind(img, 'load', onImageLoaded);
            imageLoaded(img);
        }

        function onImageLoadedProgressive(e) {
            var img = e.target;
            eventie.unbind(img, 'load', onImageLoadedProgressive);
            imageLoaded(img);

            _this.lazyLoad();
        }

        function loadImage(img, callback) {
            if(img.hasAttribute('data-lazy')) {
                var url = img.getAttribute('data-lazy');

                eventie.bind(img, 'load', callback);

                img.src = url;
            }
        }

        function loadImages(rangeStart, rangeEnd) {
//          var images = utils.filterFindElements(_this.slider.children);
            var images = utils.filterFindElements(_this.slider.children, 'img');  // KCW: Modifed to select images not their div wrappers

// KCW: Modifed to remove uneccessary, (and invalid) slice operation
            for (var i=rangeStart; i <= rangeEnd; i++)
            {
             var img = images[i];
             loadImage(img, onImageLoaded);
            }
        }

        // ==========================================
        // INITIALIZATION
        // ==========================================

        // Apply loading class on images that don't have src attribute but data-lazy
        var images = utils.filterFindElements(_this.slider.children, 'img[data-lazy]');
        for( var i=0, len = images.length; i < len; i++ ) {
            var img = images[i];
            if(!img.hasAttribute('src')) {
                classie.add(img, 'flickity-loading');
            }
        }

        // ==========================================
        // LAZY LOAD ON DEMAND
        // ==========================================
// KCW: Modifed to calculate number of preloaded images based on a new "imgWidth" option and the "cellAlign" option, (currently requires jQuery)
        if(_this.options.lazyLoad == 'ondemand') {
            if (this.cells.length == 0 || jQuery(this.element).width() == 0) {return;}

            var imgWidth = (this.options.imgWidth ? this.options.imgWidth : 640);  // Default Image Width = 640px

            var cellWidth = imgWidth + jQuery(this.cells[0].element).children('img').outerWidth(true);
            var wrapNum = Math.ceil(jQuery(this.element).width() / cellWidth) + 1;  // Add 1 in case there's room for two halves in a centered view

            var rangeStart = _this.selectedIndex, rangeEnd;
            switch (this.options.cellAlign)
            {
             case 'right':
              rangeEnd = rangeStart;
              rangeStart = rangeStart - wrapNum;
              if (rangeStart < 0) {rangeStart = 0;}
             break;
             case 'center':
              rangeEnd = rangeStart + Math.ceil(wrapNum/2)
              rangeStart = rangeStart - Math.ceil(wrapNum/2);
              if (rangeStart < 0) {rangeStart = 0;}
              if (rangeEnd >= this.cells.length) {rangeEnd = this.cells.length-1;}
             break;
             default:  // 'left'
              rangeEnd = rangeStart + wrapNum;
              if (rangeEnd >= this.cells.length) {rangeEnd = this.cells.length-1;}
             break;
            }

            loadImages(rangeStart, rangeEnd); // load next imgs

            if(_this.options.wrapAround === true)  // Preload wrapped images if "wrapAround" option is true
            {
             // Using "naive" approach here, (essentially applying the "right" and "left" cases consecutively for the "center" alignment)... Could be optimised
             rangeStart = _this.selectedIndex;
             if (this.options.cellAlign == 'right' || this.options.cellAlign == 'center')
             {
              rangeEnd = rangeStart + ((this.options.cellAlign == 'center') ? Math.ceil(wrapNum/2) : 0);
              rangeStart = rangeStart - ((this.options.cellAlign == 'center') ? Math.ceil(wrapNum/2) : wrapNum);
              if (rangeStart < 0)
              {
               rangeStart = this.cells.length - ((this.options.cellAlign == 'center') ? Math.ceil(wrapNum/2) : wrapNum) - 1;
               rangeEnd = this.cells.length - 1;
               loadImages(rangeStart, rangeEnd);
              }
             }
             rangeStart = _this.selectedIndex;
             if (this.options.cellAlign == 'left' || this.options.cellAlign == 'center')
             {
              rangeEnd = rangeStart + ((this.options.cellAlign == 'center') ? Math.ceil(wrapNum/2) : wrapNum);
              rangeStart = rangeStart - ((this.options.cellAlign == 'center') ? Math.ceil(wrapNum/2) : 0);
              if (rangeEnd >= this.cells.length)
              {
               rangeStart = 0;
               rangeEnd = rangeEnd - this.cells.length;
               loadImages(rangeStart, rangeEnd);
              }
             }
            }
            else  // Preload additional images if number is less than container element width
            {
             rangeStart = _this.selectedIndex;
             if (this.options.cellAlign == 'right' || this.options.cellAlign == 'center')
             {
              rangeEnd = rangeStart + ((this.options.cellAlign == 'center') ? Math.ceil(wrapNum/2) : 0);
              rangeStart = rangeStart - ((this.options.cellAlign == 'center') ? Math.ceil(wrapNum/2) : wrapNum);
              if (rangeStart < 0)
              {
               rangeEnd = rangeEnd - rangeStart;
               rangeStart = 0;
               if (rangeEnd >= this.cells.length) {rangeEnd = this.cells.length-1;}
               loadImages(rangeStart, rangeEnd);
              }
             }
             rangeStart = _this.selectedIndex;
             if (this.options.cellAlign == 'left' || this.options.cellAlign == 'center')
             {
              rangeEnd = rangeStart + ((this.options.cellAlign == 'center') ? Math.ceil(wrapNum/2) : wrapNum);
              rangeStart = rangeStart - ((this.options.cellAlign == 'center') ? Math.ceil(wrapNum/2) : 0);
              if (rangeEnd >= this.cells.length)
              {
               rangeStart = rangeStart - (rangeEnd - this.cells.length);
               if (rangeStart < 0) {rangeStart = 0;}
               rangeEnd = this.cells.length -1;
               loadImages(rangeStart, rangeEnd);
              }
             }
            }
        }

        // ==========================================
        // PROGRESSIVE WAY
        // ==========================================
        else if(_this.options.lazyLoad == 'progressive') {
            var images = utils.filterFindElements(_this.slider.children, 'img[data-lazy]');
            if(images.length > 0) {
                var img = images[0]; // get first child
                loadImage(img, onImageLoadedProgressive);
            }
        }

    };

    return Flickity;

}));