Sun 5 Nov 2006
I was pleasantly surprised by all the good feedback I got for Animator.js since Ajaxian linked to the article.
The most requested feature was “Can I port it to MochiKit/jQuery/Dojo/mootools”. Which got me thinking – Animator.js is built on Prototype because I didn’t want to rewrite code for function delegation and element manipulation myself, but it doesn’t use any of Prototype’s advanced functionality. I could remove the dependency on prototype quite easily and then it would work with any toolkit. I estimate this would add about 1K to the file size.
Having a class that can be used in any toolkit would be much better than a number of independently maintained forks.
Two questions:
- What changes should I make to ensure that it can plug in to your favourite toolkit?
- If someone can make a product usable for more people by re-implementing basic functionality again, is it worth it? Or is the world standardising on Prototype to do all that for them?
I’ll be making another release in a couple of weeks so let me know your thoughts, and any other feature requests.
November 5th, 2006 at 11:29 pm
Making it independent will help Animator.js gain adoption, for example I myself am making the move from Prototype to jQuery myself.
November 8th, 2006 at 1:49 am
Looking forward for the release without dependencies. Also do you have the license or terms of use for corporate / professional websites?
November 8th, 2006 at 12:39 pm
Agree with Eric. Make it independent, +1k is acceptable for such nice lib :)
November 8th, 2006 at 11:47 pm
Hi Tahir,
Currently it is Creative Commons Attribution 2.0, but I’ll be moving to BSD at the next release.
Both of these licenses mean that you can use it as you see fit, as long as you leave a note in the source code saying where it comes from.
November 13th, 2006 at 5:05 pm
very cool!
I’m going to be using prototype so i don’t care about the dependencies. But will it work with http://bennolan.com/behaviour/ ?
or something similar?
November 13th, 2006 at 8:49 pm
As far as I can tell from skimming the Behaviour docs, that library works by applying user-defined functions as event handlers on elements. In that case, Animator will drop in nicely.
You might want to be careful about caching Animator objects - don’t create a new Animator each time someone clicks on a link, just re-use the one you create the first time.
November 18th, 2006 at 1:05 am
I think more people will use it if there are more examples available (like an accordion that is ready to use). Anyway, at the moment you are probably more interested in improving that thingy.
Great stuff you are doing, appreciate it!
November 27th, 2006 at 9:28 pm
So far, love it, and thank you for the interesting tutorial as I read through - just noting (and you can delete this comment when you read it) I saw a minor typo:
“Sometimes you’ll want to fine tune the aboce transitions.”
did you mean ‘above transitions’?
November 28th, 2006 at 2:53 am
This is an amazing script. Are you far from it working with jQuery? I’m a huge jQuery fan and would LOVE to use this. Thanks!
November 28th, 2006 at 11:13 pm
How can the fade-in effect be applied to an img when the page is loaded?
November 30th, 2006 at 2:00 am
Is there any way to queue effects with Animator? For instance, I want to have a heading that bounces a couple of times from left to right, so I have one Animator that scales text-indent from 0 - 150px, then I have another runs the reverse, 150px - 0, but with a Bounce transition. Currently I have to use the onComplete function, but this doesn’t seem very elegant. Ideas?
December 2nd, 2006 at 12:23 pm
Mike: if you have the effect you want working on a button-click, as per my examples, just move the code into an onload function:
window.onload = function() {
var a = Animator.apply(…);
a.play();
}
You’ll need to make sure that the initial style of the img element includes “opacity:0; filter:alpha(0%)” so that you can’t see the image when the page is loading.
Lachlan: use the onComplete option of the Animator. See the Badda Bing example in the demo article.
And everyone: do send me links to the work you’ve done with Animator, it’s fun!
December 6th, 2006 at 7:20 am
I’ve been trying to use your opacity feature on a hide show effect, with no progress unfortunately. Would you have an example or idea of how I can have an object show(display=block) and then fade in as well as fade out and hide(display=none).
December 6th, 2006 at 9:34 am
You can only use Animator on CSS properties that scale. In other words, ‘display’ can’t be animated because what comes half way between ‘block’ and ‘none’? Fortunately, you can use ‘opacity’ instead, see the fading example in the article.
December 9th, 2006 at 1:29 pm
bernie,
Wow. When I found Animator.js, and saw just how simple and robust it was, I started giggling like an idiot. It is GREAT, and I thank you very much for providing such an excellent script. I did notice a few bugs, though.
1. Animator.js thinks negative numeric values are invalid in CSSStyleSubjects.
The solution is to add “-?” to CSSStyleSubject.numericalRe, so that it becomes /^-?\d+(%|[a-zA-Z]{2})?$/.
2. Non-integer-value opacity does not work.
I haven’t looked to figure out what’s causing this failure yet since it doesn’t affect what I’m working on at this moment… :)
3. ‘border-width’, ‘padding’, ‘margin’ properties do not work in CSSStyleSubject (but border-left-width, padding-left, margin-left, etc. do).
I haven’t spent time to figure this one out yet, either, since I can reasonably work-around this problem for now.
Also, a feature request:
1. An easy way to chain animations (that functions as expected with toggle(), and doesn’t require the order of execution to be manually reversed). As part of this, an option to execute the chained animations with a ± offset from the end of the previous animation would be useful.
Also, in response to Comment #14, something that could be implemented for non-numeric properties such as display would be to provide an option:
1. Ignore these types of non-numeric properties (default)
2. Switch from the first value to the second value at x, where x is either the percentage of the animation complete or the step number.
Cheers,
December 10th, 2006 at 9:37 pm
Thanks for the feedback Mr Snover, I’ve put your suggestions into the new version with one exception - I can’t replicate the bug in point 3. For example, padding works fine for me. If you let me know the browser you were using and send a code sample, I’ll track down the bug and fix it.
February 22nd, 2007 at 12:52 pm
Hi! Nice site!
February 23rd, 2007 at 9:37 am
Why thankyou!
March 5th, 2007 at 4:18 am
After fighting endless battles with scriptaculous and other homegrown specific purpose animation scripts, this script is a wonderful find. Not only is it elegant, but it is an exceptionally clean implementation. That’s rare IMO. Most scripts I find are nearly random cobblings. Not so here. I learned a couple of things just reading your animator script. Thanks!
How about if you were to start accumulating worthy scripts that are written using animator? I have a script or two that I might be willing to donate, and we could get/give some pointers on things we could improve on.
March 6th, 2007 at 8:21 pm
Thanks!
I’m doing just that - send anything to bernie at [this website] and I’ll post it with the next update.
March 9th, 2007 at 4:45 am
Ok, I’ve been de-cruftizing one of my animator.js usinge scripts to bring it up to Bernie standards… found a couple of bugs in the process. Here’s a patch. In general, you have to test for type of undefined in a couple of places, because the legitimate value of a numeric style could be zero, which is false in ‘if (thisValueCouldBeZero)’. The other thing I found was using [1] on the end of the numeric regex eval… that doesn’t work on FF2 (at least).
I’m on Firefox 2.0, gotta confess I haven’t tested on Opera 9 and IE7 yet.
354c354
if (typeof toStyle[prop] === ‘undefined’) {
365c365
units = match[1] || CSSStyleSubject.numericalRe.exec(toStyle[prop]);
399c399
if (typeof value !== ‘undefined’) {
March 9th, 2007 at 4:46 am
Erm… the html injection filter clobbered a few of the patch lines… but the replacement lines survived.
March 9th, 2007 at 10:18 am
Thanks Tim, I’ve patched the release version.
By the way, I’m a fan of (foo != null) rather than (typeof foo == ‘undefined’), since the former tests for null and undefined at the same time.
March 9th, 2007 at 8:19 pm
Excellent! That goes in my bag of best practices. Always kinda wondered about that. So here’s my scenario, playing out in Opera 9 and IE7: 1. create an animator using Animator.apply with a target style classname specified. 2. move the animated element by setting style.top and style.left. 3. trigger the animation. 4. in Opera 9, the animated element moves back to its original position, and animates a bit strangely (kinda sorta). In IE 7, the animation blows chunks on animator.js line 244, setting one of ‘top’, ‘left’, ‘bottom’, ‘right’ properties. Works fine in FF2. I’ve messed with it for a while, but not found a workaround other than not using a classname as the target style. The problem seems to be in the deduction of the fromStyle and toStyle properties, and in the numeric-ness of those properties (in the case of IE7). I’ve put up a test case at test.timsworlds.com/animatortest.html
March 10th, 2007 at 2:11 am
Cheers for the bug report Tim. I’m off to France for a week (the Dordogne, if anyone wonders) but I’ll look at the issue you raised on my return.
April 3rd, 2007 at 10:48 pm
Hey, got another little bug for ya…
Line 201 should be var fn = … rather than fn =
Without the var declaration, bouncy and veryBouncy transitions are identical ;)
April 4th, 2007 at 9:22 am
Thanks - fixed now
April 5th, 2007 at 4:09 am
This site is really superb!!! Thank you for you work! loan calculator Good-bye!
April 25th, 2007 at 8:59 am
Awesome library. I’m a big fan of scriptaculous for some basic effects (and coming from Ruby on Rails it’s pretty easy to become a fan without doing much research into alternatives), but I have always felt like the dang thing requires just too much effort to customize anything. This library seems like a godsend to those of us who don’t want to follow the cookie-cutter formula for building rich websites. (Please don’t look at my homepage for an example of a rich site - it’s still very much Web 1.0 and probably will be for a long time.)
The thing I find the most impressive, though, isn’t the library, but your clear desire to build a *good* library as opposed to hacking something together. I haven’t specifically looked at scriptaculous, but with an amazing number of open source projects, I find the quality of code to be so poor that I wonder how some of these people even learned to tie their shoes. It’s always a true pleasure to see open source that is written in a professional manner. And with comments, too! I may just cry, I’m so happy!
My apologies if I seem like a jerk, but badly-written OS code ruins the usefulness of OS to such a degree that I wish some of these authors would just leave their code to themselves :P - thanks for not being one of them!
April 25th, 2007 at 9:18 am
Ta!
Perhaps it’s a side-effect of code written by many people that open source projects often have hacky code. Well written JavaScript can be so beautiful (now who’s the nerd), but it seems that libraries get judged entirely by their results, ease of use and file size, not by how easy it is to read and modify their code.
Oddly enough, the Java community is much better: the source code for Spring is so well structured and documented you could base a university Java course on it.
August 28th, 2007 at 6:44 am
Bernie —
I like the ideas and samples on this website and I want to use them, but I need some simple implementation cookbook that will show a minimal setup for, say, accordion. I can’t find any kind of instructions or documentation. Am I missing them? Are they parked somewhere else? Please help; I’ve been circling your website for hours!
Karl
September 21st, 2007 at 5:18 am
Love the Library thank you for publishing it. Now to my question.
Safari on Windows:
It doesn’t recognize margin, margin-left, etc using the CSSStyleSubject style switcher function. i.e. CSSStyleSubject( $(’ID_Name’), “style1″, “style2″);
What am i doing wrong or is this just a bug in safari?
February 16th, 2008 at 11:49 pm
Hi Bernie,
First off, awesome library! Very well designed and easy to use (largely because of the great tutorial). Anyway, I have several small tweaks that I needed so I thought I would post them as individual messages. I hope I can remember all the pieces. If it helps, I can email you my entire modified file.
Again, thanks for the fantastic work! Job well done indeed!
Best regards,
Don
February 16th, 2008 at 11:51 pm
Tweak #1 - Color names in IE
I use color names a lot (like “white”) which are preserved verbatim in IE. To support this, I added these lines to ColorStyleSubject parseColor:
match = ColorStyleSubject.parseColor.colorNames[string.toLowerCase()];
if (match)
return match;
and this object:
ColorStyleSubject.parseColor.colorNames = {
aqua:”#00FFFF”, black: “#000000″, blue:”#0000FF”, fuchsia:”#FF00FF”,
gray:”#808080″, green:”#008000″, lime:”#00FF00″, maroon:”#800000″,
navy:”#000080″, olive:”#808000″, purple:”#800080″, red:”#FF0000″,
silver:”#C0C0C0″, teal:”#008080″, white: “#FFFFFF”, yellow:”#FFFF00″
};
I chose to only support the W3C standard 16 colors (otherwise it would get a bit out of control).
February 17th, 2008 at 12:27 am
Tweak #2 - Set Operations on Class Names
In my use of CSS, I often have multiple class names per element. In these cases, what I need to accomplish is to animate between the current set of class names and the set of class names after additions and/or removals of individual names. I express this as an object for style1 in the CSSStyleSubject ctor like so:
new CSSStyleSubject(el,
{ add : [”add1″, “add2″],
remove : [”remove1″,”remove2″] });
This approach is just how I’ve handled class manipulations in my designs (optional arrays of names to add/remove).
I then added these helper functions to CSSStyleSubject.prototype:
getIndex : function (array, item)
{
if (array)
for (var i = 0, n = array.length; i < n; ++i)
if (array[i] == item)
return i;
return -1;
},
mergeClasses : function (base, mod)
{
var getIndex = this.getIndex;
var c = base ? base.split(” “) : [];
var added = [];
for (var i = 0; i < c.length; ++i)
{
var item = c[i], a = getIndex(mod.add || null, item);
if (a != -1) {
if (added[a])
c.splice(i–, 1); // changes c.length and i
else
added[a] = true;
} else if (getIndex(mod.remove || null, item) != -1)
c.splice(i–, 1); // changes c.length and i
}
for (var i = 0, n = mod.add ? mod.add.length : 0; i < n; ++i)
if (!added[i])
c.push(mod.add[i]);
return c.join(” “);
},
The “||null” stuff was to appease Firebug’s break on all errors. I also extended the probing of style1 in the same ctor:
if (style1.add || style1.remove) {
this.modStyle = style1; // for Tweak #3
fromStyle = this.mergeClasses(els[0].className,
{ add : style1.remove, remove : style1.add });
toStyle = this.mergeClasses(fromStyle, style1);
fromStyle = this.parseStyle(fromStyle, els[0]);
toStyle = this.parseStyle(toStyle, els[0]);
} else if (style2) {
.. as before ..
February 17th, 2008 at 12:37 am
Tweak #3 - Removal of Element Styles
The final tweak I made was to remove the various element style overrides when the animation reached state 0 or 1. The reason is so that I could compute other animations from the new state (the styles blocked the CSS cascade) using CSSStyleSubject. I could (and may yet) switch to a “preplan everything” mode, but that approach will hit limits as the number of states and transitions between them increases. So, here goes.
In the CSSStyleSubject ctor, I remembered the els array:
this.els = els = Animator.makeArray(els);
I chose to enable this style restoration only in my Set Operations mode (see Tweak #2), which explains this line:
this.modStyle = style1;
from Tweak #2. Right after the “remove unchanging properties” loop, I added this:
this.cleanupStyles = [];
I then changed this line:
this.subjects[this.subjects.length] = new type(els, prop, from, to, units);
To this:
var sub = new type(els, prop, from, to, units);
this.subjects[this.subjects.length] = sub;
this.cleanupStyles.push(sub.property);
Lastly, the setState method looks like this now:
setState: function(state) {
var useProps = (0 < state && state < 1);
if (this.modStyle) {
var mod = this.modStyle;
if (state < 1)
mod = { add : mod.remove, remove : mod.add };
for (var i=0; i<this.els.length; ++i) {
var el = this.els[i];
var ocn = el.className;
var ncn = this.mergeClasses(ocn, mod);
if (ocn != ncn)
el.className = ncn;
if (!useProps)
for (var j=0; j<this.cleanupStyles.length; ++j) {
var prop = this.cleanupStyles[j];
el.style[prop] = “”; // mostly OK
try { delete el.style[prop]; } catch (e) {} // often fails
}
}
}
if (useProps)
for (var i=0; i<this.subjects.length; i++) {
this.subjects[i].setState(state);
}
},
February 27th, 2008 at 12:53 am
Tweak #4 - IE vs. CSSStyleSubject
Like others I ran into issues with IE and currentStyle popping out “auto” for width and/or height as well as not recognizing filter:alpha(opacity) as the way to read opacity. Here’s may attempt to hack around those issues (all inside of getStyle):
CSSStyleSubject.getStyle = function(el, property){
var style;
if (document.defaultView && document.defaultView.getComputedStyle)
style = document.defaultView.getComputedStyle(el, “”).getPropertyValue(property);
if (!style) {
var s = Animator.camelize(property);
if (el.currentStyle) {
// IE6 propogates opacity CSS values, but doesn’t honor them. To do
// what IE6 does, we parse the “filter:alpha(opacity=#%)” rule…
if (property != “opacity”)
style = el.currentStyle[s];
else {
var f = el.currentStyle[”filter”];
if (f) {
if (!CSSStyleSubject.alphaFilterRE) // if (regex not cached)
CSSStyleSubject.alphaFilterRE = /alpha\(opacity\=([0-9.]+)\%?\)/i;
var m = CSSStyleSubject.alphaFilterRE.exec(f);
if (m && m[1])
style = (parseFloat(m[1]) / 100) + “”; // stringify
}
if (!style)
style = “1″;
}
}
if (!style)
style = el.style[s];
}
if (style == “auto”) {
if (property == “width”)
style = el.offsetWidth + “px”;
else if (property == “height”)
style = el.offsetHeight + “px”;
}
return style;
}
March 20th, 2008 at 10:55 pm
Any idea why using Animator.js within a frameset that has scrolling turned on would cause IE 7 to hang?
March 21st, 2008 at 2:57 pm
Nope, sorry.
I suggest you get the most basic Animator working that you can, then gradually add features until you discover the thing that is making this happen.
Bernie :o)
April 13th, 2008 at 3:03 am
Hey bernie sorry to bother you here, but I wonder if its possible to trigger Subject1 and then, when Subject1 animation is done, trigger Subject2. thanks
April 13th, 2008 at 7:30 pm
Sure, either use an onComplete function as in example 2, or an AnimatorChain as in example 19.