Angular预加载器在其他所有加载后启动

时间:2015-04-17 13:31:30

标签: angularjs

希望这是一个简单的答案。

我有这个指令控制网站上的背景图像。它在图像之间旋转,但在开始设置背景图像之前,它实际上是预加载它们。这是我的指令代码:

// ---
// 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中删除我的指令,一切都很好。

现在我意识到这种情况正在发生,因为它必须为后台切换器加载一系列图像,所以我想做的是在其他所有内容都先加载之前不要开始预加载。

有人可以告诉我是否有一种简单的方法可以做到这一点?

0 个答案:

没有答案