javascript - La mejor manera de precargar imágenes con Angular.js
angularjs image-preloader (9)
Creo que esta es quizás la solución más elegante porque la directiva crea el spinner y lo elimina automáticamente:
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
});
}
};
}]);
Aquí está el html:
<img ng-src=''{{imgUrl}}'' spinner-load />
Nota: necesitarás usar font-awesome para que esto funcione como se describe aquí
El ng-src de Angular mantiene el modelo anterior hasta que carga la imagen internamente. Estoy usando una imagen diferente para el banner en cada página, cuando cambio de ruta, cambio la vista principal, dejando la vista del encabezado tal como está, solo cambiando el modelo bannerUrl cuando lo tengo.
Esto resulta en ver una imagen de banner anterior mientras se carga una nueva.
Me sorprendió que todavía no haya una directiva para eso, pero quería hacer una discusión antes de intentar construir una.
Lo que quiero hacer es tener un modelo de banner en un atributo personalizado. me gusta:
<img preload-src="{{bannerUrl}}" ng-src="{{preloadedUrl}}">
Luego, $ scope.watch para el cambio de bannerUrl, y tan pronto como cambie, reemplace primero ng-src con el cargador giratorio, y luego cree el elemento temproary img dom, precargar la imagen de preload-src y luego asignarla a preloadUrl
Necesito pensar cómo manejar múltiples imágenes también para galerías, por ejemplo.
¿Alguien tiene alguna entrada en él? O tal vez alguien me puede indicar el código existente?
He visto un código existente en github que usa imagen de fondo, pero no me funciona porque necesito altura / anchura dinámicas ya que mi aplicación responde, y no puedo hacerlo con la imagen de fondo.
Gracias
En lugar de usar
element.on(''load'', function() {});
utilizar el complemento imagesLoaded . Acelerará dramáticamente tus imágenes.
Entonces el código final sería:
link: function(scope, element) {
imagesLoaded(element, function() {
});
scope.$watch(''ngSrc'', function() {
});
}
Si alguien está interesado, esta es mi solución final: uso twitter bootstrap. Entonces, agregue la clase de "fade" a todas las imágenes y simplemente alternando "in" con la directiva para que aparezca y desaparezca cuando se carga la imagen
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}}">
Ejemplo de trabajo: http://ishq.org
Si lo desea, puede pasar el error de imagen y el cargador de imagen como atributos para la directiva ...
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();
}
};
});
Solo para compartir ^^
//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>'');
});
}
}
});
Tener las 2 URL en la directiva parece un poco complicado. Lo que creo que es mejor es escribir una directiva que funcione como:
<img ng-src="{{bannerUrl}}" spinner-on-load />
Y la directiva puede ver ng-src
y (por ejemplo) establecer la visibilidad: falso con un girador hasta que la imagen se haya cargado. Así que algo como:
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 esta manera, el elemento se comporta como un img estándar con un atributo ng-src
, solo con un poco de comportamiento adicional activado.
Tengo esta directiva que muestra un giro cuando cambia img-src:
<img-with-loading
img-src="{{src}}"
spinner-class="{{spinnerClass}}"
/>
Código aquí: http://jsfiddle.net/ffabreti/yw74upyr/
Una solución simple que he encontrado es cambiar la URL a ''//: 0'' antes de asignar su nuevo valor
$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
las imágenes pueden precargarse en el cambio de ruta utilizando la fábrica de image-preloader y resolver:
// 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;
});
});
complete el tutorial aquí: https://www.coditty.com/code/angular-preload-images-on-route-change-by-using-resolve