script.aculo.us morph effects
November 21st, 2006 by Thomas Fuchs, 44 comments »script.aculo.us 1.7 beta 1 is out now (see below!)—and the big new thing is Effect.Morph which allows for CSS style-based effects, and its companion, the Effect.Transform shortcut to set up complex animations in a snap (while I’ve been thinking of adding this for some time now, there’s some inspiration coming from Bernie’s Better Animation Class).
Let’s look at how Effect.Morph is used:
1 2 3 |
new Effect.Morph('error_message',{ style:'border-width:3px; font-size:15pt; color:#f00' }); |
This will start an effect to morph the div element with id error_message smoothly from whatever border-width currently is set, whatever font-size is set (note the currently set font-size must have the same unit, in this case pt), and whatever color is set to the given new values. Because Effect.Morph queries the original values with Prototype’s Element.getStyle API, it doesn’t matter whether these styles are set inline or in an external stylesheet definition. Of course the effect supports all usual options, like duration or transition.
It internally uses a new addition to the String API in script.aculo.us 1.7, that is String.prototype.parseStyle. This method is smart about how to parse CSS style properties correctly. For example, it will automatically parse a ‘border-width’ property to be split up in ‘border-left-width’, ‘border-top-width’ and so on. This allows you to use styles freely as you do in everyday CSS files.
But! Prototype and script.aculo.us are about making development easier, having less stuff to type and think about, and handle common cases with the utmost niceness:
|
|
$('error_message').morph('font-size:15pt; color:#f00'); |
.morph() ia automatically available for all elements. It takes an optional second argument, an object that’s given to the generated Effect.Morph. You can use this to chain morph effects, like this:
|
|
$('blah').morph('background:#800').morph('background:#fff',{delay:3}); |
Effect.Transform is a helper that doesn’t generate a normal effect, but stores a more complex set of Effect.Morph effects to be generated when called upon.
Let’s see an example of how this works:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// define the Effect.Transform and store for later var transformation = new Effect.Transform([ { 'div.morphing': // divs with CSS class 'morphing' 'font-size:20px;padding-left:40em;opacity:0.5' }, { 'error_message': // or '#error_message' 'width:480px;border-width:10px;' + 'border-right-width:20px;' + 'margin:20px;margin-bottom:-20px;' + 'font-size:30px;' + 'background:#954' } ],{ duration: 1.3 }); // sometime later transformation.play(); |
Effect.Transform takes an array of tracks, which are defined in a ‘identifier or CSS selector’: ‘style’ syntax, and generates Effect.Morph effects when you call the play() method. You can also add additional tracks later on, which the addTracks() method.
Notably, you can use DOM object ids or CSS selectors to choose which elements should be acted upon—which means that at any given time you call play(), Effect.Transform will automatically choose the correct elements for the effects, given on these rules. Also, you can an Effect.Transform when none of the referenced elements are already rendered—as long as they are there when you actually call play().
If you need a complex one-shot effect, you can also do this:
1 2 3 4 5 6 7 8 9 |
// one-shot transformation // shrink and fade info messages, // highlight error messages new Effect.Transform([ { '#messages li.info': 'font-size:1px;height:0px;opacity:0' }, { '#messages li.error': 'font-size:12px;background:#fee;color:#f00' } ],{ duration: 1.3 }).play(); |
The 1.7 beta version also contains the newest Prototype 1.5.0_rc2 library, fresh from SVN, which fixes lots of things here and there.
With your help (beta testing gives good karma!) we can hopefully sort any bugs out and have this released soon (maybe as an Xmas present?)!
So, get started by downloading from SVN, or use this link to get 1.7.0 beta 1. If you use Ruby on Rails, be sure to grab edge Rails, or the 1-2-prerelease branch, which both include the beta.
Tweet This Post
Digg This Post
Share on Facebook









Greeeeeaaaaat!!!!!
Happy I teased you about it…!
Out of curiosity, what is the rationale behind passing the styles as a string, rather than an object?
Performance?
In any case, this is great!
My question regarding performance was related to using a string rather than an object for passing the style, not to the overall performance of the morph effects…
And yes… I’d much rather pass an object than a string, it’s way more consistent with the way <code>setStyle</code> is handled in Prototype.
OT: why not extend Prototype to have a <code>capitalize</code> string method… I’ve seen instances of <code>s.charAt(0).toUpperCase() + s.substring(1)</code> pop up just about everywhere lately…
Just read your comment about css class support. Guess I should have read all the comments before posting.
Tobie: Great that you like it. Performance: it uses the script.aculo.us effects backend, so generally the performance is great. Object/string: for now it’s strings, but I guess I’ll add something to it that allows it to use object-based style definitions too. Also in the works: support for CSS classes.
Thomas, This is pretty cool. My only aversion to this is that now we are using javascript for css. I haven’t really thought it through, but is there anyway to morph between the current styles and a css class instead of putting in all the css selectors? Then one wouldn’t have to maintain their presentation in two places (js and css) instead of one (css). Just curious.
@ Thomas: <a href="http://dev.rubyonrails.org/ticket/6666">patch for capitalize</a>
@ John: Thomas wrote the following in the comments above:
<blockquote>Also in the works: support for CSS classes</blockquote>
Looks like your mind had been read…
Come to think about it, if morph included an <code>initialStyle</code> option, couldn’t most effects be rewritten as calls to morph?
For example:
<pre><code>
Effect.Highlight = function(element) {
element = $(element);
new Effect.Morph(element, {
initialStyle: {
backgroundColor: ‘#f00′
},
finalStyle: {
backgroundColor: element.getStyle(‘backgroundColor’)
}
})
}
</code></pre>
The other effects have some additional features that might not be doable with effect.morph, but basically, yes some of them can be rewritten to use it (i _love_ patches!).
<code>.capitalize</code>: please submit a tested patch (that also updates the occurences…)
I suppose you heard it a few times, but: You (and Scriptaculous) rock!
the effects are really choppy, and i have a 1.0GHz G4, not some old machine. compared to moo.tools this is garbage.
Pat: A 1GHz G4 actually _is_ some old machine (4+ years old). As moo.tools can’t do magic, it will render the exact same thing at exactly the same speed. Either make constructive comments, provide a performance testing comparision (this could help both moo.tools and script.aculo.us!), or shut up.
It’s great feature!
I am waiting for the stable release to implement into ProtoJax Framework
Grats!
Very cool! I also think it would be good if this will be organized in objects props, but: it´s beta, baby
Good work @th0fu!
@ Jonas: "there’s your patch"http://dev.rubyonrails.org/ticket/6674.
Could this be changed so just that text of a href with an ID could change?
@ Jay: I’m not quite sure what you want to do, but wouldn’t this work:
<pre><code>$(‘mylink’).href = ‘http://…’;</code></pre>
Looks good. What about cross browser drop shadows for divs?
Is it possible to morph background-image too?
I’m not succeeding to morph the background image.
Kind regards.
I agree that Pat’s comment was not constructive. However, he is right about the performance issue.
Let me get this straight: I really love Scriptaculous and I appreciate the work you put into it. But on my personal wish list speed improvements for the existing Effects ranked way higher than MorphEffects.
Take a look at Rico’s <a href="http://openrico.org/rico/demos.page?demo=rico_accordion">Accordion Widget</a> for instance. Now try to match this performance-wise using Effect.Parallel. There’s no need for performance testing since it’s easily perceivable by the eye.
Now this doesn’t render Scriptaculous ‘garbage’ nor does it make me want to use Rico instead. It’s just that I think performance <em>is</em> an issue in Scriptaculous and there’s certainly room for improvement.
As for the G4, I assume you’re aware of the fact that Apple sold these CPUs as part of their professional line of notebooks until less than a year ago. I hope you don’t really consider these machines obsolete.
would be nice if I could just pass a classname that matches a class defined in my css file and have the script look up the styles defined in that class instead of typing out style strings in the javascript.
@ thinsoldier, John:
Just posted "a patch":http://dev.rubyonrails.org/ticket/6674 for className support which allows the following:
<pre><code>$(element).morph(‘.morphed’);</code></pre>
And which will morph element to all the properties of the @morphed@ CSS class. Properties which cannot be morphed (like font-family, for instance) will be applied as soon as the effect is finished running.
@Andy: There obvoiusly is need for performance testing, so we compare across different browsers, CPUs, and whatnot. Browser performance isn’t really something that’s only determined by raw CPU power. For example, take the currently available version of Safari, and take the WebKit nightlies. The nightlies are 3-5x times faster when rendering, on the same machine. The same goes true for say, Firefox on Linux, where special "optimized" builds are available. All the effects engine does is to instruct the browser to change styles on the elements and do whatever is necessary to render these changes– this is basically only influenced on how a certain browser deals with these rendering updates, and how efficient its layout engine redraws.
To improve performance, we need a test suite, so we can 1) compare performance with other libs, and 2) improve stuff with measurable metrics. Note that the unit testing framework alreay includes benchmarks, but we’d need to plug to the effects backend directly for this (measure fps, basically). We probably also need some standardized DOMs to work upon (with different complexities). I’m happy to hear any ideas on how this can look like, or what should be included (we should use the rails-spinoffs mailing list for this!).
On the G4 (note that I meant the 1GHz version): personally i’ve found a bit of a joke from Apple to sell these for so long– but there’s to be a reason for their switch to Intel, and my guess that reason is IBM not coming up with mobile G5’s.
One of the main issues I have with Scriptculous is with the frame rate. It is something that we need to have a constructive discussion about how to tackle it. My impression is that moo.fx tends to be smoother, but we really need to have a framework to prove that and see how patches improve this.
Is there a way to count the number of ‘dropped frames’ ? If so it should be relatively easy to test for scriptaculous, though we need something similar for the other frameworks.
Any ideas people ?
weepy
its really cool effects…
When the bug http://dev.rubyonrails.org/ticket/6411 will be fix, is it for this new version ?
Thanks a lot for your staff !
@ Weepy: I did some minimal testing in Firefox (printing frame numbers via Firebug’s <code>console.log()</code>).
FYI: on my 1,83 GHz Intel Core Duo MacBook (2GB RAM), on Firefox 1.5, I get an occasional one up to two frame drop for 1 sec effects.
- tobie – why dont u publish ure ‘framework’ and we can all try it out
@Tom: _probably_ not, as this will increase the code size for a border case. But you can use em or % units to the same effect with the current beta!
I don’t see x-small, small, medium etc as a border case. In Bulletproof Web Design by Dan Cederholm, it is the recommended font-sizing method to accomodate IE6s inability to override specific font sizes. This is an accessability issue from what I can tell.
Just what I was looking for …thx !
Hi Thomas! Awesome, and thank you.
I’ve noticed that Morph is misinterpreting left/right style declarations regularly with absolutely positioned divs. For example, I’m morphing from class cover_content to class minimized:
.cover_content {
left:50%;
margin:0 0 0 -310px;
padding:10px;
position:absolute;
top:100px;
width:600px;
}
.minimized {
border:1px solid #fff;
font-size:12px;
height:25px;
right:-310px;
width:160px;
}
It’s refusing to adjust the element’s horizontal positioning, my guess is because there’s an explicit left value set in the first class and no left in the second. However, if I try to hack it back together by setting left to 100%, the animation moves the div about 200% to the right, then animates it back to 100%.
Anyways, I understand it’s a beta and thank you so much for everything you’ve done. Hope this helps you weed out some bugs.
SUPER SUPER COOL!
Awesome stuff. But why won’t this work?
new Effect.Morph(this.webmaster,{
style:’height:tall; color:#000; font-family:handsome; text-transform:ladies-man;’
});
Why is it in IE7, all of the effects that utilize opacity in their transition seem to display content in IE7 just a wee bit pixelated?
it seems as though the opacity gets defaulted to .999999, but i can’t seem to set that anywhere…
Why don’t you guys check out; jQuery(http://jquery.com) and interface(http://interface.eyecon.ro/) as ann alternative to prototype and scriptaculous. They so lightweight and alot quicker to use than prototype. you can use css and xpath selectors to get elements. it is very extensible and for morphing effects check out this animate class/style plugin(http://www.paul.jquery.com/plugins/animateClass/)
Give it a try I converted to a while back its well ool
Thank you for making the future. 2007 and 2008 will be beautiful because of your work.
I’ve just been creating a comparision between the frame rates of scriptaculous and interface.
http://labs.parkerfox.co.uk/scriptaculous-vs-interface/
Interface appears to be a fair bit smoother (on both FF2 and IE7), though I may have badly ported the demo to scriptaculous.
Any one have any ideas for the difference or other comparisons?
I can’t get Morph to work properly. Even when I run the test program strait from the download folder, the Morph test fails. Anyone else getting this?
Thanks for this!
I did not see any unit tests for this new effect in the download, am I missing something?
@Tim: IE7 filters (which is how the opacity of an element is currently set for IE) utilize DirectX. Fonts are smoothed using ClearType. The problem is DirectX doesn’t understand how ClearType works so any time you use a filter on an item, IE has to turn off font smoothing on the item so DirectX can do the filtering. In earlier betas of IE7, the browser would turn the font smoothing back if you there were no more filters on the item (for example, opacity started at .1 and went up to 1 and the filter was taken off as an opacity of 1.0 and no filter are the same thing). However the problem with that was when you did transitions the text would get jagged and then change back when the transition was done. Apparently the IE team thought that was too jarring and decided to just make it so if a filter was EVER applied to an item, that item would lose font smoothing as long as that page was open. That’s why your text looks jagged.
Yes this is fucking moronic on Microsoft’s part and I think its one of the most glaring problems in IE7 considering the number of sites that have an opacity set. If Microsoft wouldn’t use DirectX to do something that it honestly has no business doing, this would work just fine but hey I’m just the guy who has to work around it.
@ Ian: yes, you are missing something, unit tests are included.
I must be doing something wrong with Transform. I want to build a transformation in a for loop and then execute it all at once. (Currently, I’m just using a bunch of morphs)
The basic code is this:
// php5 is a class name assigned to a single span.
x=’php5′;
y=’opacity: .1′;
z=new Effect.Transform([],{duration: 3});
track = [{x:y}];
z.addTracks(track);
z.play();
In the for loop I actually build about 20 tracks but just hard coding it like this, I still get nothing.
Any idea what I may be doing wrong?
=C=
I think I just came in my pants.
I’m seriously drooling. I can’t wait to try this out.