jQuery背景更换时的闪烁问题

4
我已经为当前代码创建了一个jsfiddle。http://jsfiddle.net/gL5sB/38/ 我正在尝试在滚动事件中更改body背景css。当背景更改时,更新css并加载新的图像时会出现闪烁。有时它看起来很平滑,然后它似乎变得更糟。非常奇怪。想知道是否有人知道如何优化?
我正在预加载图片。不确定为什么会闪烁。有什么想法吗?
$(document).ready(function () {
    switchImage();
});

$(window).scroll(function () {
    switchImage();
});

var pics = []; // CREATE PICS ARRAY

//PRELOAD FUNCTION TO SET UP PICS ARRAY IN MEMORY USING IMAGE OBJECT
function preload() {
    for (i = 0; i < arguments.length; i++) {
        pics[i] = new Image();
        pics[i].src = arguments[i];
        //alert("preload " + arguments[i]);
    }
}
preload(
    'bgImage/100.jpg',
    'bgImage/101.jpg',
    'bgImage/102.jpg',
    'bgImage/103.jpg',
    'bgImage/104.jpg',
    'bgImage/105.jpg',
    'bgImage/106.jpg',
    'bgImage/107.jpg',
    'bgImage/108.jpg',
    'bgImage/109.jpg',
    'bgImage/110.jpg',
    'bgImage/111.jpg',
    'bgImage/112.jpg',
    'bgImage/113.jpg'
);

function switchImage() {
    var s = $(window).scrollTop()/10;
    var index = Math.floor(s / 5);

    $('body').css('background-image', 'url(' + pics[index].src + ')');
}

Shawn,我在Chrome(Windows)上看起来很流畅。你在哪里看到闪烁?除了你的问题之外,这些图像本身几乎总共有1MG,这是一个相当大的页面负载,只是为了获得背景图像。 - Jonah
无论你做什么,它们都会非常大。我建议你为了一个酷炫的效果付出了相当高的代价,所以你可能不想使用它。尽管暂且忽略这一点,我在IE中尝试了你的小提琴,并能看到闪烁效果。我仍在寻找解决方案。 - Jonah
我理解你的观点,但是问题在于我必须使用这个特效和代码。我的另一个解决方案是使用HTML5视频背景,并找出如何正向播放和反向播放,但这似乎是更好的解决方案。 - Shawn Altman
我认为视频版本的压缩效果和加载速度比多张图片要好,而且还可以完全避免帧加载问题。 - cirrus
你是否需要特别读取图像?我的意思是,它们以某种超出您控制范围的特定格式交付(在发布时),或者自己绘制该内容(和帧更新)也是一种选择吗? - user1467267
显示剩余3条评论
4个回答

3
为什么不使用一张图片(精灵图)并通过background-position移动它,而不是替换图片?(在大小方面 - 在您的情况下,您可以设置基于百分比的background-size - 高度将是1400%
因为您已经预加载了所有图片 - 它不会影响页面加载时间 - 并且还可能节省时间,因为通过正确的压缩,14张1的图片的重量可能小于1张14的图片。

2

谷歌浏览器是可以的。在IE中我能看到闪烁。解决方案在本贴底部。

我认为一个视频版本会比所有图像都要快速压缩和加载,但正如@Allendar所建议的绘制,它将是最传输效率高的。我建议使用画布或SVG。

另一种仅使用图像的方法是将各个组件作为图像或图标字体放置在显示器上,并在脚本中打开或关闭它们的位置。但那是一个非常复杂的解决方案。

我认为你今天解决问题的最简单和最快速的方法是调整你已有的解决方案。正如其他人所建议的那样,多图像方法不会非常高效,如果你要采用这种方法,请确保在Web服务器上设置缓存头以永久缓存;

Cache-Control: public;
Expires: Mon, 31 Dec 2035 12:00:00 GMT

好的,那么闪烁问题只是IE渲染引擎中的一个错误/低效率问题,因此这里提供一种使用不同方法的解决方法。

基本上,将一个绝对定位的DIV覆盖在body上,而不是使用body本身。实际上,在这种情况下,我们使用多个DIV,每个图像都有一个并且它们相互重叠。您也可以在脚本中创建这些节点。

其次,再添加另一个DIV用于您的内容,并将其覆盖在body上。

<body>
    <div id="b100" class="background" style="background-image:url('http://ingodwetrustthemovie.com/bgImage/100.jpg')"></div>
    <!-- rest omitted -->
    <div id="b113" class="background" style="background-image:url('http://ingodwetrustthemovie.com/bgImage/113.jpg')"></div>
    <div id="content">
        <div id="test">test div</div>
        here is some text
    </div>
</body>

它们只是一个接一个地显示,然后隐藏其余的;

function switchImage() {
    var s = $(window).scrollTop()/10;
    var index = Math.floor(s / 5);

    $('.background').hide();
    $('.background').eq(index).show();
}

我怀疑调整CSS显示选项会比更改src属性更不容易出现闪烁,而且它似乎起到了作用。

这是Fiddle

当然,您可能仍需要优化代码,以便首先显示第一个加载的图像而不是纯背景,但我认为这演示了修复原理。

您还可以考虑制作非常大的CSS Sprite,将图像捆绑成一个巨大的条带,然后使用background-position。那可能会在body标签本身上起作用。当然,这意味着在显示任何图像之前要下载一个巨大的文件,但有两个优点;

  1. 一张图片(尤其是具有相似性的图片)将比每个单独的图片孤立地压缩得更好。
  2. 使用相同的缓存指令,一旦您第一次获取图像,那只有一个HTTP / GET / 302周期,而不是13个,因此您的页面可能仍然加载得更快。

SVG

SVG元素的工作方式类似于DOM。如果您可以将内容作为SVG传递,则可以进入图形,定位元素,为其分配ID等,并像处理任何其他DOM元素一样对其进行操作。

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
   <ellipse id="e1" cy="420" cx="200" rx="420" ry="30" style="fill:3f5566" />
   <ellipse id="e2" cy="420" cx="170" rx="390" ry="20" style="fill:4f5566" />
   <ellipse id="e3" cy="420" cx="145" rx="370" ry="15" style="fill:5f5566" />
   <ellipse id="e4" cy="420" cx="100" rx="370" ry="20" style="fill:6f5566" />
   <ellipse id="e5" cy="420" cx="45"  rx="300" ry="15" style="fill:8f5566" />
</svg>

这是另一个根据滚动条隐藏/显示SVG元素的示例
最好假设设计师已将图形生成为“层”,请尝试让设计师提供一份SVG,其中将这些层转换为。例如,Adobe Illustrator可以实现
然后,您可以根据需要轻松关闭层/组以创建动画效果。

谢谢Cirrus。我一定会查看这个的。我尝试使用div而不是背景,但在项目上时间不够用。网站有三周的交期,也忙于其他项目。我已经看到更新htaccess并正确设置标头的修复方法。我同意雪碧图可能是最简单的解决方案之一。设计公司给了我们一个视频,但图片似乎更容易实现。感谢提醒...很快就会深入研究。 - Shawn Altman
我刚刚看到你有200张图片。在这种情况下,我倾向于选择视频而不是静态图像。然而,绘制矢量图仍然是最好的选择。你能请设计师给你提供SVG格式的图像吗?一开始就使用它来替换图像,但如果有更多的时间,你可以优化它,只显示你需要的组件。 - cirrus
我可以尝试获取SVG并在周一进行询问。我之前没有使用过SVG,所以现在要开始研究。他们提供了一个mp4文件,但我不知道如何根据滚动事件加载和播放视频的正反向,这就是为什么我选择了图片。我们必须匆忙上线网站。既然它已经上线并运行,让它顺畅运行更像是个人兴趣。非常感谢您的支持和反馈。我将根据自己的研究在周一早上首先深入代码。 - Shawn Altman
请查看带有SVG演示的更新答案。您可以让它们提供多个SVG框架,并像使用图像一样使用它们,但我认为最好的方法是手动钻取一个SVG并选择每个框架所需的元素,然后有选择地打开或关闭它们。例如,您可以添加class="frame1",class="frame2"。 - cirrus
谢谢!顺便说一下,在你翻译的时候,我在文章末尾添加了关于“层”的注释。 - cirrus
显示剩余3条评论

2
如果不需要图片,您可以创建一个使用canvas绘制并不断更新背景的系统。这是一个开始; JSfiddle HTML
<img id="template" src="http://ingodwetrustthemovie.com/bgImage/100.jpg" />
<canvas id="absBg" width="1000px" height="560px"></canvas>

CSS

body {
    margin: 0px;
}

#template {
    position: absolute;
    width: 1000px;
    height: 563px;
}

#absBg {
    position: absolute;
    /*background: rgba(0, 0, 0, 0.50);*/
    /*height: 100%;*/
}

JavaScript/jQuery

'use strict';

function drawBlock(ctx, color, line_width, start, lines) {
    ctx.lineWidth = line_width;
    ctx.strokeStyle = color;

    ctx.beginPath();

    ctx.moveTo(start[0], start[1]);

    for (var i = 0; i < lines.length; i++) {
        ctx.lineTo(lines[i][0], lines[i][1]);
    }

    ctx.closePath();
    ctx.stroke();
}

function drawBg() {
    var absBg = document.getElementById('absBg');
    var ctx = absBg.getContext('2d');

    var demo_red = 'red';
    var grey = '#28282';

    var color = demo_red;

    drawBlock(ctx, color, 1, [185, 87], [[205, 75], [226, 98], [207, 110]]);
    drawBlock(ctx, color, 1, [235, 60], [[253, 50], [272, 71], [254, 81]]);
}

$(document).ready(function () {
    drawBg();

    // Scroll trigger
    $(window).scroll(function () {

    });
});

该演示使用实际背景图像作为背景模型,您可以在画布上绘制。这样,您可以模仿原始图像的外观。
您可以尝试管理某种“差异数组”,其中存储哪些块在哪些位置上不同。这样,您就可以触发带有特定参数的函数来根据其更改绘图。
我希望您不需要图像本身。使用画布绘图比加载大量图像要快得多 :)

谢谢Allendar。我同意绘图会更好。设计公司提供了所有的图纸和视频版本。由于时间不够,我选择了数组并快速地将其组合在一起。我也会尝试这个解决方案,看看它是否可行。非常感谢您的反馈。我会整理好并告诉您结果。 - Shawn Altman
Allendar,你有关于绘制画布的教程链接吗?老实说我没有任何经验,但我可以深入研究一下,看看最终能做到什么程度。真的非常感谢。 - Shawn Altman
@ShawnAltman 很高兴听到这是一个可能的解决方案。就我个人而言,我喜欢将这些网站作为参考; http://diveintohtml5.info/canvas.html,https://developer.mozilla.org/en-US/docs/HTML/Canvas/Tutorial?redirectlocale=en-US&redirectslug=Canvas_tutorial,http://www.html5rocks.com/en/tutorials/canvas/integrating/。互联网上有很多令人惊叹的Canvas教程和示例; https://duckduckgo.com/?q=html5+canvas。祝你好运! - user1467267
顺便再给你一个小技巧;你可以在彼此之上画两个绝对 <canvas> 对象。把那些经常出现的形状画在最底部的<canvas> 上。这样,你只需在前端<canvas>重绘 变化的形状,使访问者浏览器中的“渲染负荷”更轻,还可以将原始背景的大量形状绘制列表与较小的变化形状列表分开。 - user1467267

1
这里有一个在Firefox 30.0、Chrome 35.0、Opera 22.0、IE 11.0上有效的解决方案(2014年7月11日): 步骤1:在.htaccess文件中添加以下行:
# cache for images
<FilesMatch "\.(png)$">
Header set Cache-Control "max-age=10000, public"
</FilesMatch>

步骤2:添加图像预加载,例如:

var pics = []; // CREATE PICS ARRAY

$(document).ready(function(){
    ...
    preload(
        '/public/images/stars.red.1.star.png',
        '/public/images/stars.red.2.star.png',
        '/public/images/stars.red.3.star.png',
        '/public/images/stars.red.4.star.png',
        '/public/images/stars.red.5.star.png',
        '/public/images/stars.empty.png'
    );
    ...
    $('.rating').on('mousemove', function(event){
        var x = event.pageX - this.offsetLeft;
        var id = getIdByCoord(x); //
        if ($(this).data('current-image') != id) {
            $(this).css('background-image', 'url(' + pics[id].src + ')');
            $(this).data('current-image', id);
        }
    })
    ...
})

...

// PRELOAD FUNCTION TO SET UP PICS ARRAY IN MEMORY USING IMAGE OBJECT
function preload() {
    for (i = 0; i < arguments.length; i++) {
        pics[i] = new Image();
        pics[i].src = arguments[i];
        // alert("preload " + arguments[i]);
    }
}

P.S.感谢Shawn Altman


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接