I'm trying to fade in-out my image for my photo gallery switching. All it's done in JavaScript which simply changes the opacity CSS value of the image element. This is really laggy (slow) on some computers - for example my laptop which isn't extremely powerful, but it's brand new (Asus Eeepc).
I was wondering if there's anyway I can fix this performance issue. I've seen demos of Canvas animation and HTML5 applied to images and they're really smooth on my laptop. I wonder if I can apply the same thing to my image fading feature.
Any ideas? How would I do this?
I quickly threw together an example of an image fading away using the canvas tag as requested: http://jsfiddle.net/6wmrd/12/ (Only tested in Chrome and Firefox)
I´m not sure if there is any performance gain though, but here is at least a very simple example of how it can be done. It should also be noted that this was done in 5 min so the code can be improved and optimized.
Otherwise, from my experience, if you have a solid background behind the image, I have found that it is sometimes smoother to fade an element over the image with the same color as the background.
Other ways you can improve performance could be to reduce FPS. If I´m not mistaken MooTools has 50 FPS as standard. However, reducing the FPS might influence the perceived performance.
Here is code that works for all browsers:
add to head :
/* ****************************
* updated for all browsers by
* Evan Neumann of Orbiting Eden on
* October 6, 2011.
* www.orbitingeden.com - evan#orbitingeden.com
* original version only worked for IE
*****************************/
<!-- Begin
/* *****************************
* * editable by user
* ***************************/
var slideShowSpeed = 5000; // Set slideShowSpeed (milliseconds) shared by IE and other borwsers
var crossFadeDuration = 5; // Duration of crossfade (1/10 second) shared by IE and other borwsers
var crossFadeIncrement = .05; //crossfade step amount (1 is opaque) for non-IE browsers
// Specify the image files
var Pic = new Array(); // do not change this line
// to add more images, just continue the pattern, adding to the array below
Pic[0] = 'images/dare2wear-427ED-e.jpg';
Pic[1] = 'images/PBW_3238EDSM-e.jpg';
Pic[2] = 'images/dare2wear-441_2ED-e.jpg';
/* ********************************
* do not change anything below this line
**********************************/
var f = 0; //index of the foreground picture
var b = 1; //index of the background picture
var p = Pic.length; //number of pictures loaded and in queue - this may increase as time goes on depending on number and size of pictures and network speed
//load the picture url's into an image object array
//used to control download of images and for IE shortcut
var preLoad = new Array();
for (i = 0; i < p; i++) {
preLoad[i] = new Image();
preLoad[i].src = Pic[i];
}
function runSlideShow() {//this is trigerred by <body onload="runSlideShow()" >
//if IE, use alternate method
if (document.all) {
document.images.SlideShow.style.filter="blendTrans(duration=2)";
document.images.SlideShow.style.filter="blendTrans(duration=crossFadeDuration)";
document.images.SlideShow.filters.blendTrans.Apply();
}
//increment the foreground image source
document.images.SlideShow.src = preLoad[f].src;
//if IE, use the shortcut
if(document.all) {
document.images.SlideShow.filters.blendTrans.Play();
}
//all other browser use opacity cycling
else {
var imageContainer = document.getElementById('imageContainer');
var image = document.images.SlideShow;
//convert an integer to a textual number for stylesheets
imageContainer.style.background = "url('"+Pic[b]+"')";
//set opacity to fully opaque (not transparent)
image.style.opacity = 1;
//run fade out function to expose background image
fadeOut();
}
//increment picture index
f++;
//if you have reached the last picture, start agin at 0
if (f > (p - 1)) f = 0;
//set the background image index (b) to one advanced from foreground image
b = f+1;
//if b is greater than the number of pictures, reset to zero
if(b >= p) {b = 0;}
//recursively call this function again ad infinitum
setTimeout('runSlideShow()', slideShowSpeed);
}
function fadeOut(){
//convert to element
var el = document.images.SlideShow;
//decrement opacity by 1/20th or 5%
el.style.opacity = el.style.opacity - crossFadeIncrement;
//if we have gone below 5%, escape out to the next picture
if(el.style.opacity <= crossFadeIncrement) {
return;
}
//wait 50 milliseconds then continue on to decrement another 5%
setTimeout('fadeOut()', crossFadeDuration*10);
}
// End -->
and add two elements to the body. The first is a container background element. I have used a div, but td, body and others should work too. The second is the foreground image. Lastly, within the <body> tag, add the onload function call
<body onLoad="runSlideShow()">
<!-- this is the main image background -->
<div id="imageContainer">
<!-- this is the main image foreground -->
<img id="SlideShow" name='SlideShow' width="324" height="486">
</div>
Luca one way to make it faster is to use hardware acceleration and webkit transforms. The problem is that different browser support this to different degrees. See
http://mir.aculo.us/2010/06/04/making-an-ipad-html5-app-making-it-really-fast/
hopefully in the not-to-distant futures support for hardware acceleration in the browser will be better.
Have a look at the front page of this site It's 5 images that fade in and out in rotation, pure css2, html4, javascript, and is cross-browser (as far as I am aware). The javascript is a bit hackneyed - written some time ago :) but it seems to be smooth enough. See how it is on your machine. If it lags, you could try with a slower update, say 150 or 200ms instead of a 100.
Related
In Javascript I load some elements via AJAX from a server, and for each data-element I create a <div> that contains another div and an <img>. The inner div contains between 2 and 5 lines of text. I arrange three of this items side by side in a row, then the next three in the next row and so on.
But since the texts have different lengths, I want to do some fine-adjustment of the elements within each row. I want the top-edges of all three images to be on the same height. To do this, I do this:
Insert the three <div><div>text</div><img></div>-blocks into their container.
Get the heights of the three <div>text</div>-elements,
calculate their maximum, and then
set their padding-top properties in a way that gives them all the same height.
In Safari and Chrome this works perfectly fine when I turn on the console and set breakpoints to watch what is going on in detail.
But when I turn off breakpoints in this two browsers, the text-diffs don't get their correct padding-values.
I am pretty sure, that - at breakpoints off - the browser is still working on inserting the three dom-elements and rendering the pictures, when javascript tries to measure the heights of the text-divs. So it measures them at a time when they don't have their final values. The script reads wrong values and so it puts wrong values to the padding-top-property.
This does not happen in all browsers:
When running normally (without console and breakpoints) it works always fine within:
Firefox
Opera
Internet Explorer (running in a virtual Machine on my iMac)
But I have those problems in:
Safari
Chrome
What can I do to ensure, that the measurement of an elements height happens when the rendering-machin has finished its manipulation?
EDIT:
I found out another very important detail:
The problem occurs, because of the height of the text-div above the image. Sometimes the text fits very tightly into two rows. One small letter more in any of the rows and it would be three rows.
In this case my script, that runs immediately after the div was created, measures a height of three lines (60 pixel), and everything would be absolutely correct, if this div did really contain 3 lines of text. My script manipulates the elements in a manner that would be perfect if this div really was 3 lines high.
But obviously, some milliseconds after my script was running, the browser (Safari and Chrome) performs an improvement of font-rendering. And then suddenly the text fits into 2 lines, which makes the text-div only 40 pixels high. And so the image moves up 20 pixels, and this destroys my just processed result (all images was at the same position)
So,does this give you any idea on how to solve the problem? Is there a way to let that part of my script run after all rendering-polishing has finished? Is there an event like onFinishingRenderingImprovementsDone?
(written on May the 4th be with you = Star Wars day)
How about having your script run with a short delay - setTimeout(function() {[your code here]}, 100) (or however long it needs...) - and see if you can simply avoid the problem altogether? Less than a second probably, and for an async action, adding a very short wait would likely not be noticeable.
You can monitor the height of the elements, and when an element height changes, you can recalculate the padding.
Example:
$.fn.changeHeight = function(callback){
return this.each(function(i, e){
var el = $(e), height = el.height();
window.setInterval(function(){
var h = el.height();
if (h != height) {
height = h;
callback.call(e);
}
}, 100);
});
};
function rndText() {
var txt = '';
for (var i = Math.floor(Math.random() * 20); i >= 0; i--) {
txt += 'asdf ';
}
return txt;
}
for (var i = 0; i < 10; i++) {
var d = $('<div>').append(
$('<div>').prop('contenteditable', true).text(rndText()).changeHeight(resize)
).appendTo('body');
}
resize();
function resize() {
console.log('resize');
var height = 0;
$('body > div').each(function(i, e) {
var d = $(e);
var h = d.find('div').height();
if (h > height) height = h;
});
$('body > div').each(function(i, e) {
var d = $(e);
d.css('padding-top', height - d.find('div').height());
});
}
Demo: http://jsfiddle.net/Guffa/Lunxbr6p/2/
The boxes in the demo are editable, so you can change the text and see how the boxes resize.
I make an animation using sequence images. i run the images using setinterval function animation going fine but i dont know why it paused some time. i posted fiddle here look this fiddle you can able to notice this pause
Unwanted Pause Happen Here
myAnim = setInterval(function(){
$("#myImageHolder8").attr('src', nextImage5[u]);
u++;
if(u==nextImage5.length)
{
u=0;
}
}, 50);
Pls friends Help me.
You need to preload the image. Setting the image source inside the loop will definately cause a hick-up at one point as loading and decoding the image(s) may very well exceed 50 ms (cached or not). This will also cause the problem to appear randomly (and faster computers may not notice while slower one or slower connections may cause this more frequent).
Preload the images and the simply insert the loaded image into the container (a parent element) instead.
You can preload the images either by hiding them in DOM and use window.onload to start, or do it in JavaScript using an array and load counter.
An example of an loader:
Live demo
var images = [],
count = nextImage5.length,
len = count,
i = 0;
for(; i < len; i++) {
var img = new Image;
images.push(img);
img.onload = loader;
img.src = nextImage5[i];
}
function loader() {
count--;
if (count === 0) {
... start animation here...
}
}
and then in the animation loop do something like (sorry, my jQuery escapes me but you see the point):
$('#myImageHolder8').html('');
$('#myImageHolder8').append(images[u]);
Before setting the animation, make sure you preload your images first. So that the images are ready and loaded to display smoothly. You can use this preloader. And I would like to suggest that instead of changing the src of an <img> it is better to draw the image to a <canvas> then create a <canvas> for your next image.
Hello below is my JS code for a changing background image every 30 seconds. could somebody please advise me if there is a way to alter the code to fit the image to the browser window (100%) and also if there is any JS that I could use within this code to make the images fade in and out seemlessly and elegantly.
<script>
bgArr = ['images/bg1.jpg', 'images/bg2.jpg', 'images/bg3.jpg'];
bgCur = 0;
backgroundSwitch = function()
{
if (bgCur == bgArr.length) bgCur = 0;
document.body.style.backgroundImage = 'url('+ bgArr[bgCur++]+ ')';
}
window.setInterval(backgroundSwitch, 30000); // Switch every 30 seconds.
</script>
Thank you everybody
CSS3 introduced background-size: cover. This automatically sizes the background image to cover the entire parent element, either scaling it horizontally or vertically to fill.
See the entry on MDN.
background-size is supported by all major browsers.
Fitting to browser window should be simple enough. Depending on your browser requirements, you might be able to get away with background-size: cover in your CSS (see Albert's answer).
As for animating, you could either use CSS transitions and trigger them via JavaScript (generally by modifying the class) or use JavaScript to do it.
If using JavaScript, be sure to set a duration and then lerp (or some other method) between the states.
This is the general idea...
var img = document.getElementById("fade");
var fadeLength = 5500;
var opacity = 0;
var startTime = Date.now();
requestAnimationFrame(function me() {
// It's faded in, stop animating!
if (opacity >= 1) {
return;
}
opacity = (Date.now() - startTime) / fadeLength;
img.textContent = opacity;
img.style.opacity = opacity;
requestAnimationFrame(me);
});
jsFiddle.
I want to change the background color of in-viewport elements (using overflow: scroll)
So here was my first attempt:
http://jsfiddle.net/2YeZG/
As you see, there is a brief flicker of the previous color before the new color is painted. Others have had similar problems.
Following the HTML5 rocks instructions, I tried to introduce requestAnimationFrame to fix this problem to no avail:
http://jsfiddle.net/RETbF/
What am I doing wrong here?
Here is a simpler example showing the same problem: http://jsfiddle.net/HJ9ng/
Filed bug with Chromium here: http://code.google.com/p/chromium/issues/detail?id=151880
if it is only the background color, well why don't you just change the parent background color to red and once it scroll just change it to pink?
I change your CSS to that
#dad
{
overflow-y: scroll;
overflow-x: hidden;
width: 100px;
height: 600px;
background-color:red;
}
I remove some of you Jquery and change it to this
dad.bind('scroll', function() {
dad.css('background-color', 'pink');
});
And I remove this line
iChild.css('backgroundColor', 'red');
But is the Red color it is important that won't work for sure http://jsfiddle.net/2YeZG/5/
I like Manuel's Solution.
But even though I don't get what you're exactly trying to do, I want to point out a few things.
In your fiddle code, I saw that you included Paul Irish's Shim for requestAnimationFrame.
But you never use it.
(It's basically a reliable setTimeOut, nothing else) it's from frame based animations.)
So since you just want to change some CSS properties, I don't see why you would need it. Even if you want transitions, you should rely on CSS transitions.
Other than that your code could look something like
dad.bind('scroll', function() {
dad.css('background-color', 'pink');
eachElemNameHere.css('background-color','randomColor');
});
Also you should ideally not use something like that if you can help it. You should just add and remove class names and add all these properties in your CSS. Makes it work faster.
Also, again I don't quite get it, but you could use the jQuery function to find out each elements' position from the top to have better control.
Your problem seems to be that you only change the background color of the elements which have already been scrolled into view. Your code expects that the browser waits for your code to handle the scroll event before the browser redraws its view. This is most probably not a guarantee given by the HTML spec. That's why it flickers.
What you should do instead is to change the elements which are going to be scrolled into view. This is related to off screen rendering or double buffering as it is called in computer games programming. You build your scene off screen and copy the finished scene to the visible frame buffer.
I modified your first JSFiddle to include a multiplier for the height of the scroll area: http://jsfiddle.net/2YeZG/13/.
dad.bind('scroll', function() {
// new: query multiplier from input field (for demonstration only) and print message
var multiplier = +($("#multiplier")[0].value);
$("#message")[0].innerHTML=(multiplier*100)-100 + "% of screen rendering";
// your original code
var newScrollY = newScrollY = dad.scrollTop();
var isForward = newScrollY > oldScrollY;
var minVal = bSearch(bots, newScrollY, true);
// new: expand covered height by the given multiplier
// multiplier = 1 is similar to your code
// multiplier = 2 would be complete off screen rendering
var newScrollYHt = newScrollY + multiplier * dadHeight;
// your original code (continued)
var maxVal;
for (maxVal = minVal; maxVal < botsLen; maxVal++) {
var nxtTopSide = tops[maxVal];
if (nxtTopSide >= newScrollYHt) {
break;
}
}
maxVal = Math.min(maxVal, botsLen);
$(dadKids.slice(minVal, maxVal)).css('background', 'pink');
});
Your code had a multiplier of 1, meaning that you update the elements which are currently visible (100% of scroll area height). If you set the multiplier to 2, you get complete off screen updates for all your elements. The browser updates enough elements to the new background color so that even a 100% scroll would show updated elements. Since the browser seldom scrolls 100% of the area in one step (depends of the operating system and the scroll method!), it may be sufficient to reduce the multiplier to e.g. 1.5 (meaning 50% off screen rendering). On my machine (Google Chrome, Mac OS X with touch pad) I cannot produce any flicker if the multiplier is 1.7 or above.
BTW: If you do something more complicated than just changing the background color, you should not do it again and again. Instead you should check whether the element has already been updated and perform the change only afterwards.
I have a div which contains a PNG image, like this:
<div id="pop" class="pop_komm">
<img src="Graphics/komm.png">
</div>
I have a js code which is triggered on the "OnChange" event of a drop-list:
<select onchange="fadeIn('pop');" name="list" etc></select>
Here is the js code:
function setOpacity(eID, opacityLevel) {
var eStyle = document.getElementById(eID).style;
eStyle.opacity = opacityLevel / 100;
eStyle.filter = 'alpha(opacity='+opacityLevel+')';
}
function getElm(eID) {
return document.getElementById("pop");
}
function show(eID) {
getElm(eID).style.display='block';
}
function hide(eID) {
getElm(eID).style.display='none';
}
function fadeIn(eID) {
setOpacity(eID, 0); show(eID); var timer = 0;
for (var i=1; i<=100; i++) {
setTimeout("setOpacity('"+eID+"',"+i+")", timer * 2);
timer++;
}
setTimeout("fadeOut('"+eID+"')", 5000);
}
function fadeOut(eID) {
var timer = 0;
for (var i=100; i>=1; i--) {
setTimeout("setOpacity('"+eID+"',"+i+")", timer * 2);
timer++;
}
setTimeout("hide('"+eID+"')", 310);
}
The problem is that the opacity makes the PNG corners rough and black, and the transparency of the PNG is not working good at all.
I have tried using a pngfix, which does help IF the PNG has no "fade" effect. But as soon as I apply this fade effect I get the same problem.
Somebody must have solved this before, so please give me advice on how to solve it.
I googled and found this, but I don't really know what he means.
If you need more input let me know...
Btw, this works in all major browsers but IE6, 7 and 8.
Thanks
Yeah, setting alpha on a transparent PNG replaces the alpha channel completely with the opacity value, so the transparent parts come back into view.
As the page you linked says, you can put the alpha filter on an element that contains the PNG, which typically looks better, but that container element has to hasLayout. Add a height or zoom or whatever to the container to trigger IE's crazy hasLayout nonsense.
It's still not quite right, though, as you end up with each pixel having the minimum opacity of that pixel's alpha value and the set opacity, instead of multiplying the opacities. So the semi-transparent parts of the image end up with the wrong relative transparency. It can still look quite good as a fade-in effect, though.
Full multiplicative opacity can't be done in IE before IE9.