Animation with GWT

June 14th, 2008 Alex Moffat Posted in GWT | 8 Comments »

After our Google IO presentation this year Using GWT to build a high performance collaborative diagramming tool someone came up to me and asked how the animation that Blueprint provides when you add milestones to the map is implemented. The little movie below shows what the effect looks like.
Boxes come in from the left and move to their final position. Existing boxes shrink if needed to provide space for the new boxes. This post describes how it’s done and provides a simple demonstration implementation you can download.

The basic sequence of actions is simple.

Step one is to work out the desired final state of the UI. For the map that means that when something is added or removed the final positions and sizes of all of the objects on the map are calculated. Of course the final state could include other attributes such as color or font.

Step two is to create the animations needed to move from the current state to the final state. For the map each object may have to move and shrink or expand as new objects are added or removed. Objects are added in the top left of the map and move from that location to their final location, objects are removed by moving them to the top left before removing them from the map.

Step three is to execute the animations. This is done by calling an animate method on each animation at regular intervals until the animation decides it is finished. Each animation has a target duration for completing and keeps track of what fraction of this duration has elapsed so far. It uses this to work out what fraction of the transition from initial to final state it should have reached. It’s done this way rather than moving a pre-calculated fraction on each call because you can’t rely on animate being called regularly, other activity in the browser may increase the interval between calls.

To show the steps above in action I wrote an animation example using GWT that adds and removes blocks from a vertical stack, reducing their height if needed. Use the “Add block” button to add a block and the “Remove block” button to remove one. Blocks being added appear in the bottom right and move to their final position, shrinking if needed. Blocks being removed move from their starting location to the bottom right where they disappear. You can add a maximum of 10 blocks. Give it a go.

The code is written to compile with the GWT 1.5 release but does not use the 1.5 animation facilities. You can download the source for the animation example as a gziped tar file or as a zip file. The standard GWT package structure is used and the code has many comments. There are four classes and an interface. The EntryPoint class is Anim. This creates the UI and contains the logic that works out the target state and schedules the appropriate animations whenever the add or remove buttons are pressed. There is an interface Animation that defines the methods all animations must provide, and two implementations MoveWidgetAnimation and MoveAndResizeWidgetAnimation. Finally there is the AnimationEngine that executes the animations.

8 Responses to “Animation with GWT”

  1. Balint Krivan Says:

    Hi!

    Thanks for this!
    I’ve written a CompositeAnimation object, maybe somebody is interested in. With CompositeAnimation you can create an Animation compositing several other Animations.

    import java.util.ArrayList;
    import java.util.Iterator;

    import com.google.gwt.dom.client.Element;

    public class CompositeAnimation implements Animation {

    private ArrayList animations;
    private Element object = null;

    public CompositeAnimation(ArrayList anims) {
    animations = anims;

    for (Animation anim : anims) {
    if (object != null) {
    if (!object.equals(anim.getElement()))
    throw new Exception(
    “All of the animations should be attached to the same element!”);
    } else
    object = anim.getElement();
    }
    }

    @Override
    public void afterLastFrame() {
    // do nothing.
    }

    @Override
    public boolean animateOneFrame() {
    for (Iterator entries = animations.iterator(); entries
    .hasNext();) {
    Animation anim = entries.next();

    if (anim.animateOneFrame()) {
    // This animation is completed so remove it.
    anim.afterLastFrame();
    entries.remove();
    }
    }
    return animations.size() == 0;
    }

    @Override
    public boolean beforeFirstFrame() {
    for (Iterator entries = animations.iterator(); entries
    .hasNext();) {
    Animation anim = entries.next();
    if (anim.beforeFirstFrame()) {
    entries.remove();
    }
    }
    return animations.size() == 0;
    }

    @Override
    public Element getElement() {
    return object;
    }

    }

  2. He Alex,

    This post showed up when I was looking for some animation examples ;)

    Anyway: I looked at your code and the problem I am having with this kind of code is that your client code has to be aware of the animation that takes place and that the widget might still be there after you remove it. I don’t like this, and it makes my code too fragil. It works nice in small examples, the ones I also see in mygwt (gxt), smartgwt, etc… but in more complex situations it doesn’t work.
    Example: You remove a widget from some panel and you inform the subscribers about the removal. The will check the removal and will do something. They will not do anything if your are lucky as the animation caused that it’s still there… This also can happen with an insert action and leads to really strange behavior and bugs (as it also depends on the duraction of your animation if the bug does show himself).
    My goal: make it transparent, which means that the removal/insert/add action will take effect immediately..
    With the insert, this is easy, but with the remove this is a bit more difficult, as you have to remove the widget after the effect.
    You can realize that in two ways:
    1) perform a logical removal but not Physical, such that it stays on the dom. You can do that by wrapping the widget element in a new element and inserting it on the dom just before the original widget. You then perform the (Logical and Physical) removal in the panel through GWT, such that it will be removed directly to conform the direct removal requirement but it will stay on the dom through the wrapper, that you use to perform the effect. At the end of the effect you simple remove the wrapped element from the dom. This works fine.
    2) I use so called InsertPanelWrapper that wraps a gwt panel, like a flow/vertical/horizontal panel, but has a standard api to the user (also because of the lack of some gwt interfaces like the InsertPanel interface that is in the ticket system for a long time). Anyway: you can inject an effect handler that will take care of the effect on the CRUD actions and because of the wrapper you can also correct the general methods like getWidget(), indexOf(Widget), etc… as the implementation can easily check if an effect is still busy and correct the return value..such that the getWidgetCount() will return the correct value…
    I prefer 1), but you have to be careful as you are directly working on the DOM that can cause memory leaks if not done correctly…

    What are your thoughts about this?

    Just my 50 cents,
    Ed

  3. BTW: please send me an email when you react on my post as I don’t get a notification of this.

    Ed

  4. Alex Moffat Says:

    Well, you’ve certainly thought about this more than I have. Until you mentioned it I hadn’t considered the issue you describe. I agree that solution 1 is the preferred way. However, I think in many cases it would be better to avoid the problem if possible by structuring the code so that the UI modifications are done so that they don’t trigger further changes. You’ve probably thought of that though :( Certainly when you’re removing items and doing it by index you’d have to find all the items you wanted to operate on first and the perform all the animations afterwards.

  5. He Alex,

    > the index remark…
    Yep, that’s what I do in my InsertPanelWrapper implementation.
    Example: InsertPanelWrapper extends the GWT IndexedPanel interface, so when calling getWidget(index), I first retrieve the widget from the panel and then check if it concerns a running (or queued, depending on the implementation) action animation. If so, and it concerns a remove action, I have to return null. If it concerns an insert/add action, I will simple return the widget as the insert/add action is always done immediately. Also when queuing insert/add animation actions I perform them immediately and make their css display none.
    It’s a bit of code but all very simple. It works nice and transparent.
    The first solution is a bit more tricky and I only played with it… I didn’t got it working for a vertical/horizontal panel so I stopped with it….

  6. He again,

    … Talking about it triggered me to play with solution 1 a bit again… (with the experience I have now)…
    I got solution working now as well ;)
    Why it didn’t worked with vertical/horizontal panel before: I insert a new div element just before the widget that needs a animated removal and then remove it… However, in case of a vertical panel gwt also removed the tr and td elements that are direct parents of the widget. Because my new div element is attached to the td parent, it also got removed again.. ;) ….
    So … I think I am gonne use this solution as I find it a bit more elegant and straight forward…

  7. Hi Alex,

    I am using solution 1 and it works fine.
    Btw, about your remark:
    > However, I think in many cases it would be better to avoid
    > the problem if possible by structuring the code so that the
    > UI modifications are done so that they don’t trigger
    > further changes.

    I must disagree with you on this because:

    1) The solution to prevent it is simple and straigtforward and you can gain a lot with it: a fragil situation. So I would say: go for it.
    I mean: I could maybe prevent it but the next developer next year might not fully understand it and will create bugs.

    2) In my situation it’s simple not always possible to realize this. Example: it’s almost the same example as your example during the Google I/O. I have an add button that will add entries in a panel. The entry itself has a remove button on the right and informs his presenter about the removal. The presenter manages all entries. When the remove button is pressed, informs the presenter that will remove it through animation. After removal the presenter will check if there only one entry left, if so, it has to perform some special actions like showing a text. If I use animation I have to take extra care as the presenter will always count 2 entries instead of 1 as the entry is being removed through animation.
    Of course I can solve this locally, but I want that this component isn’t aware of any animation, and works just as good without/with animation.
    Imagine a new developer comes a long and makes something similar and forgets to take care of any animation. This will create strange bugs as they are very hard to find, due to the timing behavior of the animation……

    Just my experience…

  8. Alex Moffat Says:

    I agree that solution 1 is a very useful approach. I’d still prefer, if possible, to drive the ui appearance from the model though. So, in your example, I’d have whether or not the text appeared depend on the number of entries in the model rather than on the number displayed. This would mean that you’d have both the text and the animating entry visible at the same time but perhaps this is OK? Anyway, this has certainly given me lots to think about, I’d not considered the “intermediate” state of display while the animation is in progress. Maybe I should take another look at the animation in the google i/o example and see if it is vulnerable to the issues you describe, and if so why, or why not.

Leave a Reply