1

I am building a slide show framework and was having trouble figuring out the best way to cycle the animation function, I know I could use setInterval() but was wondering if there was a better way to repeat it without being to resource intensive.

JAVASCRIPT:

class slideShow {
    constructor(imgDir, dataList) {
        this.imgDir = imgDir;
        this.dataList = dataList;
        this.settings = 0;
        this.imgList = 0;
        this._init();
    }
    _init(){
        $.getJSON(`${this.imgDir}/${this.dataList}.json`, (data)=>{
            this.settings = data.settings;
            this.imgList = data.imgList;
        })
    }
    start(){
        for(let img of this.imgList){
            $(this.settings.selector).fadeOut(this.settings.fadeTime, ()=>{
                $(this.settings.selector).attr("src",`${this.imgDir}/${img}`);
                $(this.settings.selector).fadeIn(this.settings.fadeTime);
            });
        }
    }
}

JSON:

{
    "settings":{
        "selector":"#slideShow",
        "fadeTime":1000
    },
    "imgList":[
        "slide_01.jpg",
        "slide_02.jpg",
        "slide_03.jpg"
    ]
}

2 Answers2

2

A simple solution is to pseudo-recursively loop from the "completion callback" of the final step of the animation.

start() {
    var current = 0;
    (function loop() {
        let img = imgList[current++];
        current %= imgList.length;
        let $sel = $(this.settings.selector);
        $sel.fadeOut(this.settings.fadeTime, () => {
            $sel.attr("src",`${this.imgDir}/${img}`);
            $sel.fadeIn(this.settings.fadeTime, loop); // <-- here
        });
    })();
}

Note that I've removed the for ... of loop - each pass of the loop function here just handles one frame of the slideshow.

This would resolve what appears to be a bug in your original code, where you'd get three consecutive fadeOut animations (two of which would of course be invisible) because nothing in your code allows for the first cycle of fadeOut, change image, fadeIn to complete before the next iteration of the for ... of loop starts.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • This worked, though i had to add a `that = this`. Also, thank you for pointing out the bug. – Adraxas Nazaron Nov 02 '16 at 22:56
  • 1
    Ironic - I've just had a long chat with the other answerer about the correct handling of `this` and didn't spot the bug in my own answer :) – Alnitak Nov 02 '16 at 23:01
  • the alternative would be to dereference the selector and settings values outside of the `loop`, since they're the only variables that depend on `this`. Oh, and the `imgDir` too. – Alnitak Nov 02 '16 at 23:02
1

You can use .then(), jQuery .promise(), recursion

class slideShow {
    constructor(imgDir, dataList) {
        this.imgDir = imgDir;
        this.dataList = dataList;
        this.settings = 0;
        this.imgList = 0;
        this._init();
    }
    _init(){
        $.getJSON(`${this.imgDir}/${this.dataList}.json`, (data)=>{
            this.settings = data.settings;
            this.imgList = data.imgList;
        }).then(this.start)
    }
    start(){
        for(let img of this.imgList){
            $(this.settings.selector).fadeOut(this.settings.fadeTime, ()=>{
                $(this.settings.selector).attr("src",`${this.imgDir}/${img}`);
                $(this.settings.selector).fadeIn(this.settings.fadeTime);
            });
        }
        $(this.settings.selector).promise()
        .then(() => this.start() /* or this._init() */)
    }
}
guest271314
  • 1
  • 15
  • 104
  • 177
  • 1
    this isn't "real" recursion, because the event loop gets to unwind the stack before the `.then` callback gets called – Alnitak Nov 02 '16 at 22:04
  • @Alnitak Ok. What should the process be termed? Your approach is probably what OP is looking for at animation portion. Not certain if OP wants animations in sequential order? If yes, would substitute `.queue()`, `$,map()` for `for..of` loop – guest271314 Nov 02 '16 at 22:07
  • Your `.then(this.start)` is wrong too, it should be `.then(() => this.start)` since otherwise you've got an undefined `this` – Alnitak Nov 02 '16 at 22:07
  • 1
    I always call it psuedo-recursion – Alnitak Nov 02 '16 at 22:07
  • @Alnitak Why would `this` be `undefined`? A function is being referenced outside of `for..of` loop. `.then(() => this.start)` would not call `this.start`, but reference `this.start`, yes? Was not aware of the term "pseudo-recursion", or that there was a difference. Will consider asking a Question concerning the same; though not sure how to formulate the text of the Question? – guest271314 Nov 02 '16 at 22:08
  • `this` is undefined because when `start` gets called the second time it hasn't been called using the standard function call mechanism so `this` becomes unbound. I also made a typo myself - it should read `.then(() => this.start())` – Alnitak Nov 02 '16 at 22:14
  • @Alnitak _"`this` is undefined because when `start` gets called the second time it hasn't been called using the standard function call mechanism."_ Now am confused. Also, since `Promise`, `.then()` sets `this` to `window`, would not `this` be `window` within `.then()` at `.then(() => this.start())` at your previous comment? – guest271314 Nov 02 '16 at 22:16
  • 1
    No, because the arrow function binds `this` to whatever it is in the outer scope – Alnitak Nov 02 '16 at 22:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/127228/discussion-between-guest271314-and-alnitak). – guest271314 Nov 02 '16 at 22:18