使用javascript重新加载时平滑替换图像

时间:2012-11-14 12:02:45

标签: jquery jquery-ui

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

与此问题类似:Javascript Image Reloading; flickers

但是我已经在这个问题上使用了接受的答案,但仍然闪烁。

2 个答案:

答案 0 :(得分:2)

每次mocode()触发时,您似乎都会创建一个新<img />并替换之前的<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); } ,您将获得更平滑的效果 - 只需更改其src即可。

我不确定您的代码究竟是如何工作的,因此很难确定,但我认为您需要这样的代码:

setInterval

您可能需要以不同的方式构建网址,并且稍加考虑可以使错误消息更具体。

修改

Tyco,你的解决方案看起来不错,如果它有效,你可能会坚持下去。与此同时,我一直在玩类似的想法,但是以jQuery的Deferreds为中心的代码却截然不同。如果您没有使用它们,这些有点令人兴奋,但在您具有异步任务时非常有用。

在您的情况下,您有三个连续的异步任务,获取图形URL,加载图形图像,淡出前一个图形以显示新版本。

编码这是相当简单的,但代码需要容错 - 特别是它需要满足服务器响应(URL和图像本身)以错误的顺序返回或在下一次触发{{1 }}。延迟通过提供取消在前一次迭代中建立的功能链的方法而有很大帮助。

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

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

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

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

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

答案 1 :(得分: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中运行良好 - 但我没有在其他浏览器中测试过。