I like to think I'm not a dummy, but I can't get my jQuery horizontal slideshow to animate smoothly especially in FireFox (on a Mac). Anyone have advice?
Animation is being done like so:
$('#lookbook').stop().animate({left: -((lookbook-1)*825)+'px'}, { duration: 800, complete: cap_fade(1)});
Example link:
http://mayfourteenth.com/w/lookbook?preview=1
I've tested in Firefox, Chrome(dev) and Safari on windows and the animation stutters in all browsers(but more in FF though).
To increase JavaScript performance you could get rid of all the getElementById or $("div#mydividentyfier") calls.
If you store them in variables instead they will be cached.
Example:
It could increase performance quite a bit to do this:
var lookbook = $('#lookbook');
var look_caption = $('#look_caption');
if (lookbook.length) {
lookbook.width(lookbook).width()*$('#lookbook img').length)
if (look_caption) {
look_caption.html(lookcaps[0]);
look_caption.fadeIn();
}
Instead of:
if ($('#lookbook').length) {
$('#lookbook').width($('#lookbook').width()*$('#lookbook img').length)
if ($('#look_caption')) {
$('#look_caption').html(lookcaps[0]);
$('#look_caption').fadeIn();
}
I would also recommend using data URIs for the images as it reduces the amount of httpRequests you have to make to get the page loaded.
The animation looks smooth for me in Chrome. However, I believe there are several things you can do to improve smoothness:
First, it's fine to preload all of the images in advance as you do here (at the top). However, displaying them all at once, as in the "Example link", hurts performance, as they are all animating at once:
<div id="lookbook">
<div><img src="/q_images/lib/lookbook/1.jpg"></div>
<div><img src="/q_images/lib/lookbook/2.jpg"></div>
...
<div><img src="/q_images/lib/lookbook/15.jpg"></div>
</div>
Instead of doing this, you can simply cue up the next and previous image on either side of the current image, but then don't have the rest of the images in the page until they're needed. (Preloading them is still fine though.)
Other things which can improve performance slightly are things like the following:
Use smaller (by pixels and/or file size) images.
Make minor code optimizations by computing things in advance.
Use a stand-alone animation library instead of jQuery.
You may also want to use this
.animate({left:'-=825'}); //next
//and
.animate({left:'+=825'}); //previous
Instead of
.animate({left: -((lookbook-1)*825)+'px'});
Related
I have a test page to better explain my problem. I have several items on a list (they're images on the test page); when I click on one of them, a corresponding slideshow, using flexslider, sldes down.
The problem is that, on page load, the slideshow shows all slides at once, at a much smaller size than intended. But then, if I switch the focus from the window (i.e. switch between browser tabs or move to another program and come back), the slideshow is now working and the slides are the proper size. This happens in mobile devices too.
When I check with firebug, there's an element.style rule applying to ul.slides:
transform: translate3d(-89px, 0px, 0px);
Which hides one of the slides. Additionally, there's another rule for the list items inside ul.slides that gives them their initial width, which is not even the same for all sliders so I don't understand where it is coming from.
Can someone take a look and suggest a fix? I've tried overriding the element.style rule but so far unsuccessfully.
I think I've figured it out, in principal at least...
.flexslider{display:none;} seems throw off the re-size function of Flexslider.
You could just remove it, but that makes for some ugly loading.
To avoid said ugly loading I put together a quick, work-around- jsFiddle
$(document).ready(function(){
$(".flexslider").css('display','block').slideUp();
});
There's a still a quick glitch while loading, but hopefully it will at least steer you in the right direction.
Another method I played with a bit was to try and force the re-size function like so-
$(".client").click(function () {
$('.flexslider').resize(); // Problematic but promising
var project = this.id;
var project_id = '#' + project + '-project';
var elem = $(".flexslider:visible").length ? $(".flexslider:visible"): $(".flexslider:first");
elem.slideUp('slow', function () {
$(project_id).slideDown('slow');
});
});
This sort of solved the mini-picture issue, but was spotty at best.
I am trying to achieve a "crt-like" scrolling glitch effect using Javascript and CSS. I have come up with the following code which clones the content and applies clip to make it look like it scrolls while adding random horizontal offset.
function scanglitch() {
var e = $('#wrapper').clone().appendTo('#glitchcontainer');
var i = 0;
e.css({"clip": "rect(" + i + "px,3830px," + (i + 15) + "px,0px)"});
e.css("z-index",200);
var interval = setInterval(function () {
e.css({"clip": "rect(" + i + "px,3830px," + (i + 15) + "px,0px)"});
e.css({"left": Math.round(Math.random() * 10) + "px"});
i+=4;
if (i > window.innerHeight) {
e.remove();
window.clearInterval(interval);
}
}, 40);
}
Fiddle (Click on the text to see the effect)
I am actually quite pleased with the effect, but the implementation is obviously a hack. As a result the performance is unacceptably low (chrome cpu usage spikes from 5% to 50% when the effect is triggered).
Could someone help me figure out a way to achieve this effect in a more elegant and less performance-heavy way?
UPDATE:
I have implemented your suggestions: Using translate instead of left, scrolling with translate instead of a js loop, calculations outside of the css tag and using requestAnimationFrame(). The code is nicer and more predictable now, but the animations are still very taxing.
New fiddle
You can try using requestAnimationFrame (it is available in almost all browsers). Seems to make a big difference in Chrome.
JSFiddle
EDIT
Here's a transition-only version, and while it doesn't even implement the jitter, it's useful for comparison. Surprisingly(?) it shows about the same, if not more, CPU usage. (You can check the Profile and Timeline tabs in Chrome)
CSS3 Transition-Only JSFiddle
Here's some information about why that should be expected. Essentially, CSS transitions and requestAnimationFrame work very similarly under the hood.
I would delegate as much as possible to css transitions. So instead of moving the clip with js in the interval callback, transition it from top to bottom (example of transitioning).
You could try something similar with the left property, there's no random easing function but maybe you could achieve something similar with one of the bounce functions. Maybe change the easing function with an interval that's less frequent than your current one.
Also, just by slowing the interval of your current solution you'd get visually ok results with less CPU usage.
Side-note: for a completely different route you can replicate your html in a canvas and apply some effects to that. Google has plenty of results for "canvas glitch".
Update: here's my version of your latest fiddle
I get about 10 % less cpu usage with it when comparing to yours. Key differences are:
uses a timeout instead of requestAnimationFrame. requestAnimationFrame is meant to keep framerate high and the animation smooth but we don't need that for the random offsetting. Timeout is also better than an interval since the loop function is quaranteed to finish before next iteration starts.
removed the transparent background, since transparency has a rendering cost
I want to animate some text in a slow and subtle way. jQuery animations work with integer values, so if you want to animate a size over say 10px in two seconds, you see a series of small steps at five FPS which looks jerky.
Here's a jsFiddle that shows what I'm talking about.
I found a similar question about animating positions, but the top/left/etc properties are integral, and so the accepted answer says it's not possible. However font-size can be animated as a real number, if jQuery will spit real numbers out.
I will also want to chain together a series of such animations.
Any ideas?
I had another look a the minimum point value which is visually recognisable. The smallest unit for pt I noticed change in was 0.2pt.
However, I noticed that when applying the change in steps of 0.2 points over a period of 1 millisecond per increment in a while loop that it still looked a little "laggy". May not if not running in jsfiddle though.
The point is that if you want to change the font-size smoothly by 10 points you must apply the change in steps of 0.2pt or 0.25pt or 0.5pt (what ever you find smoothest) at a time and you then must use an interval of 1 to stay smooth but you should not apply a different interval as otherwise the incremental steps are to small to notice and the whole animation ends up choppy again.
I think trying to force this 10pt change over 2 seconds will always look choppy no matter what framework you use, due to the lack of visual change on the font-size in the lower decimals.
This worked for me quite well:
(I'm not taking decreasing font-size animation into account in this example but that can be added off course)
function smoothAnimation($selector, startPoints, points){
var increments = 0.2;
var currentPoints = startPoints;
var endPoints = currentPoints + points;
while(currentPoints < endPoints){
currentPoints += increments;
$selector.animate({"font-size": currentPoints.toString() + "pt"}, 1, "swing");
}
}
$('#msg').click(function() {
$msg = $('#msg');
$msg.animate({"font-size": "80pt"}, 400, "swing");
smoothAnimation($msg, 80, 10);
$msg.animate({"font-size": "40pt"}, 400, "swing");
});
DEMO - smooth(ish) font-size animation
To make it look smoother increase the increments value to 0.25 or even 0.5. Ones you have nice smooth step you can set the points to any other value and the animation stays smooth as long as you don't force a 2 second animation interval.
Another option would be direct manipulations css transformations - here's an example (I only included webkit css to make it readable, but similar functions exist in all modern browsers). The "ease-out" property includes the fast-then-slow functionality you were aiming for. It's certainly smoother than what you've been able to get so far, but it's quite blurry - not sure if it's a trade-off you'd want to make. And as it's an experimental property, you'd still probably want your existing jQuery animation as a fallback for older browsers.
jQuery animations are terrible. Look into another tweening solution that utilizes requestAnimationFrame technique or a better timing mechanism. Maybe try a tween lib like tween.js look at the Rome demo, nice slow moving clouds...
Right now, only firefox has support for sub-pixel rendering of fonts, so animations in other browsers will always snap to pixel.
See the following fiddle:
[edit: updated fiddle => http://jsfiddle.net/NYZf8/5/ ]
http://jsfiddle.net/NYZf8/1/ (view in different screen sizes, so that ideally the image fits inside the %-width layouted div)
The image should start the animation from the position where it correctly appears after the animation is done.
I don't understand why the first call to setMargin() sets a negative margin even though the logged height for container div and img are the very same ones, that after the jqueryui show() call set the image where I would want it (from the start on). My guess is that somehow the image height is 0/undefined after all, even though it logs fine :?
js:
console.log('img: ' + $('img').height());
console.log('div: ' + $('div').height());
$('img').show('blind', 1500, setMargin);
function setMargin() {
var marginTop =
( $('img').closest('div').height() - $('img').height() ) / 2;
console.log('marginTop: ' + marginTop);
$('img').css('marginTop', marginTop + 'px');
}
setMargin();
Interesting problem...after playing around with your code for a while (latest update), I saw that the blind animation was not actually firing in my browser (I'm testing on Chrome, and maybe it was firing but I wasn't seeing it as the image was never hidden in the first place), so I tried moving it inside the binded load function:
$('img').bind('load', function() {
...
$(this).show('blind', 500);
});
Now that it was animating, it seemed to 'snap' or 'jump' after the animation was complete, and also seemed to appear with an incorrect margin. This smacks of jQuery not being able to correctly calculate the dimensions of something that hadn't been displayed on the screen yet. On top of that, blind seems to need more explicit dimensions to operate correctly. So therein lies the problem: how to calculate elements' rendered dimensions before they've actually appeared on the screen?
One way to do this is to fade in the element whose dimensions you're trying to calculate very slightly - not enough to see yet - do some calculations, then hide it again and prep it for the appearance animation. You can achieve this with jQuery using the fadeTo function:
$('img').bind('load', function() {
$(this).fadeTo(0, 0.01, function() {
// do calculations...
}
}
You would need to work out dimensions, apply them with the css() function, blind the image in and then reset the image styles back to their original states, all thanks to a blind animation that needs these dimensions explicitly. I would also recommend using classes in the css to help you manage things a little better. Here's a detailed working example: jsfiddle working example
Not the most elegant way of doing things, but it's a start. There are a lot more easier ways to achieve seemingly better results, and I guess I just want to know why you're looking to do image blinds and explicit alignment this way? It's just a lot more challenging achieving it with the code you used...anyways, hope this helps! :)
Hey, I'm just wondering how to cycle through a bunch of images, and set them as the background for a div.
What I'm looking to do is: set the first image as the background to a div. Wait X seconds. Set the next image as the background. Wait X seconds … etc. and continue
I've got the following code which works for 1 image.
$(document).ready(function() {
var source = $(".field-field-background img:first").attr("src");
$('.field-field-background img:first').remove();
$('#main-inner').css('background', 'url('+ source +') no-repeat');
});
I'm guessing I need to get an array of the image sources, loop through the array and set it as the background, with a delay somewhere in the loop. Any ideas how I'd do this?
One of the biggest advantages of jQuery is that it has a very robust plug-in community. Many tasks that you might want to accomplished have been tackled by others before you. Particularly with a common task like this, I would recommend looking for a plug-in first, before trying to reinvent the wheel. Many plug-ins have the advantage of having gone through rigorous testing and multiple versions, to result in a polished product.
The jQuery Cycle plug-in would be a good candidate, if you are looking to do a slideshow type effect. If what you want is to cycle the background, while keeping foreground elements, you might look at something more like this: Advanced jQuery background image slideshow
$(document).ready(function() {
Cycler={};
Cycler.src=['path/to/img1', 'path/to/img2', 'path/to/img3'];
Cycler.cur=0;
Cycler.cycle=function() {
if(++Cycler.cur>=Cycler.src.length) {
Cycler.cur=0;
}
$('#main-inner').css('background', 'url('+ Cycler.src[Cycler.cur] +') no-repeat');
setTimeout(Cycler.cycle, 5000);//5 seconds
}
Cycler.cycle();
});
try this:
setInterval(function(){
var source = $(".field-field-background img:first").attr("src");
$('.field-field-background img:first').remove();
$('#main-inner').css('background', 'url('+ source +') no-repeat');
},4000);