Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
404 views
in Technique[技术] by (71.8m points)

html - JavaScript to make a fast-running image slideshow?

I am making an image slideshow using native JS.
I decided to make it myself because it needs to run at ~100ms intervals, and without any special transition effects (see it here), so I figured it was unnecessary to include a big library like JQuery just for just a simple application.
This is the code I am currently using [edit: original code - now modified]:

// JavaScript Document
function preloadimages(arr){ // the preloadimages() function is adapted from http://www.javascriptkit.com/javatutors/preloadimagesplus.shtml
    var newimages = [], loadedimages = 0;
    var postaction = function() {};
    var arr = (typeof arr != "object") ? [arr] : arr;
    function imageloadpost() {
        loadedimages++;
        if (loadedimages == arr.length) {
            postaction(newimages); //call postaction and pass in newimages array as parameter
        }
    }
    for (var i = 0; i < arr.length; i++) {
        newimages[i] = new Image();
        newimages[i].src = arr[i];
        newimages[i].onload = function() {
            imageloadpost();
        }
        newimages[i].onerror = function() {
            imageloadpost();
        }
    }
    return { //return blank object with done() method
        done: function(f) {
            postaction = f || postaction; //remember user defined callback functions to be called when images load
        }
    }
}

/* USAGE:
preloadimages(['ed.jpg', 'fei.jpg', 'budapest.gif', 'duck.jpg']).done(function(images) {
    images.sort(function(a, b) {
        return a.width - b.width; //sort images by each image's width property, ascending
    });
    alert(images[0].src); //alerts the src of the smallest image width wise
});
*/

function animateSlideshow() {
    var num = window.imgNum + 1 ;
    if (num >= d['imgs'].length) {
        num = 0;
    }
    window.imgNum = num;
    imgTag.src = d['imgs'][num];
    var t = window.setTimeout(function(){animateSlideshow(imgNum, imgTag, d)}, 100);
}

var d;
var imgTag;
var imgNum = 0;
$.onDomReady (function () { // This is not JQuery, it's a simple cross-browser library which you can read here: http://assets.momo40k.ch/common/js/$.js
    // data is an array that should be already defined on the calling page,
    // containing all the necessary information to generate all the rotation slideshows on the page
    for (i = 0; i < data.length; i++) {
        d = data[i];
        var div = document.getElementById(d['id']);
        imgTag = $.Elements.getElementsByClassName('theImage', div)[0];

        // preload the images...
        preloadimages(d['imgs']).done(function(images) {
            imgTag.src = d['imgs'][0];
            animateSlideshow();
        });
    }

});



<!-- HTML calling JS Scripts -->
... HTML document ...
<script src="http://assets.momo40k.ch/common/js/$-min.js" language="javascript" type="text/javascript"></script>
<script language="javascript" type="text/javascript">

var data = [];

// I would have an index for each slideshow on the page
data[0] = [];
data[0]['id'] = 'rotation2';// the ID of the tag the initial image is in
data[0]['imgs'] = ['http://www.momo40k.ch/images/pages/stefan_lehmann/bat/pic1.png',
    'http://www.momo40k.ch/images/pages/stefan_lehmann/bat/pic2.png',
    'http://www.momo40k.ch/images/pages/stefan_lehmann/bat/pic3.png',
    '... all the images ... '];

</script>
<script src="js/rotation.js" language="javascript" type="text/javascript"></script>
</body>
</html>



This is what the tag the initial image is in looks like:

<div id="rotation2" class="rotation blackbg">
    <img src="http://www.momo40k.ch/images/pages/stefan_lehmann/bat/pic1.png" width="300" title="" class="theImage" />
</div>





Now for the question: this script only allows me to have one single 'slideshow' on the page - because in each iteration of the loop it overrides the imgNum variable. Is there an other, better way of doing this slideshow (if possible without JQuery, otherwise OK), even in a completely different way? Thank you

EDIT: I have remade the script following Jared Farrish's answer and it's now working fine!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

There were some issues I saw with your code or approach, so I decided to redo it with the approach I would take. For instance:

  1. I would use document.images to get all images and, for ones that have the rotator-specific className, to identify (domElement.parentNode) and obtain the containing div, which will give me it's id.
  2. I would use the parentNode.id of the class="rotation" images to create an object with sets (by container ids) I can use to store references to the img nodes.
  3. Use closure scope to stay out of the global scope, as well as be able to share variables between the closure-scoped functions.
  4. Use variable functions to setup the handler and callback functions.

Let me know if you have any questions or find something that doesn't work.

<div id="rotator1" class="rotation blackbg">
    <img class="slides" src="http://upload.wikimedia.org/wikipedia/commons/6/6e/Brandenburger_Tor_2004.jpg" />
    <img class="slides" src="http://upload.wikimedia.org/wikipedia/commons/a/ad/Cegonha_alsaciana.jpg" />
    <img class="slides" src="http://upload.wikimedia.org/wikipedia/commons/d/da/CrayonLogs.jpg" />
</div>
<div id="rotator2" class="rotation blackbg">
    <img class="slides" src="http://upload.wikimedia.org/wikipedia/commons/1/17/Bobbahn_ep.jpg" />
    <img class="slides" src="http://upload.wikimedia.org/wikipedia/commons/9/90/DS_Citro%C3%ABn.jpg" />
    <img class="slides" src="http://upload.wikimedia.org/wikipedia/commons/f/f0/DeutzFahr_Ladewagen_K_7.39.jpg" />
    <img class="slides" src="http://upload.wikimedia.org/wikipedia/commons/c/c7/DenglerSW-Peach-faced-Lovebird-20051026-1280x960.jpg" />
    <img class="slides" src="http://upload.wikimedia.org/wikipedia/commons/4/4d/FA-18F_Breaking_SoundBarrier.jpg" />
</div>

var slideshows = function(){
    var timeouts = {},
        imgs;

    function preloadImages(list){
        var loading = list,
            img,
            loaded = {},
            newimages = [];

        var imageloaded = function(){
            // this here is one of the new Image()s we created
            // earlier; it's not the "real" image on the screen.
            // So I put the reference to the actual image it represents
            // on the screen in the rel attribute so I can use it's
            // reference; I just have to use this.rel to get it.
            var parent = this.rel.parentNode;

            // Keeping track of the individual sets loaded.
            loaded[parent.id]++;

            // Here is where this/self gets it's context from, when
            // we .call(parent, 0). It's the parentNode to the image
            // we've referenced above. This should only run once,
            // when the last image has loaded for the set.
            if (loaded[parent.id] == loading[parent.id].length) {
                animateSlideshow.call(parent, 0);
            }
        };

        var imagenotloaded = function(){
            // this.rel is the reference to the actual image we
            // have in the DOM, so we'll set the error flag on it.
            this.rel['imageerror'] = true;
            imageloaded.call(this);
        };

        for (var load in loading) {
            // loaded is equivalent to imgs.sets, so load is the
            // id for the container.
            loaded[load] = [];

            // Now we're going to loop over every image in the
            // current set, creating a Javascript image object
            // to initiate the download of the file and tell us
            // when it's finished. Not the newimages[i].rel = img
            // part.
            for (var i = 0, l = loading[load].length; i < l; i++) {
                img = loading[load][i];
                newimages[i] = new Image();
                newimages[i].onload = imageloaded;
                newimages[i].onerror = imagenotloaded;
                newimages[i].rel = img;
                newimages[i].src = img.src;
            }
        }
    }

    var animateSlideshow = function(current) {
        // this could be used instead of self. I was doing
        // something else at first, but making this copy
        // of the context (this) is not necessary with this
        // code. It doesn't hurt either.
        var self = this;

        // Our current context is the containing div, so
        // this.id/self.id will give us the key to the correct
        // group in imgs.sets, and the current argument will
        // tell us with image in that list we're currently
        // working with. First, we hide the last displayed
        // image.
        imgs.sets[self.id][current].style.display = 'none';

        // Increment to get the next image.
        current++;

        // If we're at the end, let's move back to the
        // beginning of the list.
        if (current >= imgs.sets[self.id].length) {
            current = 0;
        }

        // This skips images which had an error on load. The
        // test for this in the markup above is the third image
        // in rotator1, which is not an image url.
        if (imgs.sets[self.id][current].imageerror == true) {
            // See how I'm passing self using .call()? This sets
            // the context for the next animateSlideshow() call,
            // which allows this/self to work on the correct div
            // container.
            animateSlideshow.call(self, current);
            return;
        }

        imgs.sets[self.id][current].style.display = 'inline';

        // Everything is organized by the self.id key, event
        // saving the references to the timeouts.
        timeouts[self.id] = setTimeout(function(){
            animateSlideshow.call(self, current);
        }, 100);
    };


    function getImages(){
        var list = document.images,
            img,
            data = {sets: {}, allimages: []},
            parent;

        // document.images gives me an array of references to all
        // img elements on the page. Let's loop through and create
        // an array of the relevant img elements, keying/grouping on the
        // parent element's id attribute.
        for (var i = 0, l = list.length; i < l; i++){
            img = list[i];
            parent = img.parentNode;

            // parent should now be a reference to the containing div
            // for the current img element. parent.id should give us
            // rotator1 or rotator2 in the demo markup.
            if (parent.className.indexOf('rotation') !== -1) {
                if (!data.sets[parent.id]) {
                    data.sets[parent.id] = [];
                }

                // Let's put the img reference into the appropriate
                // imgs.sets. I also put the img.src into an index
                // container in data.allimages; this is also a remnant
                // of a previous approach I took. It could probably be
                // removed unless you need it.
                data.sets[parent.id].push(img);
                data.allimages.push(img.src);
            }
        }

        return data;
    }

    function initializeSlideshows(){
        imgs = getImages();

        preloadImages(imgs.sets);
    }

    initializeSlideshows();
};

$.onDomReady(slideshows);

http://jsfiddle.net/DLz92/1


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...