Thomas Fuchs
Hi, I'm Thomas Fuchs. I'm the author of the script.aculo.us user interface JavaScript library, a member of the Prototype core team and a Ruby on Rails core alumnus. You're using my work every day, even if you're not aware of it (sounds creepy, I know!). Need JS foo? Hire me.

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).

Look at the demos first, then return here to get the nitty-gritty details!

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.

Post to Twitter Tweet This Post Post to Digg Digg This Post Post to Facebook Share on Facebook

44 comments »

JavaScript Rocks! Peformance ebook
Do you run a web site or web application? Do your users a favor, and grab our ebook on JavaScript performance. Profit from our knowledge gathered in over 15 years of working with the Web and JavaScript and make your sites ultra-fast and your users ultra-happy.

44 Comments on “script.aculo.us morph effects”

  1. 1 Tobie Langel said at 9:36 am on November 21st, 2006:

    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!

  2. 2 Tobie Langel said at 9:36 am on November 21st, 2006:

    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… ;-)

  3. 3 John Nunemaker said at 9:36 am on November 21st, 2006:

    Just read your comment about css class support. Guess I should have read all the comments before posting. :)

  4. 4 th0fu said at 9:36 am on November 21st, 2006:

    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.

  5. 5 John Nunemaker said at 9:36 am on November 21st, 2006:

    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.

  6. 6 Tobie Langel said at 9:36 am on November 21st, 2006:

    @ 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…

  7. 7 Tobie Langel said at 9:36 am on November 21st, 2006:

    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>

  8. 8 th0fu said at 9:36 am on November 21st, 2006:

    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…) :)

  9. 9 Chris said at 9:36 am on November 21st, 2006:

    I suppose you heard it a few times, but: You (and Scriptaculous) rock!

  10. 10 Pat said at 9:36 am on November 21st, 2006:

    the effects are really choppy, and i have a 1.0GHz G4, not some old machine. compared to moo.tools this is garbage.

  11. 11 th0fu said at 9:36 am on November 21st, 2006:

    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.

  12. 12 Amon said at 9:36 am on November 21st, 2006:

    It’s great feature!
    I am waiting for the stable release to implement into ProtoJax Framework :)
    Grats!

  13. 13 Jonas Schneider said at 9:36 am on November 21st, 2006:

    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!

  14. 14 Tobie said at 9:36 am on November 21st, 2006:

    @ Jonas: "there’s your patch"http://dev.rubyonrails.org/ticket/6674.

  15. 15 jay said at 9:36 am on November 21st, 2006:

    Could this be changed so just that text of a href with an ID could change?

  16. 16 Tobie Langel said at 9:36 am on November 21st, 2006:

    @ Jay: I’m not quite sure what you want to do, but wouldn’t this work:

    <pre><code>$(‘mylink’).href = ‘http://…’;</code></pre>

  17. 17 Bill S said at 9:36 am on November 21st, 2006:

    Looks good. What about cross browser drop shadows for divs?

  18. 18 siebejo said at 9:36 am on November 21st, 2006:

    Is it possible to morph background-image too?
    I’m not succeeding to morph the background image.

    Kind regards.

  19. 19 Andy said at 9:36 am on November 21st, 2006:

    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.

  20. 20 thinsoldier said at 9:36 am on November 21st, 2006:

    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.

  21. 21 Tobie Langel said at 9:36 am on November 21st, 2006:

    @ 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.

  22. 22 th0fu said at 9:36 am on November 21st, 2006:

    @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.

  23. 23 weepy said at 9:36 am on November 21st, 2006:

    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

  24. 24 Muhammed Asif Ali said at 9:36 am on November 21st, 2006:

    its really cool effects…

  25. 25 Ron said at 9:36 am on November 21st, 2006:

    When the bug http://dev.rubyonrails.org/ticket/6411 will be fix, is it for this new version ?
    Thanks a lot for your staff !

  26. 26 Tobie Langel said at 9:36 am on November 21st, 2006:

    @ 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.

  27. 27 weepy said at 9:36 am on November 21st, 2006:

    - tobie – why dont u publish ure ‘framework’ and we can all try it out :)

  28. 28 th0fu said at 9:36 am on November 21st, 2006:

    @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!

  29. 29 Bill DeVaul said at 9:36 am on November 21st, 2006:

    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.

  30. 30 Iansen said at 9:36 am on November 21st, 2006:

    Just what I was looking for …thx !

  31. 31 Flip Sasser said at 9:36 am on November 21st, 2006:

    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.

  32. 32 Eric said at 9:36 am on November 21st, 2006:

    SUPER SUPER COOL!

  33. 33 Andy Edinborough said at 9:36 am on November 21st, 2006:

    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;’
    });

  34. 34 tim said at 9:36 am on November 21st, 2006:

    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…

  35. 35 Joe said at 9:36 am on November 21st, 2006:

    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

  36. 36 Mathieu Jobin said at 9:36 am on November 21st, 2006:

    Thank you for making the future. 2007 and 2008 will be beautiful because of your work.

  37. 37 weepie said at 9:36 am on November 21st, 2006:

    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?

  38. 38 Seth said at 9:36 am on November 21st, 2006:

    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?

  39. 39 MaRmAR said at 9:36 am on November 21st, 2006:

    Thanks for this! :D

  40. 40 Ian Tyndall said at 9:36 am on November 21st, 2006:

    I did not see any unit tests for this new effect in the download, am I missing something?

  41. 41 Eric said at 9:36 am on November 21st, 2006:

    @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.

  42. 42 Tobie Langel said at 9:36 am on November 21st, 2006:

    @ Ian: yes, you are missing something, unit tests are included.

  43. 43 Cal Evans said at 9:36 am on November 21st, 2006:

    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=

  44. 44 Jai said at 9:36 am on November 21st, 2006:

    I think I just came in my pants.

    I’m seriously drooling. I can’t wait to try this out.