jul
29
The PanScrollingCanvas is a Flex container that scrolls its children on mouse move. Fully skinnable buttons appear when hovering over the container. The behavior of those buttons can be customized on the client side, by listening to events that this container will fire on button rollover, rollout, and click.
This component is inspired from Doug McCune’s ButtonScrollingCanvas, which is part of flexlib. Using Doug’s component, users interact with one of the four buttons laid out over the container, in order to initiate a scrolling effect–in the direction indicated by the button. I wanted to do something similar, but without relying on the buttons for scrolling the content. Here, the interaction consists of panning the mouse over the container, with a goal-based ease out to give an organic feel to it. Also, 8 directional buttons (instead of 4) are introduced for control affordance and client custom behavior.
Let’s jump to a demo, before getting to the nitty gritty…
Example: exploring Yahoo! maps
To see a full page demo (with source view), click here.
In the above example, the zoom buttons (in the center) have been added by the client application, and their visibility is synchronized through the PanScrollingEvent.CONTROLS_VISIBLE_CHANGE event.
Brief Notes
Syntax
The <containers:PanScrollingPan> tag inherits all the tag attributes of its superclass mx.containers.Canvas and adds the following tag attributes:
<containers:PanScrollingCanvas
Properties
buttonsAlpha="1.0"
buttonsEnabled="true"
buttonsVisible="false"
explicitButtonLength="NaN"
panningEaseFactor="0.08"
panningRefreshRate="10"
panningThreshold="1.5"
Styles
buttonThickness="20"
eastButtonStyleName="eastButton"
mouseScrollPadding="20"
northButtonStyleName="northButton"
northEastButtonStyleName="northEastButton"
northWestButtonStyleName="northWestButton"
southButtonStyleName="southButton"
southEastButtonStyleName="southEastButton"
southWestButtonStyleName="southWestButton"
westButtonStyleName="westButton"
Events
panScrollingButtonClick="No default"
panScrollingButtonRollOut="No default"
panScrollingButtonRollOver="No default"
panScrollingClick="No default"
/>
Properties
- buttonsAlpha : Number
Indicates the alpha transparency value of the eight control buttons located around the canvas. - buttonsEnabled : Boolean
Whether the eight control buttons can accept user interaction. - buttonsVisible : Boolean
Controls the visibility of the eight control buttons.This property is controlled by the component. But in some cases, the client may need to manually set the visibility. (See example’s source code.)
- explicitButtonLength : Number
If set, determines the length of the North, East, South, and West control buttons, that is North/South button’s width, and East/West button’s height, not to exceed the length they would have when the property is not set. If not set, or set back toNaN, the button length will be determined by the size of the canvas and thebuttonThicknessproperty. - panningEaseFactor : Number
Regulates the ease out during the panning. As mouse locations determine scrolling goals, this parameter tells the component what portion of the goal to scroll to, each time the pan clock refreshes. This is a ratio between 0 and 1, not inclusive.Adjust this property to get the desired pan scrolling feel, based on the relative sizes of the canvas and its scrollable content.
- panningRefreshRate : Number
Pan clock tick, in milliseconds. - panningThreshold : Number
Amount of pixels by which a scrolling goal is deemed reached. Should be a non-zero positive number.
Methods
- startPanScrolling():void
Starts the pan clock, which in turn activates the goal-based automatic scrolling. - stopPanScrolling():void
Stops the pan clock, which stops the automatic scrolling.
Events
- controlsVisibleChange
dda.events.PanScrollingEvent.CONTROLS_VISIBLE_CHANGE
Dispatched when the control buttons changed their visibility state. - panScrollingButtonClick
dda.events.PanScrollingEvent.PAN_SCROLLING_BUTTON_CLICK
Dispatched when a control button has been clicked. Seedataproperty for information about the button index. - panScrollingButtonRollOut
dda.events.PanScrollingEvent.PAN_SCROLLING_BUTTON_ROLL_OUT
Dispatched when the user rolls over a control button. Seedataproperty for information about the button index. - panScrollingButtonRollOver
dda.events.PanScrollingEvent.PAN_SCROLLING_BUTTON_ROLL_OVER
Dispatched when the user rolls out of a control button. Seedataproperty for information about the button index. - panScrollingClick
dda.events.PanScrollingEvent.PAN_SCROLLING_CLICK
Dispatched when the user clicks on the canvas. SeelocalXandlocalYproperties for the mouse location, relative to the top left corner of the canvas.
Styles
- buttonThickness Type: Number
Specifies the thickness of the 8 control buttons. - mouseScrollPadding Type:Number
Specifies the amount of padding from the canvas edges to be excluded from the area that is used for determining scrolling goals with respect to mouse locations. By default it is set tobuttonThickness.A positive value allows the user to see the edges of the content without having to pan the mouse to the edges of the canvas.
- <ID>ButtonStyleName Type: String
Specifies the stylename for the corresponging button, where <ID> is one of {north, northEast, east, southEast, south, southWest, west, northWest}.
Public Constants
- BUTTON_NONE
:int = -1;
[static] - BUTTON_NORTH
:int = 0;
[static] The index value for the North control button. - BUTTON_NORTH_EAST
:int = 1;
[static] The index value for the North East control button. - BUTTON_EAST
:int = 2;
[static] The index value for the East control button. - BUTTON_SOUTH_EAST
:int = 3;
[static] The index value for the South East control button. - BUTTON_SOUTH
:int = 4;
[static] The index value for the South control button. - BUTTON_SOUTH_WEST
:int = 5;
[static] The index value for the South West control button. - BUTTON_WEST
:int = 6;
[static] The index value for the West control button. - BUTTON_NORTH_WEST
:int = 7;
[static] The index value for the North West control button.

Valerie
August 22nd, 2008 at 4:58 amHi Didier,
Is it possible to drag the map instead of roll over it ?
Thanks,
Valerie
didier
August 22nd, 2008 at 8:23 amDragging the map is a built-in feature in the Yahoo! Maps API. You can enable it by doing:
yahooMap.addPanControl();You can also check the source code of the examples at http://developer.yahoo.com/flash/maps/examples.html if you need more insight about the Yahoo! Maps API.
Just note that exploring maps was just an application of this Flex component, which should be considered an alternative to clicking and dragging. Mixing both might be very confusing. I’ll post more uses of mouse panning using this component.
Valerie
August 22nd, 2008 at 2:10 pmThanks for your answer.
Actually, I don’t use yahoo maps but svg maps that I load dynamically with Degrafa … And dragging the map is a feature I would like to implement in my project …
Best regards,
Valérie
newbie_ekol
September 18th, 2008 at 11:41 amhi I need some help with using your container in a project that I am doing right now…how do I use the container if I am not using your container for yahoo! maps? is there a tutorial that I can take which could help me out? I am a total newbie to flex and I know a lil bit of AS3
didier
September 18th, 2008 at 7:19 pmJust use the container as a
Canvas, and make sure its content is allowed to be bigger than the container itself, i.e. don’t constraint the content width and height to 100%. Otherwise, you will not get any scrolling. Refer to the Flex doc if you need more info about how to use containers.Kim
May 14th, 2009 at 9:39 pmI would also like to know how to use the PanScrollingCanvas for things other then the YahooMaps and I would appreciate any other usages and Ideas on how to code this to work with a canvas rather the the UIComponent as a newbie to flex and having one specific project I am hoping to get off the ground.
This component is seems very nice, but, I can not get things to work and any tutorial would also be great.
didier
May 19th, 2009 at 7:45 pmThe
PanScrollingCanvasis a container like any other Flex container. To make sure the scrolling activates, its content has to be bigger than its size–similarly to scrollbars. That’s all there is to it. The code is provided, its API described in the blog entry, and the code should be easy to understand once you learn about the Flex component lifecycle. (http://www.onflex.org/ACDS/BuildingAFlexComponent.pdf)Sonny
October 18th, 2009 at 4:06 amHi,
I have placed an image in the PanScrollingCanvas container.
The problem is that the image is not coming centered initially within the container.
Can you please propose a solution .
Thanks for the invaluable help.
didier
November 3rd, 2009 at 9:01 amThe
PanScrollingCanvasextendsCanvasso you can use theverticalCenterandhorizontalCenterwith a value of0to center the image.h.
December 11th, 2009 at 1:54 amHi, many thanks for your great component. One simple problem – I can’t find any way to completely hide the buttons – buttonsVisible = false not working.
didier
December 11th, 2009 at 9:14 amAs stated in the blog entry, the
buttonsVisibleproperty is controlled by the component. Setting the value totrueorfalsewill trigger a fade in/out of the controls. If the client changes the value of this property, it’s only temporary–until the mouse rolls over or out the component again. So thebuttonVisibleproperty cannot solve your problem, and there is no mechanism in the existing code that hides the buttons completely. But it’s just a few lines away: you can modify the code and add a boolean property that controls the desired behavior, and address the hiding of the controls in functionupdateButtonsDisplayState(), or simply not add the buttons as raw children increateChildren()if you never need them. Hope this help. Thanks!h.
December 11th, 2009 at 1:37 pmThanks for your fast replay. I made exactly the same hack you suggest
. However, direct support of this feature would be very useful and it’s simply weird that it’s not there… One more thing. Initial setup of horizontal(vertical)ScrollPosition as attribute of the component in mxml is “ignored”. Setting it in creationComplete event handler doesn’t work too. Doing it in runtime (e.g. in some event handler) works fine. Is that by design? Any workaround?
didier
December 12th, 2009 at 11:51 amI agree with you that exposing the visibility of the buttons at the API level would be a useful feature. However, this component is not something I sell or directly support. It reflects some user experience exploration I was doing with Flex, maps, and goal-based animations at some point in time. The code is there for others to maybe inspire or pursue the exploration thereof. There’s a handful of even more useful features that should be added to make it a normal production component. Thanks for your interest!
Re: the
horizontal(vertical)ScrollPosition, this might be due to the fact that theinnerCanvasis not fully rendered yet oncreationComplete. It’s more by design. A workaround might be to callinvalidateDisplayList()in the setters in acallLaterfashion, in order to delay the scroll positions to the next frame. Not sure this will work, though. Great catchh.
December 15th, 2009 at 5:25 amWell, spent some hours trying to center the view on load, but no success. I can set the *ScrollPosition property but the mouse position makes it to scroll back again. The goal is to be able to go to all directions after the page is loaded. Your demo starts in upper left corner too – any hint how to center the view on startup? Many thanks
didier
December 15th, 2009 at 12:15 pmHave you tried to use a timer to set your scroll positions? You can adjust the time so that it gives an ease out towards the center of your container just after the app loads… HTH.
Now the mouse position will always dictate the scroll positions. That’s the idea of the component. Maybe this is not what you need for your application.
Amar
April 20th, 2010 at 8:07 amYour Component is very nice . Am trying this component. i found One bug.. If you are crossing your component quickly the map and buttons are not displayed. The Component is totally blank..Can You have any solution for this..
Thanks in Advance
didier
April 20th, 2010 at 8:56 amThanks for your comment. Do you experience that behavior from my example or yours? If you move the mouse quickly across your application, especially outside of it, the
MouseEvent.ROLL_OVERorMouseEvent.ROLL_OUTmight not be captured properly. It’s just how Flex works… when the mouse leaves the bounding box of the application too quickly. Not sure if this is the same issue, but in order to compensate for this, you can look at the code of my example. There is a line:stage.addEventListener(Event.MOUSE_LEAVE, handleMouseLeave, false, 0, true);which handles the case when the mouse leaves the app. The client component has to handle that case. That could be something you may want to consider. Now if your canvas is blank, it might also be the way you are implementing its content. I don’t directly see how this relates to the implementation of the component itself–as it extends a regular canvas. I’d probably need to see what you did in order to clarify the issue, should it still occur.Amar
April 20th, 2010 at 10:02 amI didn’t any change in your code. Your demo(above) have same prob. How can i correct it. Where i want to change.
Am trying this stage.addEventListener(Event.MOUSE_LEAVE, handleMouseLeave, false, 0, true); Still same prob…
didier
April 20th, 2010 at 12:02 pmUnfortunately, I cannot replicate the problem you are describing. Things are working fine in the latest versions of Safari, Chrome, and Firefox, using the latest Flash plugin. Not sure about IE. Maybe you can check your browser/plugin. Thanks.
Amar
April 21st, 2010 at 6:24 amSorry to say this.. Am checking with latest versions of Safari, Chrome, and Firefox, using the latest Flash plugin. Still same error in all.
Thanks
Amar
April 27th, 2010 at 6:58 amDid you got any solution for the above error. Please post the answer once you got the solution
didier
December 16th, 2010 at 9:37 amAgain, I cannot replicate the problem you are stating. There must be something in your application that requires additional handling.