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 淡入淡出过渡/动画来平滑交换两个图像?理想情况下,我想要一种方法来做到这一点,而不需要太多额外的标记口香糖或 css 限制来限制图像在页面中的样式和定位。

类似于这个问题:Javascript Image Reloading; 闪烁

但是我已经在这个问题上使用了公认的答案,并且仍然在闪烁。

4

2 回答 2

2

每次mocode()触发时,您似乎都会创建一个新的<img />并替换以前的。通过重复使用相同的内容,您将获得更平滑的效果<img>- 只需更改其 src。

我不确定你的代码是如何工作的,所以很难确定,但我认为你需要这样的东西:

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,加载图形图像,并淡出以前的图形以显示新版本。

对此进行编码相当简单,但代码需要具有容错性——特别是它需要满足服务器响应(URL 和图像本身)以错误的顺序返回或在下一次触发setInterval. 通过提供取消在前一次迭代中建立的功能链的方法,延迟提供了极大的帮助。

间隔 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 派生的承诺

最后一个任务,graph_swap(),很简单,错误处理程序graph_error()略有不同。

有关详细信息,请参阅代码中的注释。

除了处理错误和延迟的服务器响应之外,这种方法的一个主要优点是主迭代器函数fetchGraph()变得非常简单。真正聪明的一行是一组链式pipe()命令,它们对任务进行排序并将任何错误路由到错误处理程序。

我已经尽可能多地对此进行了测试,并认为就像您自己的解决方案一样,它将提供平稳的过渡。

于 2012-11-14T13:15:01.523 回答
0

这是我目前确定的解决方案 - 我不确定这是否是最好的方法,所以非常欢迎任何替代想法:)

我使用了第二张图像并使用 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 回答