Mastering the Tao of Personal Computing

Great maxim to follow: From reading the code, one should be able to tell what the code does, but also why it does it. npacemo's twitter

Flex 3 Animated Scrolling (a.k.a. Smooth Scroll)

Oct 24th 2009
12 Comments
respond
trackback

Almost year and a half ago I’ve wrote an article about making a pretty custom scroll-bar with a Fixed Sized Scroll-Thumb - a task known in the community as a designer scroll-bar. Both blog posts proved to be useful, but just recently I received an email from this very cool guy Dino from Canada with the following question.

Dino wrote:
I was also wondering how would I go about adding easing to the scrolling of a VBox? I don’t think there is an out-of-box solution such as an Accordion having a style property openEasingFunction that allows you to specify easing but I could be wrong. Does this require customization of the Container class? I was looking at Container.scrollChildren() and other methods in the Container class to determine if I could override any behaviour.

Somehow his email coincided with a scroll-related task I had recently. I had to use a scroll-bar which is situated on a pretty custom position and it was strolling columns of content with a snapping effect. So, how would I approach the problem with the animated scroll-bar?

The Solution

Going for extending the Container or some of the others Flex containers is a hairy and let’s say adventurous direction to proceed with. And I believe the solution won’t be much flexible. What would you do if you need to place the scroll-bar on a more peculiar place (e.g. on the left or the top side of the container sizes or event somewhere else outside the scrolled container)?

The solution is to detach the scroll-bar from the container, so we can use an outside scroll-bar. The following code fragment shows you how to achieve this:

<!-- 1. Place the View that needs scrolling into a Canvas which represents the visible area
     which can't be exceeded by the View.
     !!!IMPORTANT!!! Disable the scrolling of the Canvas, so we can use a detached scroller,
     which we can place wherever we want (e.g. on the left or top sides of the Canvas) -->
<mx:Canvas id="viewContainer"
    width="{largerView.width}" height="500"
    verticalScrollPolicy="off" horizontalScrollPolicy="off"
    borderColor="black" borderStyle="solid">
    <view:ViewThatNeedsScroll id="largerView"/> <!-- THIS IS THE VIEW -->
</mx:Canvas>

<!-- 2. Create a Canvas with fictive content - this is the actual detatched scroll-bar.
     Make sure the dimensions of this Canvas are corresponding to the size of the visible
     area specified by the View-container. Also make sure the fictive content has dimensions
     corresponding to the the dimensions of the View.
     Use data binding to set the corresponding dimensions! -->
<mx:Canvas id="scrollerContainer"
    x="{viewContainer.x - scrollerContainer.width}"
    y="{viewContainer.y}"
    width="40" height="{viewContainer.height}">
    <mx:Canvas width="1" height="{largerView.height}"/> <!-- THIS IS FAKE CONTENT -->
</mx:Canvas>

The Simplest Way to Attach the Scroll-Bar to the View

Before trying to add a tween and an easing to the scrolling motion, I’ll show you the most simplest solution for attaching this outer scroll-bar to the View that needs to be scrolled:

<!-- 3. Use Data Binding to attach the scroller to the View: -->
<mx:Binding source="scrollerContainer.verticalScrollPosition"
    destination="viewContainer.verticalScrollPosition"/>

Here’s the working sample (view source is enabled):

Animating the Scrolling

I’ve implemented a simple controller component which animates the scrolling movement. You can adjust various properties of the ScrollerController, such as:

● interaction delay
● easing function
● max duration of the tween
● etc.

I’m not going to look at the controller’s implementation in details. Feel free to ask me any questions in the comments section.

Here’s the working sample (view source is enabled):

Tell me what do you think of my solution? How would you solve this problem?


This post is tagged , , , , ,

12 Comments

  1. Grey

    Hi, is there a Solution with an MouseWheel?

  2. Daniel Summers

    Dude this great! thanks for the post!!

  3. Brian

    Thats class, good man.

  4. Hi

    I found a different approach for the same effect (much shorter but maybe not better) .

    Add a listener to the scrollContainer:

    scrollerContainer..addEventListener(ScrollEvent.SCROLL, onCanvasScroll);

    And in that listener use a Caurina tween:

    Tweener.addTween(viewContainer, {verticalScrollPosition:scrollerContainer.verticalScrollPosition, time:0.7, transition:”linear”});

    And thats it. Will put a demo on my blog if anyones interested.

    Ported to Caurina/Flex from a Flash version on http://www.soundstep.com/blog/experiments/scrollpane/

  5. Jami

    Can’t make a component out of this. This one’s better: http://www.soundstep.com/blog/2008/09/09/tween-scroll-movement/

  6. Vladimir Tsvetkov

    @Jami I won’t even bother explaining you how my solution works and why it’s designed in this way.

    Let’s say I’m a better programmer than you and you’ll never wrap your head around this.

    Read this when you have some time:
    What’s wrong with the creative community of Flash?

  7. daslicht

    Any Solution for the MouseWheel ?

  8. daslicht

    I have another approch but it also suffers from bad mousewheel support:
    http://www.wensauer.info/flex/SmoothScroller/SmoothScroller.html

    Any idea how to solve this better ? please

  9. Vladimir Tsvetkov

    Your solutions is in the right direction. The interaction feels clumsy, because it seems that each notch of the wheel probably is scrolling to many lines at a time.

    I can see you’re moving with a whole page each time. Probably you should move proportionally to the value of the delta.

  10. Gourav

    Hi,

    Thats very cool man, can u tell me is it generic, i mean if i need horizontal List then what i have to do.

    Plz reply
    Thanks in advance

  11. Vladimir Tsvetkov

    Hi Gourav,

    The solution is pretty generic. I believe all you need to do is to adjust the scrollerContainer so that it can accommodate a horizontal scroller.

    Then you need to change the configuration of the ScrollerController in the following way:

    animatedProperty=”horizontalScrollPosition”

    and:

    scrollPosition=”{scrollerContainer.horizontalScrollPosition}”

    Let me know if you managed to get it working!

Leave a Reply

STOP SOPA

Imagine a World Without Free Knowledge

Right now, the U.S. Congress is considering legislation that could fatally damage the free and open Internet. To raise awareness, I'm blacking out my blog.