希望这是一个简单的答案。
我有这个指令控制网站上的背景图像。它在图像之间旋转,但在开始设置背景图像之前,它实际上是预加载它们。这是我的指令代码:
// ---
// CONTROLLERS.
// ---
.controller('BackgroundSwitcherController', ['$rootScope', 'Options', 'BackgroundSwitcherService', function ($rootScope, options, service) {
var self = this;
// Declare our variables
self.model = {
staticImage: '',
loading: false,
rotate: false
};
// On state change
$rootScope.$on('$stateChangeStart', function (event, toState) {
// Get our data
var data = toState.data;
// Set our rotate flag
self.model.rotate = options.current.rotateBackground;
// If we are set to rotate
if (self.model.rotate) {
// Make a call to our service (this is called every state change, but only preloaded once)
service.get().then(function (response) {
// Return the images in the response
self.model.images = response;
});
} else {
// Otherwise, reset the images
self.model.images = null;
}
// If we have data
if (data) {
// Reset our variables
self.model.staticImage = data.backgroundImage;
//self.model.loading = true;
}
});
}])
// ---
// SERVICES.
// ---
.service('BackgroundSwitcherService', ['$q', 'PreloaderService', function ($q, preloader) {
this.loading = true;
this.successful = false;
this.percentLoaded = 0;
// Get our images
this.get = function () {
// Our images
var images = [
'assets/backgrounds/Avebury Steps.jpg',
'assets/backgrounds/Avebury Stripe.jpg',
'assets/backgrounds/Avebury.jpg',
'assets/backgrounds/Forest Hills.jpg',
'assets/backgrounds/Inteface.jpg',
'assets/backgrounds/New Oaklands.jpg',
'assets/backgrounds/Primo Delight.jpg',
'assets/backgrounds/Secure 3d.jpg',
'assets/backgrounds/Secure.jpg',
'assets/backgrounds/Sensation Heathers.jpg',
'assets/backgrounds/Sensation.jpg',
'assets/backgrounds/Woman.jpg'
];
// Create our promise
var deferred = $q.defer();
// Preload the images
preloader.preloadImages(images).then(
// Function to handle the changing of the flags when all images have loaded
function handleResolve(imageLocations) {
this.loading = false;
this.successful = true;
deferred.resolve(images);
},
// Function to handle any errors
function handleReject(imageLocation) {
this.loading = false;
this.successful = false;
deferred.reject();
},
// Function that notifies our percentage loaded flag
function handleNotify(event) {
this.percentLoaded = event.percent;
}
);
// Return our promise
return deferred.promise;
};
}])
// ---
// DIRECTIVES.
// ---
.directive('backgroundSwitcher', ['$interval', function ($interval) {
// Variables
var target = null,
timer = null,
images = null,
currentIndex = 0;
// Function to get a random value between two numbers
function getRandomInt(min, max) {
// Return our random number
return Math.floor(Math.random() * (max - min)) + min;
};
var getImageIndex = function (length) {
// Get our random index
var index = getRandomInt(0, images.length);
// If our index matches the current index
if (index == currentIndex) {
// Run again until we get a different index
return getImageIndex(length);
}
// Set our current index our the new index
currentIndex = index;
// Return our index
return index;
}
// Apply the image
var applyImage = function () {
// Get a random image
var image = images[getImageIndex(images.length)];
// Apply our image to our target
applyStaticImage(image);
};
// Apply a static image
var applyStaticImage = function (image) {
// Apply our image to our target
target.css('background-image', 'url(' + escape(image) + ')');
};
// Remove any images
var removeImage = function () {
// Remove our background image
target.css('background-image', 'none');
};
// Start timer function
var startTimer = function () {
// If our timer is not running
if (!timer || timer.$$state.status === 2) {
// Apply our first image
applyImage();
// Start our timer
timer = $interval(changeImage, 10000);
}
};
// Function to change the background image
var changeImage = function () {
// Apply our image
applyImage();
};
// Stop the timer
var stopTimer = function () {
// If we have started our timer
if (timer) {
// Stop it
$interval.cancel(timer);
}
};
return {
restrict: 'A',
controller: 'BackgroundSwitcherController',
link: function (scope, element, attr, controller) {
// Assign our element to our global variable
target = element;
// Watch our image
scope.$watch(function () {
// Return our image
return controller.model.staticImage;
}, function (staticImage) {
// If we have an image
if (staticImage) {
// Stop our timer
stopTimer();
// Apply our static image
applyStaticImage(staticImage);
} else {
// If we are not rotating
if (!controller.model.rotate) {
// Remove any images
removeImage();
}
}
})
// Watch our rotate
scope.$watch(function () {
// Return our rotate flag
return controller.model.images;
}, function (array) {
// Set our variable
images = array;
// If we have some images
if (images) {
// If we don't have a static image
if (!controller.model.staticImage) {
// Start rotating our images
startTimer();
}
} else {
// Remove any images
removeImage();
// Otherwise, stop our timer
stopTimer();
}
});
// Destroy function to cancel all timers
element.on('$destroy', function () {
// Stop our timer
stopTimer();
});
}
}
}]);
对于实际的预加载器,我有这个:
// ---
// SERVICES.
// ---
.factory('PreloaderService', function ($q, $rootScope) {
function Preloader(imageLocations) {
this.imageLocations = imageLocations;
this.imageCount = imageLocations.length;
this.loadCount = 0;
this.errorCount = 0;
this.states = {
PENDING: 1,
LOADING: 2,
RESOLVED: 3,
REJECTED: 4
};
this.state = this.states.PENDING;
// When loading the images, a promise will be returned to indicate
// when the loading has completed (and / or progressed).
this.deferred = $q.defer();
this.promise = this.deferred.promise;
}
// I reload the given images [Array] and return a promise. The promise
// will be resolved with the array of image locations.
Preloader.preloadImages = function (imageLocations) {
var preloader = new Preloader(imageLocations);
return (preloader.load());
};
Preloader.prototype = {
// Best practice for 'instnceof' operator.
constructor: Preloader,
// ---
// PUBLIC METHODS.
// ---
// I determine if the preloader has started loading images yet.
isInitiated: function isInitiated() {
return (this.state !== this.states.PENDING);
},
// I determine if the preloader has failed to load all of the images.
isRejected: function isRejected() {
return (this.state === this.states.REJECTED);
},
// I determine if the preloader has successfully loaded all of the images.
isResolved: function isResolved() {
return (this.state === this.states.RESOLVED);
},
// I initiate the preload of the images. Returns a promise.
load: function load() {
// If the images are already loading, return the existing promise.
if (this.isInitiated()) {
return (this.promise);
}
this.state = this.states.LOADING;
for (var i = 0 ; i < this.imageCount ; i++) {
this.loadImageLocation(this.imageLocations[i]);
}
// Return the deferred promise for the load event.
return (this.promise);
},
// ---
// PRIVATE METHODS.
// ---
// I handle the load-failure of the given image location.
handleImageError: function handleImageError(imageLocation) {
this.errorCount++;
// If the preload action has already failed, ignore further action.
if (this.isRejected()) {
return;
}
this.state = this.states.REJECTED;
this.deferred.reject(imageLocation);
},
// I handle the load-success of the given image location.
handleImageLoad: function handleImageLoad(imageLocation) {
this.loadCount++;
// If the preload action has already failed, ignore further action.
if (this.isRejected()) {
return;
}
// Notify the progress of the overall deferred. This is different
// than Resolving the deferred - you can call notify many times
// before the ultimate resolution (or rejection) of the deferred.
this.deferred.notify({
percent: Math.ceil(this.loadCount / this.imageCount * 100),
imageLocation: imageLocation
});
// If all of the images have loaded, we can resolve the deferred
// value that we returned to the calling context.
if (this.loadCount === this.imageCount) {
this.state = this.states.RESOLVED;
this.deferred.resolve(this.imageLocations);
}
},
// I load the given image location and then wire the load / error
// events back into the preloader instance.
// --
// NOTE: The load/error events trigger a $digest.
loadImageLocation: function loadImageLocation(imageLocation) {
var preloader = this;
// When it comes to creating the image object, it is critical that
// we bind the event handlers BEFORE we actually set the image
// source. Failure to do so will prevent the events from proper
// triggering in some browsers.
var image = angular.element(new Image());
image.bind('load', function (event) {
// Since the load event is asynchronous, we have to
// tell AngularJS that something changed.
$rootScope.$apply(
function () {
preloader.handleImageLoad(event.target.src);
// Clean up object reference to help with the
// garbage collection in the closure.
preloader = image = event = null;
}
);
}).bind('error', function (event) {
// Since the load event is asynchronous, we have to
// tell AngularJS that something changed.
$rootScope.$apply(
function () {
preloader.handleImageError(event.target.src);
// Clean up object reference to help with the
// garbage collection in the closure.
preloader = image = event = null;
}
);
}).prop('src', imageLocation);
}
};
// Return the factory instance.
return (Preloader);
});
现在,当我使用此指令时,所有其他图像/字体真棒图标在加载之前暂停。它会导致一个问题,因为字体真棒图标实际上在完全加载之前会短暂显示一个矩形框,并且配置文件图像在加载之前是空白的。 如果我从HTML中删除我的指令,一切都很好。
现在我意识到这种情况正在发生,因为它必须为后台切换器加载一系列图像,所以我想做的是在其他所有内容都先加载之前不要开始预加载。
有人可以告诉我是否有一种简单的方法可以做到这一点?