问题
I created a jsfiddle for my current code. http://jsfiddle.net/gL5sB/38/
I am trying to change the body background css on scroll event. When the background changes it appears to flicker when the css is updated and new image is loaded. At times it seems smooth and then it seems to get worse. Very strange. Curious if anyone knows how to optimize?
I am preloading the images. Not sure why the flicker. Any ideas?
$(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 + ')');
}
回答1:
Chrome is OK. I can see the flicker in IE. A Solution for that is at the bottom of this post.
I suspect A video version would compress and load faster than all the images, but as suggested by @Allendar drawing it would be the most transmission efficient. I would suggest canvas or SVG.
Another way using images along would be to have individual components as either images or icon fonts placed on the display with absolute positioning and then just turning them on or off in script. But that's a very complex solution.
I think the simplest and fastest method for you to solve the issue today though would be to just tweak the solution you have. As other people have suggested, the multiple images approach won't be super efficient and if you're going to take that route at least make sure you set the caching headers on your web server to cache indefinitely;
Cache-Control: public;
Expires: Mon, 31 Dec 2035 12:00:00 GMT
OK, so the flicker problem is just a bug/inefficiency in the rendering engine in IE, so here's a workaround that uses a different approach.
Basically stretch an absolutely positioned DIV
over the body
, instead of using the body
itself. In fact, in this case we use multiple DIV
s, one for each image and overlay them on top of one another. You could create these nodes in script as well.
Secondly, add another DIV
for your content, and overlay that over the body
as well;
<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>
The simply show one at a time and hide the rest;
function switchImage() {
var s = $(window).scrollTop()/10;
var index = Math.floor(s / 5);
$('.background').hide();
$('.background').eq(index).show();
}
I had a suspicion that twiddling the css display option would be less flickery than changing the src attribute and it seems to do the trick.
Here's the Fiddle
Of course you may still need to optimise the code to allow for the first loaded image to be shown first instead of the plain background, but I think this demonstrates the principle of a fix.
You could also consider making a very large CSS Sprite, by bundling the images into one huge strip and then using background-position
. That would probably work on the body
tag itself then. Of course this would mean downloading a huge file before you can display any images at all, but there are two advantages;
- One image (especially with such similarity) will compress way better than each individual one in isolation.
- Using the same caching directives, that's only one HTTP/GET/302 cycle instead of 13 once you've fetched the image the first time, so your page may load faster still.
SVG
SVG elements work much like the DOM. If you can get your content delivered as an SVG you can drill into the graphic, locate the elements, give them IDs etc, and manipulate them much like you would any other DOM element;
<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>
Here's another fiddle that hides/unhides SVG elements based on a scroll.
Ideally, assuming they've generated that graphic in 'layers', try and have your designers deliver you an SVG where the layers are converted into groups. Adobe Illustrator can do that for instance.
Then you can easily turn off the layers/groups as necessary to create the animated effect.
回答2:
Why don't you use one image (sprite image) and just move it with background-position
instead of replacing the image?
(about the size - you can set percentage based background-size
in your case - the height will be 1400%
Because your'e preloading all the images anyway - it won't cost you in page loading time - and it might also save some time because with the right compression 1 image of 14 will weight less then 14 images of 1
回答3:
If the images aren't really needed, you can create some system for yourself where you draw, and keep updating, the background using canvas. This would be start;
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 () {
});
});
The demo uses the actual background image you have as a background-model, where you can draw over with the canvas. This way you can mimick the look of the original image you have.
You can try to manage some kind of "difference-array" where you store which blocks are different on which locations. This way you can trigger the function on scroll
with certain parameters to let it change the drawing based on that.
I hope for you that you don't "need" the images per se. Drawing with canvas is so much faster than loading tons of images :)
回答4:
Here is a solution that works (2014.7.11) at firefox 30.0, chrome 35.0, opera 22.0, ie 11.0:
STEP 1: add these lines at .htaccess:
# cache for images
<FilesMatch "\.(png)$">
Header set Cache-Control "max-age=10000, public"
</FilesMatch>
- detailed description of this problem and how to fix it: https://code.google.com/p/chromium/issues/detail?id=102706
STEP 2: add images preloading, for example:
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. thanks Shawn Altman
来源:https://stackoverflow.com/questions/16181424/flicker-on-jquery-background-change