SnapSVG animation callback on set of elements not firing - javascript

See JSfiddle!
I am wanting to animate a set of elements and execute a callback when finished like so:
s.selectAll('.active').animate( {
transform: matrix
},
300,
mina.linear,
function() {
//callback doesnt fire
alert('callback')
}
)
The elements are animated correctly but the callback isnt executed.
However, when I apply the animation to a group of elements, the callback is fired:
group.animate( {
transform: matrix
},
300,
mina.linear,
function() {
alert('callback')
}
)
.. But I don't want to put my selected elements in a group as this would cause more complications in other places.
Is it possible to animate a set of elements that I got via a .select() or .selectAll() while being able to fire the callback?
Thanks a lot in advance!
Edit: For future readers, you can animate a set of elements by using forEach and counting if all elements are done animating:
function hideToPoint(elements, x, y, callback) {
var finished = 0;
elements.forEach(function(e) {
e.animate( {
//do stuff
},
300,
mina.linear,
function () {
finished++;
if (finished == elements.length) {
callback();
}
}
)
})
}

I'm going to have a stab at answering a couple of problems, even though I'm not sure if related to the callback. Its hard to tell if its just the example code or not without a proper test like a jsfiddle.
However, there are at least 2 problems in the code above.
Creating a matrix is with
new Snap.Matrix(); // as opposed to Snap.matrix()
Also
elements.animate()
The problem here is that animate acts on one element (edit: looks like it can work on elements within a set, but not the callback as example here, edit2: callbacks on sets may now be supported), not multiple elements as such (you can sometimes apply somethings to a set which deals with them individually, but as far as I'm aware, thats not the case with animate).
So you either want to do a
elements.forEach( function(el) { el.animate({blah: value}, 2000, mina.linear.callback )
});
or if its an svg group (as opposed to a set), the original code would possibly work (but I would call it 'myGroup' or something instead of 'elements' for code readability and guessing what it contains)
fiddle (have included a different animation using snap animation string)

Related

Callbacks : animate complete: called after .promise().done()

I made a very simple plugin for my personal use that allows animated rotations in jQuery.
And I added callback functions.
If the rewind parameter is true, the rotation is undone and then, a callback.
If it's false, the callback is immediate after the rotation.
I had to do this because, for some reason, the .promise().done(function(){ ... }) is called before complete: of animate()
So I had to skirt this.
Now the problem is that if I target multiple elements, the callback is called for each element. 'cause I use an each function I presume. But I want only one callback for the whole animation.
Here is the fiddle
How to do this please? Thx !
PS: I saw a similar question here : jQuery $.animate() multiple elements but only fire callback once but not applying because of the .promise().done() issue. And I don't want to add a variable, I want to understand ^^
You could try using the index of each and only call the callback if it is the first element
Something like:
return this.each(function(i) {
....
....
complete: function() {
i==0 && a.callback && a.callback()
}

Jquery or Javascript Animating the Z-Index

I need the functionality of animating the z-index property of a specific HTML object. I've been able to achieve this animation in two ways that both have their difficulties/drawbacks. Successfully answering this question for me will fix one of the following two issues:
The first is by adapting the JQuery animate command with the step functionality outlined here by the accepted answer:
jQuery's $('#divOne').animate({zIndex: -1000}, 2000) does not work?
The problem with this method for me is that the $('#obj').stop(); command cannot prematurely end the animation when done in this way. It always finishes unless I destroy the object I'm working with and create a new one (which causes blinking obviously). If anyone knows of a way to properly stop a step animation like this, or a work-around for the issue, I'd love to see it.
var div = $('#obj');
$({z: ~~div.css('zIndex')}).animate({z: [-2000, 'linear']}, {
step: function() {
div.css('zIndex', ~~this.z);
},
duration: 10000
});
The second is using a setInterval loop on 20 MS (a speed that is sufficient for my needs) to simply adjust the z-index to what it should be at that point of the "animation". This works great for a few moments, then something causes it to stop working suddenly. The code still runs through the $('#obj').css('z-index', val); line, and val is changing, but it no longer updates the object in the DOM. I've tried it on slower timer settings as well with identical results. Anyone know why JQuery might suddenly no longer be able to set the Z-Index?
function () move {
if (!(MoveX == 0 && MoveY == 0))
{
$('#obj').css('z-index', val);
}
}
$('#obj').stop() doesn't work for you because the animation isn't being performed on $('#obj').
It is being performed on the object $({z: ...}) (with a custom step function). This means you should do something like
var anim = $({z: ~~div.css('zIndex')}).animate({z: [-2000, 'linear']}, {
step: function() {
div.css('zIndex', ~~this.z);
},
duration: 10000
});
// sometime later
anim.stop();
See this demo.
Edit For what it's worth, here is the same demo using an animation interval. I see a syntax error in your second snippet: the function declaration should be
function move() { ...
I assume that's a typo since your code wouldn't even parse. Other than that, I'm not sure why that solution didn't work for you.

RaphaelJS animate does not work when called from an outside method

As a desktop developer I am very new to Javascript, so I often run into things that puzzle me about the language. I was working with click events on RaphaelJS shapes, and initially I was setting the state and animation of the object in a private method:
innershape.node.onclick = function () {
if (scope.state === 0) {
_setState(1);
} else {
_setState(0);
}
};
function _setState(state) {
scope.state = state;
if (scope.state === 0) {
innershape.animate({ fill: "#00FF19" }, 500);
} else {
innershape.animate({ fill: "#C05219" }, 500);
}
}
This was functioning as expected. I then decided to add an outside function that would loop through all the objects and de-select (and therefore reverse-animate) all the other shapes. The result may be seen in this jsfiddle: http://jsfiddle.net/txj4zasn/4/
The function is called properly, and the animate() function is apparently executed, but the visible animation never appears, and the color never changes. I suspect that this is something very basic to Javascript that I just don't understand. Can someone explain to me why this is happening?
Its not really very clear what you want to achieve (beyond getting the animation to work), so my initial solution I think isn't good, but I will expand on that.
The problem looks a bit like you are trying to combine two different elements, functional scope and object variables.
A quick solution would be to include...
this.id = 1;
var id = this.id; // so id now a closure to the later function
as updateSelected(id); the id here, is inside another function, so we can't use 'this.id'. But then later you are checking against z[i].id so you need that to be defined also.
jsfiddle
This all feels a bit clunky though, prone to error, and is quite hard to read. So the first question is do you need objects ? You could store information in the "data" part of a Raph element, which already is an object.
Here is an example of how I would write it, I appreciate this may not be suitable as it may be part of a bigger project which needs other elements in an object, but it may give some idea.
function updateSelected( el ) {
if( el.data('innerstate') == 1 ) {
el.animate({ fill: "#00FF19" }, 500);
el.data('innerstate',0)
} else {
el.animate({ fill: "#C05219" }, 500);
el.data('innerstate',1);
}
}
function addElement() {
var innershape = paper.rect(100,100,100, 100);
innershape.attr({fill: "#00FF19" });
innershape.data('innerstate', 0);
innershape.click( function () {
updateSelected( innershape )
} );
};
addElement();
This code I can pretty much read instantly and know how and if it will work.
jsfiddle
jsfiddle showing it combined with more than one element, or jsfiddle thats a bit more compact

Run function only after another function is complete

I have been trauling the web to find an answer to this, but everything I find seems to fail.
In a function I want to disable a href link. Once this function is complete (including animations) I want to re-enable the link.
I currently have the following:
function prev() {
$prevId = $('.active').prev('.slide-item').attr('id');
if ($prevId) {
$('#prev').bind('click', false);
var id = '#'+$prevId;
$('#thumb-list li a.current').removeClass('current');
var thumb = '#thumb-list li a'+id;
$(thumb).addClass('current');
$('.active').transition({ left: offCanvas }, 300, function() { $(this).hide(); }).removeClass('active');
setTimeout(function() {
$(id).addClass('active').css({ left: -pageWidth }).show().transition({ left: 0 }, 300).css("-webkit-transform", "translate3d(0px,0px,0px)"):
}, 30);
prevFinished();
} else {
$('.active').css("-webkit-transform", "translate3d(0px,0px,0px)");
id = '#'+$('.active').attr('id');
}
$prevId = $(id).prev('.slide-item').attr('id');
$nextId = $(id).next('.slide-item').attr('id');
prevNextCheck();
}
function prevFinished() {
$('#prev').unbind('click', false);
}
But it doesn't work at all.
If anyone could point me in the right direction that would be great.
P.S I tried using callbacks, but unless I am doing something terribly wrong it failed every time.
As stated in the previous answer you could use jQuery's Deferred and Promises. Depending on when exactly you'd want your functions to be executed it'd look something like this:
var deferred = new $.Deferred();
var promise = deferred.promise();
$('.someClass').transition({ left: 0 }, 300, function(e) {
// .. maybe do some other stuff or maybe not
deferred.resolve();
});
promise.then(someFunction);
function someFunction() {
// this runs after your transition has finished
}
You can of course chain multiple then together if you wanted.
Additionally if you have multiple functions that you need to wait for to finish you could also use $.when and wait for all of them to finish.
$.when(promise1,promise2).then(someFunction);
There are a few methods of accomplishing this.
Solution one is to use callbacks. Your animation sequences look overly complex, but it appears you are making a rotation animation for multiple objects, and I don't know the requirements of it. But, one thing I can say is that your callbacks are only encapsulating a small amount of functionality from the looks of it. Widen it out to include EVERY action that is expected to occur after the animation.
Solution two is to use deferred objects and promises. I'm not the best person to ask for a working example of how this works, but the functionality fits your requirement.
Both of these routes are documents jquery features that just need to be put in place correctly.

get height from ajax request in prototype

How can i get the height of an ajaxreponse in prototype. I need to get the height of the element returned from Ajax.
new Ajax.Updater('myDiv','myUrl' {
parameters: '?something=something',
onSuccess: function(transport){
//get height from first div returned
}
});
You'll have to insert the element into the DOM, then measure it. Your best bet is to insert it where it's really going to be when you're done with it (which you're doing, since you're using Ajax.Updater). [If for some reason you want to measure it first, you can do that by appending it with the style position: absolute and giving it a large negative left value (e.g., left: -10000px) to keep it off-page, then measure it, then move it to where you want it (removing the big left, etc.), but beware of the size changing because of styles and other factors related to its position in the DOM.]
Prototype calls all the usual Ajax callbacks, but note that onSuccess is called before the update is performed. To get a callback afterward, either use onComplete (but note it gets called for failures, too) or use setTimeout from within onSuccess.
Given your code and the comment "get height from first div returned," using onComplete would look like this:
onComplete: function() {
var firstDiv, height;
firstDiv = $("myDiv").down("div");
if (firstDiv) {
height = firstDiv.getHeight();
// ...
}
}
Live example
If you prefer the onSuccess + setTimeout approach (but note there will be a longer delay between when the element is added and when you measure it):
onSuccess: function() {
setTimeout(function() {
var firstDiv, height;
firstDiv = $("myDiv").down("div");
if (firstDiv) {
height = firstDiv.getHeight();
// ...
}
}, 0); // Won't really be 0ms, but not much longer
}
Live example

Categories

Resources