Home > 3D, Animation, Custom Layout, Spark Layouts > Two Examples of Layout Animations

Two Examples of Layout Animations

April 11th, 2010 Leave a comment Go to comments

layout-animation A few folks have been asking for the source of my Creating Custom Layouts in Flex 4 talk at the Flash Camp Boston event, and I finally found the time to post it here. My apologies for the delay. Click on the image to go to the demo, right-click and select “view source” to get to the goods. This is pretty much my old WheelLayout demo, but I added a couple of animations  to make things a little bit slicker:

  1. Clicking on any image in the WheelLayout mode will rotate all the elements until the selected item is centered in front. In this animation the layout runs on every frame of the animation.
  2. Selecting different layouts from the drop-down will animate the items’ transformation. In this animation the layout is disabled and is not running during the animation.

Animating the Scroll Position

In the first animation I want the selected item to come “in view”. In the WheelLayout I’ve already implemented the standard API that calculates the scroll distance to an item – getScrollPositionDeltaToElement(). Now in the item renderer of the list – FlickrThumbnail.mxml – I detect when the item has been clicked and then create an animation for the scroll position of the parent Group:

protected function itemrenderer1_clickHandler(event:MouseEvent):void
{
    var g:GroupBase = parent as GroupBase;
    var p:Point = g.layout.getScrollPositionDeltaToElement(this.itemIndex);
    if (p)
    {
        var startX:Number = g.horizontalScrollPosition;
        var startY:Number = g.verticalScrollPosition;
        var anim:Animate = new Animate();
        anim.motionPaths = new [
            new SimpleMotionPath("horizontalScrollPosition", startX, startX + p.x, 500),
            new SimpleMotionPath("verticalScrollPosition", startY, startY + p.y, 500)
        ];
 
        var interpolator:NumberInterpolatorWrapping =
			new NumberInterpolatorWrapping(0, g.contentWidth - g.width);
        var scrollLength:Number = interpolator.getLength(startX, startX + p.x);
        anim.interpolator = interpolator;
        anim.duration = Math.max(550, Math.min(2500, scrollLength * 2));
 
        anim.play([g]);
    }
}

One interesting thing to note is that because of the circular nature of the WheelLayout I wanted the animation to interpolate between the values in a wrap-around manner. For example scrolling from the last element to the first element shouldn’t scroll through any other elements since the first and last elements are visually next to each other. For that reason I implemented a custom interpolator – NumberInterpolatorWrapping.as.

Animating Items between Different Layouts

In this animation I wanted all the elements to move and rotate smoothly when I switch between various layouts. In this animation the start position is defined by the current layout and the end position is defined by the new layout. In-between, while the elements are being animated, they really don’t belong to any layout. For that reason, I disable the layout of the container for the duration of the animation using the autoLayout property of the parent Group.

For convenience I transition between the layouts using states:

<s:states>
	<s:State name="tile"/>
	<s:State name="wheel"/>
	<s:State name="vertical"/>
	<s:State name="horizontal"/>
</s:states>
 
<s:List width="100%" height="100%"
		dataProvider="{photoFeed}"
		itemRenderer="FlickrThumbnail"
		id="theList" useVirtualLayout="false">
 
	<s:layout.vertical>
		<s:VerticalLayout horizontalAlign="center"/>
	</s:layout.vertical>
	<s:layout.horizontal>
		<s:HorizontalLayout verticalAlign="middle"/>
	</s:layout.horizontal>
	<s:layout.tile>
		<s:TileLayout horizontalAlign="center" verticalAlign="bottom"
                                      columnWidth="112" rowHeight="132"
				      requestedColumnCount="5"/>
	</s:layout.tile>
	<s:layout.wheel>
		<my:WheelLayout gap="20" axisAngle="{axisSlider.value}"
		verticalCenterOffset="{offsetSlider.value}"/>
	</s:layout.wheel>
</s:List>

Because the elements could be both moved and/or rotated in 2D or 3D, I use a parallel of both effects:

<fx:Declarations>
    <s:Parallel id="myEffect" effectEnd="myEffect_effectEndHandler(event)">
        <s:Move3D applyChangesPostLayout="false"/>
        <s:Rotate3D applyChangesPostLayout="false"/>
    </s:Parallel>
</fx:Declarations>

And finally, when I switch from one layout state to another, I want to run the effect. Now I need to perform the following steps:

  1. Make the effect target all of the elements.
  2. Capture the start values (the position and rotation defined by the old layout).
  3. Switch the layout (by changing the state) and call validateNow() so that all elements are positioned and/or rotated by the new layout.
  4. Turn off the autoLayout property so that the new layout doesn’t interfere with the running effect.
  5. Run the effect. Note that at this point the effect is going to capture the animation end values – the position/rotation defined by the new layout. I’ve already captured the start values at step 2, so I’m all set.
  6. When the effect is done, turn the layout back on.
private function animateTo(toState:String):void
{
    // Make sure any previous animation is stopped.
    if (myEffect.isPlaying)
        myEffect.stop();
 
    // Add targets
    myEffect.targets = new Array();
    for (var i:int = 0; i < theList.dataGroup.numElements; i++)
        myEffect.targets.push(theList.dataGroup.getElementAt(i));
 
    // Create Transition for all elements
    myEffect.captureStartValues();
 
    // Go to the end state
    var fromState:String = this.currentState;
    setCurrentState(toState);
 
    // Validate everything before turning off the layout
    theList.validateNow();
 
    // Turn the layout off before running the effect
    theList.dataGroup.autoLayout = false;
 
    // Play the effect
    myEffect.play();
}
 
protected function myEffect_effectEndHandler(event:EffectEvent):void
{
    theList.dataGroup.autoLayout = true;
}

Note that my demo has one limitation – I needed to turn off virtual layout so that everything works fine. If I have virtual layout running, I’m not guaranteed that the old and new layouts will assign the items to the same item renderers.
Click on the image at the top to go to the demo, where you can select “view source” from the right-click menu to get the code.

  1. Djam
    April 12th, 2010 at 15:06 | #1

    Thanks, I was trying to solve the exact same behavior.

    I have a question though: is it considered a bad practice if a layout changes the size of elements? For example, in your example if in the horizontal state all the items should be stretched vertical, what is the best approach to do that? Should the layout stretch elements or it is the container who should do that and let the layout deal with resized elements?

  2. Evtim
    April 12th, 2010 at 23:31 | #2

    Hi Djam,

    The layouts determine the actual size and position/transformation for all of the elements (based on any user settings + preferred/measured sizes of the ILayoutElement and the container width/height). The Spark containers delegate all of this logic to the layouts and don’t resize or position the elements.

  3. Mario
    May 4th, 2010 at 04:55 | #3

    Hi Evtim,

    fantastic work, well done.

    One suggestion: in terms of usability of the wheel, I thought it would be nice if the user could rotate it not only from the scrollbar but also by grabbing (click and hold) an item and moving the mouse left or right.

    Best, Mario

    • Evtim
      June 2nd, 2010 at 00:03 | #4

      Hi Mario,

      Interesting idea – basically adding sort of touch interaction? I may give it a try when I find the time.

      Thanks,
      Evtim

  4. B
    June 1st, 2010 at 00:56 | #5

    Nice work 🙂

    Is there any special license on this work? Or could I modify it and include it in my program as long as I credit you?

    Thanks again!

    • Evtim
      June 1st, 2010 at 23:59 | #6

      Yeah, everything on this blog, unless explicitly stated otherwise, is licensed under Creative Common Attribution 3.0 license – there’s a link on the right hand pane of the blog. So yeah, you can modify and use in your software as long as you credit me for the original work only. Cheers!

  5. June 7th, 2010 at 08:38 | #7

    Why don’t you use instead of changing yourself the state and playing an effect ?

  6. June 7th, 2010 at 08:40 | #8

    tom flex :
    Why don’t you use instead of changing yourself the state and playing an effect ?

    sorry :
    Why don’t you use S:TRANSITION instead of changing yourself the state and playing an effect ?

    • Evtim
      September 18th, 2010 at 11:21 | #9

      Hi Tom,

      I didn’t use a Transition since I need to add the targets – the layout elements – dynamically. Maybe all of this can be summarized into a new Transition sub-class – a LayoutTransition.

  7. Gene
    June 16th, 2010 at 07:54 | #10

    @Evtim

    Very very good work of your WheelLayout. Regarding to usability of the wheel, please also refer to the effect of the following link:

    http://theflashblog.com/flash/clickcarousel.swf,

    but that is not an open source work. Please check.

    Thanks.

    • Evtim
      September 18th, 2010 at 11:27 | #11

      Thanks Gene,
      With this example I was aiming to show how the layout logic is isolated in the layout class and all the interaction and animations can very easily go into the application. Modifying the app to do this kind of scrolling and zoom on click should be fairly straightforward. Probably listen for MOUSE_MOVE on the List and use the mouse coordinates to do horizontal scrolling. You can see how clicking on an item already sets up an animation to scroll to that item.

  8. Gene
    June 16th, 2010 at 08:01 | #12

    By the way, does WheelLayout have the limitation for image file size? it seems that it will not load the image with a large size? Thanks for your response.

    • Evtim
      September 18th, 2010 at 11:31 | #13

      This is just how I coded the application sample, it’s not a limitation of the WheelLayout itself. Take a look at the FlickrThumbnail.mxml – at the bottom the Image binds to the URL of the thumbnail. You can change that to bind to the full size image, or perhaps introduce states in the renderer and when clicked then switch to the full-size image.

  9. BreizoReva
    July 27th, 2010 at 07:30 | #14

    Hi Evtim,
    Great example with impressive visual effect.
    I have one simple question: how do you manage the addition or deletion of elements to the list? I.e. how can a new element be animated to get in and how can a deleted element be animated out?
    In Flex 3 there was the itemsChangeEffect property, see here:
    http://livedocs.adobe.com/flex/3/html/help.html?content=createeffects_5.html

    Any clue as to how this could be achieved with the spark architecture?
    Thx

    • Evtim
      September 18th, 2010 at 11:45 | #15

      This is a good question. Spark doesn’t currently support data effects – adding/removing/changing items. Off the top of my head – for non-virtualized containers I think it should be possible to code your own solution, similar to the one I’ve shown here. For example, the first “state” would be before the element is added and the second “state” would be after it’s been added. If an element exists only in the second state, then add it as a target to some “appear” effect, otherwise, “disappear” effect and for all the rest of the elements use the effects similarly to the way I had them. Not sure how exactly this will work, but I’ll try to code something up when I find the time.

  10. September 7th, 2010 at 16:56 | #16

    Love the work.

    I am working on a project at the moment and we have committed to the Flex SDK 4 and the Spark architecture. We have been using Flash Builder 4 since the first release and have grown and learnt how to use this great new approach to the skinning and component architecture.

    It is a world away from my years with flash only development (continually making my own frameworks – if they could even be called that) and am feeling like I am now learning the correct way to tackle/plan/develop a large project.

    BUT… I have spent many, many hours now trying to get the site we are developing to look and feel like flash and not like a data-driven RIA that is used for managing business logic and processes. I am really feeling like I have come all this way and am now unable to deliver (a cool looking site with finesse and easing, etc).

    Evtim, can you give me some reassurance that this is not to case please. For example, I would like to make the itemRenderers in a list ease into position (or a group with a flex/custom layout) like this example: http://www.fwaphoto.com/. I have been struggling with this for some time and need help from someone who has answers to these questions.

    Feel free to email me directly.

    Thanks

    • Evtim
      September 18th, 2010 at 12:26 | #17

      Hi Hudson,
      The effect in the link you provided should be possible. Take a look at the sources in my example, in FlickrThumbnail.mxml, the click handler at the top. When an item is clicked, I calculate how much to scroll the container to center that item by using the layout’s getScrollPositionDeltaToElement(). Then I create an animation to scroll to that position. I imagine you’d want to use similar code – when the user interaction causes you to change scroll position in your container, then you calculate how much you’ll scroll and create an animation to do the scrolling for you with the necessary easing, etc. Does that help?

  11. December 8th, 2010 at 10:30 | #18

    Hi Evtim,
    No ones commented in a while ( you must be enjoy being stress free :P), i have just recently discovered Flex and i enjoy your examples a lot but im not trying to be as fancy as you for my first attempt – i am just trying to get the wheel to populate based on images stored locally in a folder, i have edited some of the code but my experience is low and i keep getting errors. If you get a chance to point me in the right direction as to what i need to modify that would be great help.

    Thanks

    • Evtim
      April 19th, 2011 at 20:04 | #19

      @Philo
      Hi Philo,
      If you want to load images from disk, take a look at FlickrThumbnail.mxml – this is the Item Renderer source. You see the image is being loaded by the mx:Image component, through its source property like this “source=”{data.thumbnail.url}””. Modify that line to something like “data.imagePath” and then modify your dataProvider to be an ArrayCollection of Objects, one Object per image, and each Object having a property “imagePath” of type String that is set to the path to the image. Hope that helps.

  12. Bula
    December 18th, 2010 at 14:07 | #20

    @Evtim
    Hi Evtim, thank you for your patience and great examples youre putting out there. Regarding animating the addition or deletion of elements to the list: I was trying to put up a custom layout for this but the problem is that I cannot prevent container (DataGroup) from refreshing only until after all effects are done. It is possible to animate when dataProvider doesnt change ie. only visibility like in filtering example here http://coenraets.org/blog/2010/02/flexstore-revisited-building-an-animated-spark-layout/ When elements are actually added (or deleted) autoLayout=false does not prevent update of the layout and ending of all effects.

    • Evtim
      April 19th, 2011 at 19:42 | #21

      @Bula
      Hi Bula, sorry took so long to reply. I think this might be a bug in DataGroup’s updateDisplayList(), I just gave it a quick look and I see that we don’t check the “autoLayout” flag in there… If that’s the problem indeed, we should get this logged – https://bugs.adobe.com/flex/
      A work-around would be to subclass DataGroup, override updateDisplayList() and if “autoLayout” is false, return, else call the super.updateDisplayList().

  13. Bruno
    January 20th, 2011 at 04:56 | #22

    Hi Evtim,

    Very nice work with this one! 🙂

    I would like to do something a little more, like add one image to the center of the wheel, like the image that was rated as the best one in a period of time, one week, for example.
    Can this be done easly ? This only for the use of the wheellayout, changing to on of the others, is not import…

    Just add one image to the center…

    Thank you,

    Bruno

  14. Bruno
    January 20th, 2011 at 05:14 | #23

    more one thing, the image can be on two positions, at the middle when viewing the imagens on a 2D perpective by rotating the axis to the depth plane. OR above the others when viewing the images on 3D with the axis on a Vertical plane …

    Thanks,

    Bruno

    • Evtim
      April 19th, 2011 at 19:49 | #24

      @Bruno
      Hi Bruno,
      I’m not exactly sure what you mean by “in the middle”, but I think it should be possible. I imagine that you can add additional property on the WheelLayout – like index of the item to be specially treated. Then in updateDisplayList() you’ll layout that item in the special way you describe. From your application you’ll set/update that property on the WheelLayout and the layout would need to invalidate and re-layout – take a look at how the rest of the WheelLayout properties do the invalidations.

  15. Tim
    August 9th, 2011 at 10:45 | #25

    Hi Evtim,

    Thank you for posting these examples and the presentation you gave on custom Spark layouts. They’ve been invaluable in helping me understand the concepts involved.

    There are many different ways that examples are laying out their elements in 3D space. I noticed you’re using setLayoutMatrix3D(). Would you consider this the best/correct way to do so in a custom layout?

    Thanks,

    Tim

    • Evtim
      August 9th, 2011 at 22:41 | #26

      Hi Tim,

      This is a very good question! With custom layouts (or even custom layout code withing components), usually the most common pitfall is to cause unnecessary invalidations. For example a simple 2D layout should use setLayoutBoundsPosition() instead of setting the x/y properties. Setting x/y will actually invalidate properties, size and display list which will cause the layout to run again – inefficient (and sometimes could even lead to infinite loops). The same rule applies to 3D layouts – don’t set properties like x, y, z, rotationX, rotationY, transform, transform.matrix3D, etc. from within the layout’s updateDisplayList() method. Instead, the ILayoutElement interface should be used. For the 3D case in particular, there are two equally correct choices – ILayoutElement::setLayoutMatrix3D() and ILayoutElement::transformAround(). Just make sure you’re passing “false” to the “invalidateLayout” parameter.

      Cheers,
      Evtim

  16. Tim
    August 16th, 2011 at 04:16 | #27

    Thanks again Evtim – this is all solid gold advice!
    @Evtim

  17. Yang
    November 16th, 2011 at 21:23 | #28

    Hi Evtim,
    Thanks for sharing so much cool tips about layouts.
    Are there any scenarios where positioning elements in the component class is appropriate?
    For example in a tab bar component I created, I allow the user to click and drag the tab bar buttons to reorder the tabs (like Chrome). Therefore the positions of the tabs depend on user interaction (drag and drop) which should be the responsibility of the component class. Now if I were to move the positioning code to the layout class, I would need to either reference the layout class directly from the component class or use events to tell the layout class the desired position of an element (based on mouseX) and when to change the positions. Your thoughts?

    Regards,
    Yang

  18. Tim
    November 17th, 2011 at 10:55 | #29

    Hi Evtim,

    Is there a simple way that a layout can determine whether its target is a ListBase (or descendant) so that it can automatically deal with selection?

    Thanks,
    Tim

  19. Tim
    November 17th, 2011 at 10:57 | #30

    p.s. When I said descendant, I meant subclass.

  20. alias
    March 19th, 2012 at 02:40 | #31

    Hi, this is very nice, can u help me. all i need is just one more button and on click button i want all the images to be resized little bigger…please help me……

  21. September 9th, 2017 at 12:12 | #32

    With havin so much content do you ever run intro any problems
    of plagorism or copyright violation? My blog has a lot of exclusive content I’ve either authored myself or outsourced
    but it seems a lot of itt is popping it up all over the internet
    without my agreement. Do you kknow any solutions tto herlp
    stop content from being ripped off? I’d really appreciate it.

  1. No trackbacks yet.