web-dev-qa-db-fra.com

Meilleur moyen de précharger des images avec Angular.js

Angular's ng-src conserve le modèle précédent jusqu'à ce qu'il précharge l'image en interne. J'utilise une image différente pour la bannière sur chaque page. Lorsque je change d'itinéraire, je change de vue principale, laissant la vue d'en-tête telle qu'elle est, changeant simplement le modèle de bannièreUrl quand je l'ai. 

Cela entraîne l'affichage de la bannière précédente pendant le chargement de la nouvelle. 

J'ai été surpris qu'il n'y ait pas encore de directive pour cela, mais je voulais faire une discussion avant d'essayer de la construire.

Je pense que ce que je veux faire, c'est avoir un modèle de bannière sur un attribut personnalisé. comme:

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

Puis $ scope.watch pour le changement bannerUrl, et dès que cela change, remplacez d'abord ng-src par loader spinner, puis créez un élément img dom temproary, préchargez l'image à partir de preload-src, puis assignez-le à preloadUrl.

Vous devez également penser à la gestion de plusieurs images, par exemple pour les galeries.

Est-ce que quelqu'un a des commentaires à ce sujet? ou peut-être que quelqu'un peut m'indiquer le code existant?

J'ai vu du code existant sur github qui utilise background-image - mais cela ne fonctionne pas pour moi car j'ai besoin d'une hauteur/largeur dynamique car mon application est réactive, et je ne peux pas le faire avec background-image.

Je vous remercie

26
Yuri

Avoir les 2 urls sur la directive semble un peu compliqué. Ce qui me semble préférable, c’est d’écrire une directive qui fonctionne comme:

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

Et la directive peut regarder ng-src et (par exemple) définir la visibilité: false avec un compteur jusqu'à ce que l'image soit chargée. Donc, quelque chose comme:

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
  });
}

De cette façon, l’élément se comporte comme un img standard avec un attribut ng-src, avec juste un comportement supplémentaire.

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

48
Michal Charemza

Si quelqu'un est intéressé, voici ma solution finale: j'utilise le bootstrap de Twitter. Donc ajouté classe de "fade" à toutes les images et juste basculer la classe "in" avec directive à fondu en entrée et en sortie lorsque l'image est chargée

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}}">

Exemple de travail: http://ishq.org

23
Yuri

Si vous le souhaitez, vous pouvez transmettre l’échec et le chargeur d’image en tant qu’attributs de la directive ....

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
yonia

Je pense que c'est peut-être la solution la plus élégante, car la directive crée effectivement le cône et le supprime automatiquement

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
            });
        }
    };
}]);

Voici le code HTML:

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

Remarque: vous devrez utiliser font-awesome pour que cela fonctionne comme décrit ici.

2
Jeremy Moritz

À la place d'utiliser 

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

utilisez imagesLoaded plugin. Cela accélérera considérablement vos images.

Le code final serait donc:

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

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

  });
}
1
borN_free

Juste pour partager ^^

 //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>');
                    });      
                }
            }
        });
1
Whisher

J'ai cette directive qui montre un spinner quand img-src change:

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

Code ici: http://jsfiddle.net/ffabreti/yw74upyr/

0
Fernando Fabreti

les images peuvent être préchargées lors du changement de route à l’aide de image-preloader factory et résolvent:

// 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;
                                });

                            });

terminez le tutoriel ici: https://www.coditty.com/code/angular-preload-images-on-route-change-by-using-resolve

0
Igor Simic