1

目標:

  • 配列内の各要素でロジックを実行します。
  • X次の実行の間にmsを待ちます。
  • mouseover(#slider)遅延の一時停止-遅延=1000ミリ秒で、300ミリ秒が経過した場合mouseout(#slider)、残りの700ミリ秒の遅延のカウントダウンを再開します。
  • 最後の要素で実行した後、ループバックして再度実行します-永久に。

視覚的な説明は次のとおりです。

var = s Array(1,2,3)

var x = s[1];   //get first element   
console.log(x); //do something to it
wait();         //START wait timer 1000ms

//------------> timer : 300ms
//------------> user  : mouseover (#slider) : pause timer
//------------> user  : waited 5000ms
//------------> user  : mouseout  (#slider) : resume timer
//------------> timer : 300ms --> still 700ms to go!
//------------> timer : 500ms
//------------> user  : mouseover (#slider) : pause timer
//------------> user  : waited 10000ms
//------------> user  : mouseout  (#slider) : resume timer
//------------> timer : 500ms --> still 500ms to go!

var x = s[2];   //get second element   
console.log(x); //do something to it
wait();         //START wait timer 1000ms

//------------> timer : 200ms
//------------> user  : mouseover (#slider) : pause timer
//------------> user  : onclick   (.slideButton1) : change index of array and clear timer
//------------> user  : waited 6000ms
//------------> user  : mouseout  (#slider) : resume timer
//------------> timer : 0ms --> still 1000ms to go!

var x = s[1];   //get first element   ( index was changed by clicking button )
console.log(x); //do something to it
wait();         //START wait timer 1000ms

// ... s[2] ... s[3] ...
//theres nothing else in the array, lets start back from s[1] again!

ソリューション:

jQuery:

http://jsfiddle.net/zGd8a/8/

この解決策は、関連する投稿から来ました。このプラグインの公式ソースはここにあります。

ネイティブJS:

http://jsfiddle.net/SyTFZ/4/

AaditMShahによるこの回答は本当に役に立ちました。彼はまた、デルタタイミングとそれが同様の場合にどのように役立つかについても詳しく説明します。

新しい目標:

これらの方法のいずれかを抽象化して、他の用途に使用できるようにします。

4

3 に答える 3

6

さて、質問を完全に単純化したので、配列の各要素の反復の間に遅延を置き、コールバック関数が戻るまで永久に循環するジェネリック配列イテレータ関数を次に示しますfalse

function iterateArrayWithDelay(arr, delay, fn) {
    var index = 0;

    function next() {
        // protect against empty array
        if (!arr.length) {
            return;
        }

        // see if we need to wrap the index
        if (index >= arr.length) {
            index = 0;
        }

        // call the callback
        if (fn(arr[index], arr, index) === false) {
            // stop iterating
            return;
        }

        ++index;

        // schedule next iteration
        setTimeout(next, delay);
    }
    // start the iteration
    next();
}

そして、あなたの例では、次のように使用します。

iterateArrayWithDelay(s, 1000, myFunction);

myFunction各要素を処理するコールバック関数として定義する場所。コールバックには次の3つの項目が渡されます。

myFunction(item, array, i){
    // your code here to process item
}

.delay()アニメーションキューを使用するjQueryメソッドでのみ機能します。コード例では.delay('1000')、同じオブジェクトの後にjQueryアニメーションメソッドがないため、は何もしていません。

メモリリークに関しては、によって表されるオブジェクトの存続期間thisとそのプロパティを確認できないため、実行していることの全体的なコンテキストを追跡することは困難です。このシーケンスはかなり奇妙に見えます:

var x = t.s[i];
...
delete t.s[i];
t.s.push(x);

特に、内容への参照がまだ残っているため、ガベージコレクションが行われないdeleteため、ステートメントが実際にどのように機能しているかはわかりません。xさらに、deletejavascriptでは、オブジェクトを解放したり、配列要素を削除したりするためではなく、オブジェクトプロパティを削除するために使用されます。オブジェクトを解放するには、そのオブジェクトへのすべての参照を削除する必要があります(他の値に設定して、参照が含まれなくなったり、スコープから外れたりしないようにします)。したがって、中にあるものへの参照を削除することは決してないのでt.s[i]、何も解放されません。


の使用はsetTimeout()再帰を引き起こしていません。を呼び出すとsetTimeout()、タイマーが設定され、そのタイマーに関連付けられているデータ構造に関数参照が配置されます。その後、呼び出し元の関数は実行を継続し、実行を終了します。したがって、setTimeout()が起動して再度呼び出す前に、実行が完了します。したがって、実際には再帰ではありません。これは、時間間隔で区切られた一連のシーケンシャル関数呼び出しであり、次の呼び出しが実行される前に終了します(javascriptはシングルスレッドであり、タイマーが将来に設定されているため)。

于 2012-10-17T03:13:41.387 に答える
4

さて、私はjqueryを使用していません。おそらく、あなたが何を達成しようとしているのかについての手がかりはありません。しかし、私が理解していることから、あなたはこのようなことをすべきだと思います:

var i = 0;
var t = this;

var timer = new DeltaTimer(function (time) {
    // your animation
    var x = t.s[i];
    x.delay("1000").css("background-color", "#FAAF16");
    delete t.s[i];
    t.s.push(x);
    // increment i
    i++;
}, 1000);

var start = timer.start();

ここで、私が。というコンストラクターを使用したことに気付くでしょうDeltaTimer。このコンストラクターは、この要点で定義されています。startこれにより、関数を使用してアニメーションを正確に制御できますstoprender渡される関数には、である引数が与えられtimeますDate。この式は、time - start関数が呼び出された正確な時刻を示します(たとえば、、、、4... )。10002000

またはを使用DeltaTimerする利点は次のとおりです。setTimeoutsetInterval

  1. それは自分自身を修正します。これは、アニメーションがよりスムーズになり、ラグが少なくなることを意味します。
  2. タイマーを開始および停止することにより、アニメーションを制御できます。
  3. 関数呼び出しの正確な時刻が関数に渡されます。これは、どのフレームがレンダリングされているか、スプライトがどこにレンダリングされるべきかなどを追跡するのに役立ちます。
  4. アニメーションのロジックは、タイミング制御のロジックから分離されています。したがって、コードはよりまとまりがあり、より緩く結合されます。

デルタタイミングに関する他の回答は、ここここここで読むことができます。

編集1:それは実際にはかなり簡単です。配列の最初の要素をシフトアウトし、処理してから、最後にプッシュバックするだけです。ロジックは次のとおりです。

function loopIterate(array, callback, interval) {
    var timer = new DeltaTimer(function (time) {
        var element = array.shift();
        callback(element, time - start);
        array.push(element);
    }, interval);

    var start = timer.start();
};

これで、配列を作成し、次のようにループすることができます。

var body = document.body;

loopIterate([1, 2, 3], function (element, time) {
    body.innerHTML += element + ": " + time + "<br/>";
}, 1000);

ここで出力を見ることができます:http://jsfiddle.net/aGQfr/

編集2:おっと、問題が見つかりました。私が理解していることから、現在の要素の処理が終了してから一定の時間、次の要素を処理したいと考えています。私のデルタタイミングスクリプトはそれをしません。一定の時間間隔でのみ機能を実行します。

したがって、デルタタイミングはまったく必要ありません。setTimeout各要素が処理された後に呼び出す必要があります。

function loopIterate(array, callback, interval) {
    var start = + new Date;
    process();

    function process() {
        var element = array.shift();
        callback(element, new Date - start);
        array.push(element);

        setTimeout(process, interval);
    }
};

その後、配列を作成し、次のようにループします。

loopIterate([1, 2, 3], function (element, time) {
    alert(element);
}, 1000);

ここでデモを見ることができます(ブラウザが気に入らない場合があることに注意してください):http://jsfiddle.net/aGQfr/1/

編集3:メソッド1と2を組み合わせて、次のようなスクリプトを作成することもできます。

  1. 処理が終了するのを待ってから、処理する次の要素をイベントキューに追加します。
  2. startandstop関数を使用して制御できます。
  3. コールバックが呼び出された正確な時間を示します。
  4. 処理をタイミング制御から分離します。

次のメソッドを使用してLoopIteratorイテレータオブジェクトを返すというコンストラクター関数を作成します。startstop

function LoopIterator(array, callback, interval) {
    var start, iterate, timeout;

    this.start = function () {
        if (!iterate) {
            start = + new Date;
            iterate = true;
            loop();
        }
    };

    this.stop = function () {
        if (iterate) {
            clearTimeout(timeout);
            iterate = false;
        }
    };

    function loop() {
        var element = array.shift();
        callback(element, new Date - start);
        array.push(element);

        if (iterate) timeout = setTimeout(loop, interval);
    }
}

これで、次のように新しいイテレータを作成して開始できます。

var iterator = new LoopIterator([1, 2, 3], function (element, time) {
    alert(element);
}, 3000);

iterator.start();

必要に応じて、マウスが要素上または要素外に移動したときに、イテレータを停止および開始することもできます。

var div = document.getElementsByTagName("div")[0];
div.addEventListener("mouseout", iterator.start, false);
div.addEventListener("mouseover", iterator.stop, false);

停止するとイテレータの状態が保持され、再開すると中断したところから続行されます。

ここでデモを見ることができます:http://jsfiddle.net/PEcUG/

編集4:では、単純なスライダーを作成しますか?HTML、CSS、JavaScriptの順に始めましょう。

HTML:

<div class="slider">
    <div class="slide">Slide 1</div>
    <div class="slide">Slide 2</div>
    <div class="slide">Slide 3</div>
</div>

divと呼ばれるクラスを持つ要素sliderがあります(ページに複数のスライダーがある可能性があるため)。各スライダーには、クラスを持つ0個以上のdiv要素がありslideます。各スライドには任意のコンテンツが含まれる場合があります。スライダーにもボタンがありますが、JavaScriptによって自動的に生成されるため、これはHTMLに含まれていません。冗長性はありません。また、どのスライドにも手動で番号が付けられていないことに注意してください。すべてがJavaScriptによって処理されます。

CSS:

.slide {
    background-color: #EEEEEE;
    -moz-border-radius: 0.25em;
    -webkit-border-radius: 0.25em;
    border-radius: 0.25em;
    display: none;
    padding: 1em;
}

.slider-button {
    background-color: #CCCCCC;
    -moz-border-radius: 0.25em;
    -webkit-border-radius: 0.25em;
    border-radius: 0.25em;
    cursor: pointer;
    float: right;
    height: 1.25em;
    margin: 0.5em;
    width: 1.25em;
}

好みに合わせて任意のCSSを指定できます。ただし、重要な点は、スライドを最初に非表示にする.slide必要があるため、この必要があるということです。display: none;また.slider-button、持っている必要がありますfloat: right;。右に浮かぶ要素は順序が逆になるため、これは重要です。したがって、最初のボタンは実際には最後のボタンです。これはJavaScriptで正しく処理する必要があるため、何をしているのかわからない場合は変更しないでください。

JavaScript:

了解しました。このボトムアップについて説明します。

window.addEventListener("DOMContentLoaded", function () {
    var sliders = document.querySelectorAll(".slider");
    var length = sliders.length;

    for (var i = 0; i < length; i++)
        new Slider(sliders[i], 2000);
}, false);

Slider渡されたスライダー要素を初期化して開始するコンストラクター関数を次に示します。2番目の引数として2つのスライド間の時間間隔を受け入れます。のコードは次のSliderとおりです。

function Slider(slider, interval) {
    var slides = slider.querySelectorAll(".slide");
    var iterate, start, timeout, delay = interval;
    slides = Array.prototype.slice.call(slides);
    var buttons = [], numbers = [], goto = [];
    var length = slides.length;

    for (var i = 0; i < length; i++) {
        var button = document.createElement("div");
        button.setAttribute("class", "slider-button");
        slider.appendChild(button);
        buttons.unshift(button);
        numbers.push(i + 1);

        var handler = getHandler(length - i);
        button.addEventListener("click", handler, false);
        goto.unshift(handler);
    }

    this.goto = function (index) {
        var gotoSlide = goto[index];
        if (typeof gotoSlide === "function")
            gotoSlide();
    };

    slider.addEventListener("mouseover", stop, false);
    slider.addEventListener("mouseout", start, false);

    this.start = start;
    this.stop = stop;

    showSlide();
    start();

    function start() {
        if (!iterate) {
            iterate = true;
            start = + new Date;
            timeout = setTimeout(loop, delay);
        }
    }

    function stop() {
        if (iterate) {
            iterate = false;
            clearTimeout(timeout);
            delay = interval - new Date + start;
        }
    }

    function loop() {
        hideSlide();
        slideSlider();
        showSlide();

        if (iterate) {
            start = + new Date;
            timeout = setTimeout(loop, interval);
        }
    }

    function hideSlide() {
        slides[0].style.display = "none";
        buttons[0].style.backgroundColor = "#CCCCCC";
    }

    function slideSlider() {
        slides.push(slides.shift());
        buttons.push(buttons.shift());
        numbers.push(numbers.shift());
    }

    function showSlide() {
        slides[0].style.display = "block";
        buttons[0].style.backgroundColor = "#FAAF16";
    }

    function getHandler(number) {
        return function () {
            hideSlide();
            while (numbers[0] !== number) slideSlider();
            showSlide();
        };
    }
}

コードはかなり自明です。のすべてのインスタンスにSliderは、より細かく制御するためのstartstopおよびメソッドがあります。gotoこのgotoメソッドは、スライドのインデックス番号を取ります。nスライドの場合、インデックスの範囲はから0ですn - 1。それでおしまい。

スライダーのデモはここにあります:http://jsfiddle.net/SyTFZ/4/

于 2012-10-17T03:45:45.373 に答える
1

プラグインjquery-timingは、非常に短いコードでこの効果を実現するのに役立ちます。マウスオーバーを待つ例はすでにあります。

これはあなたのユースケースにも適応できると確信しています:

function noHover() {
   return this.is(':hover') ? this.wait('mouseleave') : this;
}

$('.images').repeat().each($).fadeIn($).wait(1000).wait(noHover).fadeOut($);
于 2012-10-21T06:38:07.547 に答える