2

javascriptのsetIntervalを使用してタイマーのページ上の画像を置き換えています(これは最新である必要がある統計グラフです)。

私は次のコードを使用しています:

var tid = setInterval(mycode, 20000);
function mycode() {
    var theDate = new Date();
    var mili = theDate.getUTCDate() + theDate.toLocaleTimeString() + theDate.getMilliseconds();
    var img = $('<img />')
        .attr('src', '@Url.Action("Chart", "Home", new {ForceNoCache = "theDate"})')
        .attr('id', 'GraphImage')
        .load(function () 
        {
            if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) 
            {
                alert('broken image!');
            } 
            else 
            {
                $('#GraphImage').replaceWith(img);
            }
        });
        img.attr('src', img.attr('src').replace("theDate", mili));
}

画像は現在、次のようにページのdivに配置されています。

    <div style="float:left;margin-right:20px">
    <img id="GraphImage" alt="Graph of Results" src="@Url.Action("ChartImage", "Home")" /> 
  </div>

このコードは機能し、20秒ごとに画像を置き換えます。ただし、.load関数を使用していて、完全に読み込まれるまで画像を置き換えない場合でも、ブラウザーが画像を入れ替えるときに、厄介な小さなちらつきが発生します。

jQueryのフェードトランジション/アニメーションを使用して2つの画像をスムーズに入れ替えるにはどうすればよいですか?理想的には、画像のスタイル設定とページ内での配置方法に関する追加のマークアップgumphまたはcssの制限をあまり必要とせずに、これを行う方法が必要です。

この質問に似ています:Javascript画像の再読み込み; ちらつき

しかし、私はすでにこの質問で受け入れられた答えを使用していて、まだちらつきがあります。

4

2 に答える 2

2

発火するたびに、新しいものを作成して前のものを置き換えるmocode()ように見えます。<img />同じものを再利用することで、よりスムーズな効果が得られます.srcを<img>変更するだけです.

あなたのコードがどのように機能するのか正確にはわからないので、特定するのは難しいですが、次のようなものが必要だと思います:

function graphError() {
    alert('Graph: reload failed');
}
var $graphImg = $("#GraphImage").on('error', graphError);
var tid = setInterval(mycode, 20000);
function mycode() {
    var theDate = new Date();
    var mili = theDate.getUTCDate() + theDate.toLocaleTimeString() + theDate.getMilliseconds();
    $.ajax({
        url: '@Url.Action("Chart", "Home", new {ForceNoCache = "theDate"})',
    }).done(function(srcURL) {
        $graphImg.attr('src', srcURL.replace("theDate", mili));
    }).error(graphError);
}

URL を別の方法で作成する必要がある場合があります。少し考えれば、エラー メッセージをより具体的にすることができます。

編集

Tyco さん、あなたのソリューションは良さそうです。その間、私は同様のアイデアで遊んでいましたが、jQuery の Deferreds を中心とした劇的に異なるコードです。これらを使用したことがない場合は少し驚くかもしれませんが、非同期タスクがある場合は非常に便利です。

あなたの場合、グラフの URL をフェッチし、グラフの画像を読み込み、前のグラフをフェードアウトして新しいバージョンを表示するという 3 つの連続した非同期タスクがあります。

これをコーディングするのはかなり簡単ですが、コードはエラー トレラントである必要があります。特に、サーバーの応答 (URL と画像自体) が間違った順序で返されたり、setInterval. Deferred は、前の反復で確立された関数チェーンをキャンセルする手段を提供することで、非常に役立ちます。

20 秒の間隔で問題はありませんが、ある日、インターネット/サーバーの動作が非常に遅くなるか、間隔を短くすることを決定する場合があります。

以前に Deferreds を使用したことがない限り、以下のコードは非常に異質に見えますが、私の側のエラーがなければ、機能するはずです。

Javascript:

$(function() {
    var $graphImg1 = $("#GraphImage1");
    var $graphImg2 = $("#GraphImage2");

    var promises = {
        fetchGraphUrl: null,
        loadImg: null,
        fadeOut: null
    };

    var graph_errors = {
        threshold: 5,//set low for testing, higher in production environment.
        count: 0
    };
    var interval = 5;//seconds

    $graphImg2.on('error', function() {
        if(promises.loadImg) {
            promises.loadImg.reject();
        }
    }).on('load', function() {
        if(promises.loadImg) {
            promises.loadImg.resolve();
        }
    });

    function graph_fetchURL(milli) {
        if(promises.fetchGraph) {
            promises.fetchGraph.reject(milli, 'fetchURL', 'timeout');
        }
        var dfrd = promises.fetchGraph = $.Deferred().fail(function() {
            jqXHR.abort();
        });
        var jqXHR = $.ajax({
            url: '@Url.Action("Chart", "Home", new {ForceNoCache = "theDate"})'
        }).done(function(srcURL) {
            dfrd.resolve(milli, srcURL);
        }).fail(function(jqXHR, textStatus, errorThrown) {
            dfrd.reject(milli, 'ajax', textStatus);
        });
        return dfrd.promise();
    }

    function graph_loadImg(milli, srcURL) {
        if(promises.loadImg) {
            promises.loadImg.reject(milli, 'loadImg', 'timeout');
        }
        //An extra deferred is needed here because $graphImg2's handlers don't know about milli.
        var dfrd = $.Deferred();
        promises.loadImg = $.Deferred().done(function() {
            dfrd.resolve(milli);
        }).fail(function() {
            dfrd.reject(milli, 'loadGraph', 'load error');
        });
        $graphImg2.attr('src', srcURL.replace("theDate", milli));
        return dfrd.promise();
    }

    function graph_fade(milli) {
        if(promises.fadeOut) {
            promises.fadeOut.reject(milli, 'fade', 'timeout');
        }
        promises.fadeOut = $.Deferred();
        $graphImg2.show();
        $graphImg1.fadeOut('fast', function() {
            promises.fadeOut.resolve(milli);
        });
        return promises.fadeOut.promise();
    }

    function graph_swap() {
        $graphImg1.attr('src', $graphImg2.attr('src')).show();
        $graphImg2.hide();
    }

    function graph_error(timestamp, phase, txt) {
        var txt = txt ? (' (' + txt + ')') : '';
        console.log(timestamp + ': fetchGraph failed in the ' + phase + ' phase' + txt);
        if(++graph_errors.count >= graph_errors.threshold) {
            clearInterval(tid);
            console.log('fetchGraph errors exceeded threshold (' + graph_errors.threshold + ')');
        }
        return $.Deferred().promise();//an unresolved, unrejected promise prevents the original promise propagating down the pipe.
    }

    function fetchGraph() {
        var now = new Date();
        var milli = now.getUTCDate() + now.toLocaleTimeString() + now.getMilliseconds();
        graph_fetchURL(milli)
            .pipe(graph_loadImg, graph_error)
            .pipe(graph_fade, graph_error)
            .pipe(graph_swap, graph_error);//this .pipe() chain is the glue that puts everything together.
    }
    fetchGraph();
    var tid = setInterval(fetchGraph, interval * 1000);
});

CSS:

#graphWrapper {
    position: relative;
    float: left;
    margin-right: 20px;
}
#GraphImage, #GraphImage2 {
    position: absolute;
    left: 0;
    top: 0;
    width: XXpx;
    height: YYpx;
}
#GraphImage2 {
    display: none;
}

HTML:

<div id="graphWrapper">
    <img id="GraphImage1" alt="Graph of Results" src="" /> 
    <img id="GraphImage2" alt="" src="" /> 
</div>

ご覧のとおり、すべてがかなり簡潔な関数の集まりに編成されており、それぞれが共通のテーマのバリエーションです。つまり:-

  • 前の繰り返しからの同じ種類の未処理のタスクを拒否する
  • 次の反復で拒否できる deferred を作成する
  • タスク自体を実行する
  • Deferred から派生した promise を返す

最後のタスクgraph_swap()は簡単で、エラー ハンドラgraph_error()は少し異なります。

詳細については、コード内のコメントを参照してください。

エラーの処理とサーバー応答の遅延に加えて、このアプローチの主な利点は、メインのイテレータ関数が非常にfetchGraph()単純になることです。非常に巧妙な行はpipe()、タスクを順序付けし、エラーをエラー ハンドラーにルーティングするチェーン コマンドのセットです。

私はこれをできる限りテストしましたが、あなた自身のソリューションのように、スムーズな移行ができると思います.

于 2012-11-14T13:15:01.523 に答える
0

これが私が現在解決している解決策です-これがこれを行うための最良の方法であるかどうかはわかりません。そのため、代替のアイデアは大歓迎です:)

2 番目の画像を使用し、z-index を使用して古い画像の下に新しい画像を追加し、DOM から削除する前に古い画像をフェードアウトしました。

これには、イメージが含まれている DIV 内に絶対位置があり、新しいイメージが横ではなくすぐ後ろになるようにする必要があります。

<script>
    // set interval
    var tid = setInterval(mycode, 20000);
    function mycode() {
        var theDate = new Date();
        var mili = theDate.getUTCDate() + theDate.toLocaleTimeString() + theDate.getMilliseconds();
        var img = $('<img />')
            .attr('src', '@Url.Action("Chart", "Home", new {ForceNoCache = "theDate"})')
            .attr('id', 'GraphImageNew')
            .attr('style', 'z-index:: 4;')
            .load(function () {
                if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {
                    alert('broken image!');
                }
                else {
                    $('#GraphContainer').append(img);
                    $('#GraphImage').attr('id', 'GraphImageOld');
                    $('#GraphImageOld').fadeOut(1000, function () {
                        $('#GraphImageOld').remove();
                        $('#GraphImage').attr('style', 'z-index: 5;');
                    });
                    $('#GraphImageNew').attr('id', 'GraphImage');

                }
            });
            img.attr('src', img.attr('src').replace("theDate", mili));

    }
    function abortTimer() { // to be called when you want to stop the timer
        clearInterval(tid);
    }
</script>

これは IE と Firefox では問題なく動作するようですが、他のブラウザではテストしていません。

于 2012-11-15T14:06:56.117 に答える