Snap SVG animation slowing down the FPS - javascript

As you can see on this page: http://www.gigil.it/newroot/index.php//come-doniamo
I have some svg animations done with snap SVG.
The animations are triggered when the element is in the viewport.
And it's basically doing the same animation on all the paths inside the SVG.
Here is the script:
jQuery(window).scroll(function() {
jQuery(".icon-come-doniamo").each(function(){
//console.log($(this));
if (jQuery(this).isOnScreen() == true) {
if (!jQuery(this).hasClass("already-visible")){
var $that = jQuery(this);
setTimeout(function(){
//jQuery(this).addClass("already-visible");
var Elemento = $that.find("svg");
var iconSnap = Snap("#"+Elemento.attr("id"));
var iconPaths = iconSnap.selectAll("path");
var delays = 0;
var count = 0;
iconPaths.forEach(function(elem,i) {
setTimeout(function(){
//console.log("gegge");
var elemDim = elem.getBBox();
elem.animate({transform: 'r0,' + elemDim.cx + ',' + elemDim.cy + 's1,1' }, 700, mina.linear );
}, delays);
delays = delays + 400;
count = count + 1;
});
if (count == iconPaths.length) {
//console.log("fattgegge");
$that.addClass("already-visible");
}
},1000);
}
}
});
});
Pretty simple I think, but very often the FPS gets really really low and the animations become super bulky.
I've tried anything, but no big changes.
Any tips?

Solved it by removing the Bounding Box calculations.
Removed the rotation property.
Now it's a lot smoother.
From this:
elem.animate({transform: 'r0,' + elemDim.cx + ',' + elemDim.cy + 's1,1' }, 700, mina.linear );
to this:
elem.animate({transform: 's1,1' }, 700, mina.linear );

Related

jQuery spin function - specify 20% spin upon image click?

I'm a jQuery newbie - but have managed to modify a roulette wheel script to spin a "pie" image for a homepage I'm working on.
It works great - but the client also want to add an arrow on either side that will advance the pie one section upon click - so clockwise for one arrow, counter-clockwise for another.
Is there a way to specify a partial spin?
Any guidance is much appreciated! I'm trying to meet a ridiculous deadline and am struggling with this.
Here's the page:
http://bluetabby.com/rr/index13.html
Here's the jQuery code so far - the functions I need to figure out are leftArrow and rightArrow:
$( document ).ready(function() {
window.WHEELOFFORTUNE = {
cache: {},
init: function () {
console.log('controller init...');
var _this = this;
this.cache.wheel = $('.wheel');
this.cache.wheelSpinBtn = $('.wheel');
this.cache.leftArrow = $('.leftarrow');
this.cache.rightArrow = $('.rightarrow');
//mapping is backwards as wheel spins clockwise //1=win
this.cache.wheelMapping = ['Mitzvahs','Galas','Florals','Props','Weddings'].reverse();
this.cache.wheelSpinBtn.on('load', function (e) {
e.preventDefault();
if (!$(this).hasClass('disabled')) _this.spin();
});
this.cache.rightArrow.on('click', function (e) {
e.preventDefault();
if (!$(this).hasClass('disabled')) _this.spin();
});
},
spin: function () {
console.log('spinning wheel');
var _this = this;
//disable spin button while in progress
this.cache.wheelSpinBtn.addClass('disabled');
/*
Wheel has 10 sections.
Each section is 360/10 = 36deg.
*/
var deg = 1000 + Math.round(Math.random() * 1000),
duration = 6000; //optimal 6 secs
_this.cache.wheelPos = deg;
//transition queuing
//ff bug with easeOutBack
this.cache.wheel.transition({
rotate: '0deg'
}, 0).delay(1000)
.transition({
rotate: deg + 'deg'
}, duration, 'easeOutCubic');
//move marker
_this.cache.wheelMarker.transition({
rotate: '-20deg'
}, 0, 'snap');
//just before wheel finish
setTimeout(function () {
//reset marker
_this.cache.wheelMarker.transition({
rotate: '0deg'
}, 300, 'easeOutQuad');
}, duration - 500);
//wheel finish
setTimeout(function () {
// did it win??!?!?!
var spin = _this.cache.wheelPos,
degrees = spin % 360,
percent = (degrees / 360) * 100,
segment = Math.ceil((percent / 5)), //divided by number of segments
win = _this.cache.wheelMapping[segment - 1]; //zero based array
console.log('spin = ' + spin);
console.log('degrees = ' + degrees);
console.log('percent = ' + percent);
console.log('segment = ' + segment);
console.log('win = ' + win);
//re-enable wheel spin
_this.cache.wheelSpinBtn.removeClass('disabled');
}, duration);
},
resetSpin: function () {
this.cache.wheel.transition({
rotate: '0deg'
}, 0);
this.cache.wheelPos = 0;
}
}
window.WHEELOFFORTUNE.init();
});//]]>
Thanks for any pointers!
I looked through your code and figured out you are using transit.js to do the spinning animations. Essentially, the object's css (transform "rotate") is being updated over a certain amount of time (like jQuery's animate).
You can extend your wheel of fortune object with spinright and spinleft functions (or whatever name you prefer), which you can bind to the keys/buttons that you'll create. Your code would look something like this:
WHEELOFFORTUNE.spinright = function() {
// get current degree of wheel and convert to integer
var degree = parseInt( this.cache.wheel.css('rotate'), 10 );
this.cache.wheel.transition( { "rotate": (degree + 73) + "deg" },1000 );
}
WHEELOFFORTUNE.spinleft = function() {
var degree = parseInt( this.cache.wheel.css('rotate'), 10 );
this.cache.wheel.transition( { "rotate": (degree - 73) + "deg" },1000 );
}
Then you can bind these functions to buttons or call the functions directly in console:
WHEELOFFORTUNE.spinright()
WHEELOFFORTUNE.spinleft()
Note: 73deg looks to be about the amount that 1 section is, but you'll probably have to play around with the numbers. You may also want to cache the degrees in your object as well. You probably will also need to figure out a way to center on each section per button press.
Cheers!

Efficiently creating a simple parallax effect with jQuery

So for one of my new projects, I decided to write a super simple parallax script for some background images on scroll. This is what I came up with:
$(document).ready(function(){
parallaxScroll();
$(window).bind('scroll', function() {
parallaxScroll();
});
});
function parallaxScroll() {
$(".parallax").each(function() {
if($(this).hasClass('reverse')) {
$(this).css("background-position","center " + (($(this).offset().top - $(window).scrollTop())/2) + "px");
} else {
$(this).css("background-position","center " + (($(this).offset().top - $(window).scrollTop())/-2) + "px");
}
});
}
My question is, is this efficient enough? If not, is there a better solution? I wasn't sure if using an .each() would be best for performance, but it seems to work fine. The reason I have the function run at document load is so when you scroll the page for the first time, the background image doesn't jump.
Instead of css which sets the value immediately, consider using animate instead. It defers setting values using timers/requestAnimationFrame, ensuring that your animation does not block the UI, is async (runs pseudo-parallel to other code), and ensures that the animation is smooth.
This is a plain JS solution, but you'll be able to port it to jQuery really easily:
var lastScrollY = 0;
var backgroundImageY = 0;
var requestAnimationFrame = window.requestAnimationFrame ||
window.msRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame;
window.addEventListener('load', processScrollEvent);
function processScrollEvent() {
var innerHeight = window.innerHeight;
var scrollHeight = document.body.scrollHeight;
var backgroundImage = document.querySelector('#background img');
lastScrollY = document.body.scrollTop;
var currBackgroundImageY = Math.round(((backgroundImage.scrollHeight - innerHeight) / 100) * ((lastScrollY / (innerHeight - scrollHeight)) * 100));
if(currBackgroundImageY != backgroundImageY) {
backgroundImageY = currBackgroundImageY;
requestAnimationFrame(processScrollAnimationFrame);
}
}
function processScrollAnimationFrame() {
var backgroundImage = document.querySelector('#background img');
var transforms = ['transform', 'oTransform', 'msTransform', 'mozTransform', 'webkitTransform'];
for(var i = 0; i < transforms.length; i++) {
backgroundImage.style[transforms[i]] = 'translate3d(0, ' + backgroundImageY + 'px, 0)';
}
}

jQuery - Fixing the animation

I am creating a new "whack-a-mole" style game where the children have to hit the correct numbers in accordance to the question.
I have the numbers animating from a set top position to another with a random width so that they look like they are floating up like bubbles.
The only problem I am having with it is that sometimes the numbers glitch and the width on them changes suddenly making it appear to jump from one side of the container to the other.
The only explanation I can think of is the width must be resetting somewhere which I have tried to look for.
Either I am blind or it is something else, can someone help me to find the source of the problem.
Here is the code that maps the numbers...
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function scramble() {
var children = $('#container').children();
var randomId = randomFromTo(1, children.length);
moveRandom("char" + randomId);
}
function moveRandom(id) {
var cPos = $('#container').offset();
var cHeight = $('#container').height();
var cWidth = $('#container').width();
var bWidth = $('#' + id).width();
var bHeight = $('#' + id).css(
'top', '400px'
).fadeIn(1000).animate({
' top': '-100px'
}, 10000).fadeOut(1000);
maxWidth = cPos.left + cWidth - bWidth;
minWidth = cPos.left;
newWidth = randomFromTo(minWidth, maxWidth);
$('#' + id).css({
left: newWidth
}).fadeIn(1000, function () {
setTimeout(function () {
$('#' + id).fadeOut(1000);
window.cont++;
}, 1000);
});
Here is also a working fiddle so you can see the issue I am talking about: http://jsfiddle.net/pUwKb/26/
The problem is that you are re-entering your moveRandom function for an ID that is already animated. The new width calculation causes the piece to seem to jump when it is reassigned during the already animated movement. One way to fix this is to reject new piece movements for pieces you are already animating. I modified your jsFiddle and fixed it with this code:
// Keep track of the pieces actually moving
var currentMoving = [];
function moveRandom(id) {
// If this one's already animating, skip it
if ($.inArray(id, currentMoving) !== -1) {
return;
}
// Mark this one as animating
currentMoving.push(id);
var cPos = $('#container').offset();
var cHeight = $('#container').height();
var cWidth = $('#container').width();
var bWidth = $('#' + id).width();
var bHeight = $('#' + id).css('top', '400px').fadeIn(1000).animate({
'top': '-100px'
}, 10000).fadeOut(1000);
maxWidth = cPos.left + cWidth - bWidth;
minWidth = cPos.left;
newWidth = randomFromTo(minWidth, maxWidth);
$('#' + id).css({
left: newWidth
}).fadeIn(1000, function () {
setTimeout(function () {
$('#' + id).fadeOut(1000);
// Mark this as no longer animating
var ix = $.inArray(id, currentMoving);
if (ix !== -1) {
currentMoving.splice(ix, 1);
}
window.cont++;
}, 1000);
});
}
Forked jsFiddle here.
Edit: The OP wanted to show more divs at once without speeding the animation up. To do this I added 20 more character divs (each a duplicate of the first 10 numbers), fixed the guarding code a bit, altered the CSS to specify the image of the character by class, and then put a limit of 20 animations at a given time. I also put a loop around the rejection of an already animated piece, to pick another. I made some other minor improvements. Updated JSFiddle here.

JS/jQuery delay loop to get desire result (delay() not working)

I'm trying to create an loading icon by moving the css 'background-position' of an image in a loop:
$('#LoginButton').click(function () {
var i = 1, h = 0, top;
for (i = 0; i <= 12; i++) {
h = i * 40;
top = h + 'px';
$('#ajaxLoading').css('background-position', '0 -' + top).delay(800);
}
});
The problem here is that it runs to fast so I don't se the 'animation' of the moving background.
So I added jquerys delay(), but:
delay(800) is not working because delay() only works in jquery animation effects and .css() is not one of those.
How to delay this loop?
I'd suggest using jQuery timer plugin: http://jquery.offput.ca/js/jquery.timers.js
$('#LoginButton').click(function () {
var times = 13;
var delay = 300;
var h = 0, top;
$(document).everyTime(delay, function(i) {
top = h + 'px';
$('#ajaxLoading').css('background-position', '0 -' + top);
h += 40;
}, times);
});
In case you don't want any plugins, use setInterva/clearInterval:
$('#LoginButton').click(function () {
var delay = 300;
var times = 13;
var i = 0, h = 0, top;
doMove = function() {
top = h + 'px';
$('#ajaxLoading').css('background-position', '0 -' + top);
h += 40;
++i;
if( i >= times ) {
clearInterval( interval ) ;
}
}
var interval = setInterval ( "doMove()", delay );
});
Have you looked at using animate() instead of css()? I'm not 100% sure I understand what you're trying to accomplish, so this is kinda a shot in the dark.
http://api.jquery.com/animate/
Chrome, Safari and IE3+ should support background-position-y, so if you're targeting these specific browser, using jquery you could just make a timed animation() on backgroundPositionY property - http://snook.ca/archives/html_and_css/background-position-x-y
(On Firefox the effect won't work)
You can use setTimeout() and clearTimeout() functions in order to accomplish that.
IE:
var GLOBAL_i = 0;
function doAnimation() {
var h = GLOBAL_i * 40;
var top = h + 'px';
$('#ajaxLoading').css('background-position', '0 -' + top);
if (GLOBAL_i < 12) {
GLOBAL_i++;
t=setTimeout(doAnimation, 800);
}
}
$('#LoginButton').click(function () {
doAnimation()
});

jQuery Image Slider - Performance issues

I'm currently building a simple "jQuery Image Slider" but it does not work as i hoped. It's incredible slow and unresponsive, and the last image does not do anything.
URL: http://fusionmedia.dk/jquery/
What is the problem?
Thanks in advance
Specify a faster speed. Its defaulting to a slower speed.
$('#gallery').delegate('img', 'mouseover', function() {
$this = $(this);
for(var i = 0; i <= $this.siblings().size(); i++) {
if($this.index() > i) {
$this.siblings().eq(i).stop().animate({ left: (i * 50) + 'px' }, 300);
} else {
$this.siblings().eq(i).stop().animate({ left: ((i * 50) + 500) + 'px' }, 300);
}
}
});
EDIT:
You have 2 really bad problems for speed.
1: You are running a time costly loop every time they hover.
2: You are calling $this.siblings() too many times. Cache that.
Here is an example of how to better implement some of this, I still have you loop inside the hover event, you should try and get that moved out.
$(function(){
$('#gallery').find('img').each(function(){
$this = $(this);
$this.css('left', $this.index() * 50 + 'px');
});
$('#gallery').delegate('img', 'mouseover', function(){
$this = $(this);
var $sibs = $this.siblings();
for (var i = 0; i <= $sibs.size(); i++) {
if ($this.index() > i) {
$sibs.eq(i).stop().animate({
left: (i * 50) + 'px'
});
} else {
$sibs.eq(i).stop().animate({
left: ((i * 50) + 500) + 'px'
});
}
}
});
});
It depeneds on various things , are you loading all the images upfront and even size of the images matters alot.
All your subsequent requests should be cached in the browser.
you should use some caching mechanisims if possible.

Categories