Sunday, June 14, 2015

“me-lazyload” module for Image lazy loading in AngularJS

There are n number of solution to load Image on demand(read lazy). In this example we will use our custom directive to lazy load Image.

The module is very simple to implement. I have taken from here.



So, let us implement this.

 <script src="~/Scripts/angular.js"></script>
    <script src="~/app/me-lazyload.js"></script>
      <script>
          var demo = angular.module('demo', ['me-lazyload']);
          demo.controller('mainCtrl', ['$scope', function ($scope) {
              var url = 'http://www.gravatar.com/avatar/6ca6c6611136a3e05ce30d872da1b551.jpg?s=100';

              var ary = [];
              for (var i = 0; i < 50; i++) {
                    ary.push(url + '&t=' + i + (+new Date()))
              }
              $scope.images = ary;
            }])
        </script>

We have created “demo” module and injecting “me-lazyload” module in demo module. Within controller, we are just building array with same Image. The concatenation part within for loop is to just avoid duplicate key problem.

Now, we will use the array in ng-repeat.

<div ng-controller="mainCtrl">
    <div class="container">
        <div class="wrap" ng-repeat="img in images">
            <img lazy-src="{{img}}" alt="">
        </div>
       
    </div>

That’s all the whole implementation. If you open me-lazyload module you will see this code which will display Image when it will appear in viewport.

angular.module('me-lazyload', [])
.directive('lazySrc', ['$window', '$document', function($window, $document){
    var doc = $document[0],
        body = doc.body,
        win = $window,
        $win = angular.element(win),
        uid = 0,
        elements = {};

    function getUid(el){
        return el.__uid || (el.__uid = '' + ++uid);
    }

    function getWindowOffset(){
        var t,
            pageXOffset = (typeof win.pageXOffset == 'number') ? win.pageXOffset : (((t = doc.documentElement) || (t = body.parentNode)) && typeof t.ScrollLeft == 'number' ? t : body).ScrollLeft,
            pageYOffset = (typeof win.pageYOffset == 'number') ? win.pageYOffset : (((t = doc.documentElement) || (t = body.parentNode)) && typeof t.ScrollTop == 'number' ? t : body).ScrollTop;
        return {
            offsetX: pageXOffset,
            offsetY: pageYOffset
        };
    }

    function isVisible(iElement){
        var elem = iElement[0],
            elemRect = elem.getBoundingClientRect(),
            windowOffset = getWindowOffset(),
            winOffsetX = windowOffset.offsetX,
            winOffsetY = windowOffset.offsetY,
            elemWidth = elemRect.width,
            elemHeight = elemRect.height,
            elemOffsetX = elemRect.left + winOffsetX,
            elemOffsetY = elemRect.top + winOffsetY,
            viewWidth = Math.max(doc.documentElement.clientWidth, win.innerWidth || 0),
            viewHeight = Math.max(doc.documentElement.clientHeight, win.innerHeight || 0),
            xVisible,
            yVisible;

        if(elemOffsetY <= winOffsetY){
            if(elemOffsetY + elemHeight >= winOffsetY){
                yVisible = true;
            }
        }else if(elemOffsetY >= winOffsetY){
            if(elemOffsetY <= winOffsetY + viewHeight){
                yVisible = true;
            }
        }

        if(elemOffsetX <= winOffsetX){
            if(elemOffsetX + elemWidth >= winOffsetX){
                xVisible = true;
            }
        }else if(elemOffsetX >= winOffsetX){
            if(elemOffsetX <= winOffsetX + viewWidth){
                xVisible = true;
            }
        }

        return xVisible && yVisible;
    };

    function checkImage(){
        Object.keys(elements).forEach(function(key){
            var obj = elements[key],
                iElement = obj.iElement,
                $scope = obj.$scope;
            if(isVisible(iElement)){
                iElement.attr('src', $scope.lazySrc);
            }
        });
    }

    $win.bind('scroll', checkImage);
    $win.bind('resize', checkImage);

    function onLoad(){
        var $el = angular.element(this),
            uid = getUid($el);

        $el.css('opacity', 1);

        if(elements.hasOwnProperty(uid)){
            delete elements[uid];
        }
    }

    return {
        restrict: 'A',
        scope: {
            lazySrc: '@'
        },
        link: function($scope, iElement){

            iElement.bind('load', onLoad);

            $scope.$watch('lazySrc', function(){
                if(isVisible(iElement)){
                    iElement.attr('src', $scope.lazySrc);
                }else{
                    var uid = getUid(iElement);
                    iElement.css({
                        'background-color': '#fff',
                        'opacity': 0,
                        '-webkit-transition': 'opacity 1s',
                        'transition': 'opacity 1s'
                    });
                    elements[uid] = {
                        iElement: iElement,
                        $scope: $scope
                    };
                }
            });

            $scope.$on('$destroy', function(){
                iElement.unbind('load');
            });
        }
    };
}]);



No comments:

Post a Comment