jquery - 使用 javascript 重新加载时顺利替换图像

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

类似这个问题:Javascript Image Reloading; flickers

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

最佳答案

每次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,并且稍微考虑一下错误消息可以变得更加具体。

编辑

泰科,您的解决方案看起来不错,如果有效,也许您会想坚持使用。与此同时,我一直在研究类似的想法,但以 jQuery 的 Deferreds 为中心的代码截然不同。如果您没有使用过它们,它们会有点令人兴奋,但当您有异步任务时,它们非常有用。

在您的例子中,您有三个连续的异步任务:获取图形 URL、加载图形图像以及淡出上一个图形以显示新版本。

编码相当简单,但代码需要具有容错能力 - 特别是它需要满足以错误顺序返回或在下一次触发 setInterval 之后返回的服务器响应(URL 和图像本身)。 。延迟通过提供取消上一次迭代中建立的函数链的方法来提供巨大帮助。

间隔 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 的 Promise

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

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

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

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

关于jquery - 使用 javascript 重新加载时顺利替换图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13378469/

相关文章:

javascript - 如何仅删除实例中添加的事件处理程序?

javascript - 如何获取全日历的开始和结束时间?

javascript - 如何使用 jQuery 影响同一类的 8 个 div 中的单个 div?

jquery - 使用 jQueryUI 对话框确认时如何停用 "Enter Key"

jquery-ui - jQuery UI 可选和滚动条

javascript - 当用户继续滚动时,在不中断动画的情况下在滚动页面上滑动 div 和关闭页面

javascript - 单击按钮会折叠所有 div,而不是折叠它所属的 div

ajax - Jquery UI slider - 输入值并将 slider 移动到位置

javascript - 可拖动固定元素内的固定元素

滚动时 jQuery ui 对话框停留在底部