使用Angular.js预加载图像的最佳方法

时间:2014-01-07 23:31:36

标签: javascript angularjs image-preloader

Angular的ng-src保留了之前的模型,直到它在内部预加载图像。我在每个页面上使用不同的图像作为横幅,当我切换路径时,我更改主视图,保持标题视图不变,只是在我拥有它时更改bannerUrl模型。

这会导致在加载新横幅图像时看到上一个横幅图像。

我很惊讶,目前尚无指令,但我想在尝试构建之前进行讨论。

我想做什么我认为自定义属性上有横幅模型。像:

<img preload-src="{{bannerUrl}}" ng-src="{{preloadedUrl}}">

然后$ scope.watch for bannerUrl更改,并且一改变,首先用loader spinner替换ng-src,然后创建temproary img dom元素,从preload-src预加载图像,然后将其分配给preloadUrl。 / p>

例如,需要考虑如何为画廊处理多个图像。

有人对此有任何意见吗?或者有人可以指出我现有的代码?

我在github上看到了使用background-image的现有代码 - 但这对我不起作用,因为我需要动态高度/宽度,因为我的应用程序是响应式的,我不能用背景图像来做。

谢谢

9 个答案:

答案 0 :(得分:48)

指令上的2个网址似乎过于复杂。我认为更好的是编写一个类似于:

的指令
<img ng-src="{{bannerUrl}}" spinner-on-load />

该指令可以观察ng-src和(例如)使用微调器设置visibility:false,直到图像加载完毕。如下所示:

scope: {
  ngSrc: '='
},
link: function(scope, element) {
  element.on('load', function() {
    // Set visibility: true + remove spinner overlay
  });
  scope.$watch('ngSrc', function() {
    // Set visibility: false + inject temporary spinner overlay
  });
}

这样,该元素的行为非常类似于具有ng-src属性的标准img,只需要用一些额外的行为进行操作。

http://jsfiddle.net/2CsfZ/47/

答案 1 :(得分:24)

如果有人有兴趣,这是我的最终解决方案:我使用twitter bootstrap。因此,为所有图像添加了“淡入淡出”类,只需将指令切换为“in”,并在加载图像时淡入淡出

angular.module('myApp').directive('imgPreload', ['$rootScope', function($rootScope) {
    return {
      restrict: 'A',
      scope: {
        ngSrc: '@'
      },
      link: function(scope, element, attrs) {
        element.on('load', function() {
          element.addClass('in');
        }).on('error', function() {
          //
        });

        scope.$watch('ngSrc', function(newVal) {
          element.removeClass('in');
        });
      }
    };
}]);

<img img-preload class="fade" ng-src="{{imgSrc}}">

工作示例:http://ishq.org

答案 2 :(得分:3)

如果您愿意,可以将图像失败和图像加载器作为指令的属性传递....

myApp.directive("mySrc", function() {
    return {
      link: function(scope, element, attrs) {
        var img, loadImage;
        var IMAGE_LOAD="123.jpg";
        var IMAGE_FAIL="123.jpg";
        img = null;

        loadImage = function() {

          element[0].src = IMAGE_LOAD;

          img  = new Image();
          img.src = attrs.mySrc;

          img.onload = function() {
            element[0].src = attrs.mySrc;
          };
          img.onerror=function ()
          {
              element[0].src = IMAGE_FAIL;
          }
        };

        loadImage();


      }
    };
  });

答案 3 :(得分:2)

我认为这可能是最优雅的解决方案,因为该指令实际上创建了微调器并自动将其删除:

app.directive('spinnerLoad', [function spinnerLoad() {
    return {
        restrict: 'A',
        link: function spinnerLoadLink(scope, elem, attrs) {
            scope.$watch('ngSrc', function watchNgSrc() {
                elem.hide();
                elem.after('<i class="fa fa-spinner fa-lg fa-spin"></i>');  // add spinner
            });
            elem.on('load', function onLoad() {
                elem.show();
                elem.next('i.fa-spinner').remove(); // remove spinner
            });
        }
    };
}]);

这是html:

<img ng-src='{{imgUrl}}' spinner-load />

注意:您需要使用font-awesome才能按照此处的说明进行操作

答案 4 :(得分:1)

只是为了分享^^

 //css
.media-box{
                position: relative;
                width:220px;
                height: 220px;
                overflow: hidden;
            }
            .media-box div{
                position: absolute;
                left: 0;
                top: 0;
            }
            .spinner{
                position: absolute;
                left: 0;
                top: 0;
                background: #CCC url(./spinner.gif) no-repeat center center;
                display: block;
                width:220px;
                height: 220px;
            }
            .feed img.spinner-show{
                visibility: visible;
            }
            .feed img.spinner-hide{
                visibility: hidden;
            }

//html
<div class="media-box">
  <div>
    <img data-ng-src="{{item.media}}" alt="" title="" data-spinner-on-load>
  </div>
</div>

//js
.directive('spinnerOnLoad', function() {
            return {
                restrict: 'A',
                link: function(scope,element){
                    element.on('load', function() {
                        element.removeClass('spinner-hide');
                        element.addClass('spinner-show');
                        element.parent().find('span').remove();
                    });
                    scope.$watch('ngSrc', function() {
                        element.addClass('spinner-hide');
                        element.parent().append('<span class="spinner"></span>');
                    });      
                }
            }
        });

答案 5 :(得分:1)

而不是使用

element.on('load', function() {});

使用imagesLoaded插件。它会大大加快您的图像速度。

所以最终的代码是:

link: function(scope, element) {
  imagesLoaded(element, function() {

  });
  scope.$watch('ngSrc', function() {

  });
}

答案 6 :(得分:0)

我有这个指令,当img-src改变时显示一个微调器:

<img-with-loading
      img-src="{{src}}"
      spinner-class="{{spinnerClass}}"
/>

此处代码:http://jsfiddle.net/ffabreti/yw74upyr/

答案 7 :(得分:0)

可以使用image-preloader工厂在路线更改时预先加载图像并解决:

// call REST
                    return getContent.get().$promise.then(function(response) {

                                //return response;

                                // preload images from response
                                var imageLocations = [
                                  // put image(s) from response to array
                                  response.PostImage.big[0],
                                ];

                                // check do we have (all) image(s) in array
                                console.log(imageLocations);

                                // return when all images are preloaded
                                return preloader.preloadImages( imageLocations )
                                .then(function() {

                                    //if it was success 
                                    return response;
                                },
                                function() {

                                        //if it failed 
                                    return response;
                                });

                            });

在这里完成教程: https://www.coditty.com/code/angular-preload-images-on-route-change-by-using-resolve

答案 8 :(得分:-1)

我发现一个简单的解决方案是在为其分配新值之前将网址更改为“//:0”

$scope.bannerUrl = 'initial value'; 

// When we want to change it
$scope.bannerUrl = '//:0';  // remove the previous img so it's not visible while the new one loads
$scope.bannerUrl = scope.preloadedUrl