|
|
Labs.byHook |
Flash Scripts, Tools & Methods Developed at Hook |
A cover flow is a great way to display and browse through discrete content. It feels like flipping through a magazine: easy and fast, but informative. Its a natural addition to many user interfaces, but many existing implementations are like black boxes with strict display rules. You add in your elements, and you end up with iTunes 7. We needed something more generic and reusable; those were the priorities. We take a look at the built in transform property of a DisplayObject, and how that can be used to build an flexible flow.
Breaking It Down
The flow can be broken into several pieces. The main class, called a Flow, handles things like user input, positioning of the elements, and z-sorting. Inside a Flow is an array of FlowContainer‘s, which is really where the magic happens. The FlowContainer actually encapsulates various transformations, and applies a percentage of those transformations to itself based on the position of the mouse within the Flow. Both the Flow and FlowContainer extend MovieClip, and this means that any DisplayObject can be added as a child to the FlowContainer and it will inherit its various transformations.
Wow, that was abstract. What do I mean when I say FlowContainer encapsulates transformations? Every DisplayObject has a property transform which holds various information about the object’s position and color. I’ll make an example with a transformation matrix, which is a three by three matrix. The template of the matrix is:
![delim{[}{~matrix{3}{3}{a c t_x b d t_y u v w}~}{]} delim{[}{~matrix{3}{3}{a c t_x b d t_y u v w}~}{]}](http://labs.byhook.com/wp-content/plugins/wpmathpub/phpmathpublisher/img/math_956.5_7b8d6d0418fb24dddf3886c063870ef4.png)
The elements a, b, c, and d control the scaling of the DisplayObject, while tx and ty control the translation of the DisplayObject. The u, v, and w usually provide additional information if its needed, but in a standard two dimensional transformation they are unnecessary. The most basic transfomation matrix is the identity matrix. The identity matrix, when applied to the object, does absolutely nothing.
![delim{[}{~matrix{3}{3}{1 0 0 0 1 0 0 0 1}~}{]} delim{[}{~matrix{3}{3}{1 0 0 0 1 0 0 0 1}~}{]}](http://labs.byhook.com/wp-content/plugins/wpmathpub/phpmathpublisher/img/math_971.5_d67fb2fcb18eebaebdc543e1ee203469.png)
Now, lets say we want to translate, or move, the object 56 pixels to the right and 32 pixels down. The transformation matrix we would use is,
;
Easy. Scaling is similar, and uses a to scale horizontally and d to scale vertically. So to make our object 4.4 times wider and 2.6 times taller, we would use,
;
Note that the translation is still there, now its just scaling at the same time. Rotation is the strangest, because its actually a compound operation. Without getting too mathy, the elements a and d of the array control scale and the elements c and b control skew. If you do both operations at the same time, you can rotate the object. I’m going to remove the scaling from the previous matrix for clarity. If we wanted to rotate the DisplayObject by 70 degrees (about 1.2 radians) we would use,
;
Let’s look at all this nonsense in action to drive the point home.
There, that’s a little more interesting. Try manipulating a, b, c, and d to make the box kind of rotate. Also, note that a, b, c, and d are all relative to the blue box’s registration point in the top left, while tx and ty are the translation from the parent’s registration point to the child’s registration point. Why does this happen? If tx is 56 and ty is 32, why isn’t the blue box at pixel (56, 32)? This is because the when the display object is rendering, it doesn’t stop at its own transformation matrix. It actually traverses up the display list and concatenates (multiplies) each and every transformation matrix above it to find an absolute transformation matrix relative to the stage. The inverse of this is that if we change the transformation matrix of a DisplayObject, all of the children of the object inherit that transformation.
Go with the Flow
Back to flows. How can we use all these transformations to make a cool effect like the cover flow? We can think of each FlowContainer in the Flow as having two appearances. One is when its out of focus, or off to the side. The other is when it is in focus, either in the center of the screen (like in the classic cover flow) or directly underneath the mouse (like in our flow example). If we define those two appearances as matrices, we can easily interpolate between the appearances based on whatever input we’re using (the mouse, a slider, arrow keys, etc.). For basic transformations, our FlowContainer class keeps track of four variables.
private var lerp:Number; // the percentage of the effect currently being applied private var positioning:Matrix; // the original position of the object, letting our transformations be relative private var transformNone:Matrix; // the relative transformation when lerp is 0 private var transformFull:Matrix; // the relative transformation when lerp is 1
To set the values of these, we use a single method with some constants for context.
/** * Bind a transformation matrix to either the near or far appearence of the * container. To specify whether the transformation happens near or away from * the mouse, set the distance parameter to either Flow.NEAR_EFFECT or Flow.FAR_EFFECT. * @param value the transformation matrix to be bound * @param distance the distance constant at which the transformation is applied */ public function bindTransformEffect(value:Matrix, distance:String):void { if (distance == Flow.NEAR_EFFECT) transformFull = value.clone(); else if (distance == Flow.FAR_EFFECT) transformNone = value.clone(); else throw new TypeError("The distance must be either Flow.NEAR_EFFECT or Flow.FAR_EFFECT"); synchronize(); }
After these are set, we use synchronize() to match the appearance of the container with whatever matrices were set.
public function synchronize():void { var lerpedTransform:Matrix; if (lerp != 0.0) { lerpedTransform = Util.lerpMatrix(lerp, transformNone, transformFull); lerpedTransform.concat(positioning); // make the transformation relative to the original position } else { // transformNone is cloned because otherwise the concat operation would compound lerpedTransform = transformNone.clone(); lerpedTransform.concat(positioning); } transform.matrix = lerpedTransform; }
There are two things to especially take note of in that code block. The first is at Util.lerpMatrix(), where we use linear interpolation between transformNone and transformFull. This is kind of bad, because linear interpolation doesn’t handle rotation correctly. But, it is a heck of a lot faster and easier than the alternatives: quaternions, slerping, or matrix decomposition/recomposition. If you’re like us and only need to use scaling and translation, here is the linear interpolation algorithm.
public static function lerpMatrix(value:Number, low:Matrix, high:Matrix):Matrix { if (value < 0.0 || value > 1.0) throw new RangeError("The lerp value must be a percentage between 0.0 and 1.0."); var result:Matrix = new Matrix(); result.a = value * (high.a - low.a) + low.a; result.b = value * (high.b - low.b) + low.b; result.c = value * (high.c - low.c) + low.c; result.d = value * (high.d - low.d) + low.d; result.tx = value * (high.tx - low.tx) + low.tx; result.ty = value * (high.ty - low.ty) + low.ty; return result; }
The second thing to take note of is the concat() function, where we concatenate (multiply) the positioning variable onto the transformation. This is because its way easier for whoever is interfacing with this code to define the translations relative to wherever they’ve placed the object. The tx and ty value can look like 50 and -25, instead of random nonsense like 480 and 312. But, the transform property of the DisplayObject stores transformations relative to its parent, not itself, so we have to take care of that step ourselves.
In addition to transformations, we also store ColorTransformation objects and BlurFilter objects to lerp between and synchronize in our FlowContainer. After seeing how the regular transformations work, hopefully these are pretty easy to understand. Using only the FlowContainer object, we can make something like this:
A Little Organization
The final step is organizing these little nuggets of transformation juju into a usable UI component. Its a lot simpler than you might think; the Flow object only has about three substantial functions. The first step though, is keeping track of the FlowContainers so that we can iterate through them quickly. After that, adding DisplayObjects is easy.
/** * Add a component to the flow. The component can be anything; if it is not already * a flow container, it will be wrapped in one automatically. * @param item the component to be added to the flow */ public function addComponent(item:DisplayObject):void { var fc:FlowContainer if ( !(item is FlowContainer) ) { fc = new FlowContainer(); fc.addChild(item); } else fc = FlowContainer(item); items.push(fc); addChild(fc); }
After all the items are added, we need to put them in their place. We use the variable track here; its just a invisible movie clip that lets us separate the Flow‘s area for interaction from its area for display. A simple spacing algorithm, followed by the z-sorting algorithm:
/** * Organizes the flowable items into an even spread along the track. */ public function organize():void { var item:FlowContainer; for (var i:int = 0; i < items.length; ++i) { item = (items[i] as FlowContainer); item.x = track.x + ( Number(i + 1) / Number(items.length + 1) ) * track.width; item.x -= item.width / 2.0; // center the component in its slot item.y = track.y; item.freezePositioning(); // stores the flow containers current position as the start point for any relative transformations } } /** * Sorts the z-index of the items based on their distance to the mouse. */ private function zsort():void { var i:int = 0; var j:int = numChildren - 1; // make i the index of the component which should have the highest z-index while (i < (items.length - 1) && items[i].effectPercent <= items[i + 1].effectPercent) { ++i; } /* Move this component to the top of the z-stack. Note that j will then point to the next highest spot on the z-stack because of the post-decrement operator. */ swapChildren(items[i], getChildAt(j--)); // sort the items left of the middle item for (var k_left = i - 1; k_left >= 0; --k_left) { swapChildren(items[k_left], getChildAt(j--)); } // sort the items right of the middle item for (var k_right = i + 1; k_right < items.length; ++k_right) { swapChildren(items[k_right], getChildAt(j--)); } }
Then, we just listen to the mouse and update the FlowContainers. The input could easily be replaced by a slider or arrow buttons for something more infinite.
private function moveListener(e:MouseEvent):void { // relative to the envelope of the flow var mouseX:Number = e.stageX - this.x; for (var i:int = 0; i < items.length; ++i) { var distance = Math.abs(items[i].x + items[i].width / 2.0 - mouseX); // here, effectNone and effectFull are numbers which indicate the distance from // the mouse where there is no effect, and full effect. then it just maps the distance // between those and winds up with a percentage. var percent:Number = Util.map( distance, effectNone, effectFull, 0.0, 1.0); items[i].effectPercent = Util.clamp(percent, 0.0, 1.0); } zsort(); // if a new flow container is in focus, it needs to be on top! }
Custimization
That’s the last of it really. Put all the pieces together, and you end up with something like this:
To drive home how simple this framework makes it, here is all of the code used to configure the above flow.
var flow:Flow; flow.farEffectDistance = 150.0; flow.nearEffectDistance = 0.0; flow.maintainEffect = false; for (var i:int = 0; i < 12; ++i) flow.addComponent(new SampleContainer()); flow.organize(); flow.transformEffect(new Matrix(1.5, 0, 0, 1.5, -30.0, -40.0), Flow.NEAR_EFFECT); flow.blurEffect(new BlurFilter(4, 4, 1), Flow.FAR_EFFECT);
And to make it obvious how easy it is to customize it, here are some wacky settings that you might never want, but if you do, its possible. And that’s the important part.
var flow:Flow; flow.farEffectDistance = 150.0; flow.nearEffectDistance = 0.0; flow.maintainEffect = false; for (var i:int = 0; i < 8; ++i) flow.addComponent(new SampleContainer()); flow.organize(); flow.transformEffect(new Matrix(0.2, 1, 1, 0.2, 2.0, -2.0), Flow.FAR_EFFECT); flow.transformEffect(new Matrix(1.0, 0, 0, 1.0, 25.0, -25.0), Flow.NEAR_EFFECT); flow.colorEffect(new ColorTransform(1, 1, 1, 0, 0, 0, 0, 0), Flow.FAR_EFFECT);
And guess what? Those buttons will always work.
3D, Actionscript, Actionscript 2, Actionscript 3, adobe, air, airforandroid, Alchemy, android, as2, as3, Audio, avm, ByteArray, C, Canvas, charts, Cygwin, data, Data Structures, Debugging, Decoder, Diagnostics, Encoder, Event, EventDispatcher, facebook, Flash, Flow, Framework, gcc, Graph API, haXe, html5, Image Extraction, ios, iphone, java, Javascript, JSFL, Like Button, Linux, llvm, Menu, Meta Balls, Motion Blur, MVC, Nav, OAuth, Object Extraction, Ogg, Ogg Vorbis, performance, php, Physics, pie menu, Pointers, processing, radial menu, real-time, sheep, Siox, Sound, State Machine, statistics, tricks, tutorial, twitter, Utilities, Vectors, Verlet, video, visualization, Vorbis, Windows, |
Worderful article. I love it, thank you for posts.
Thanks for the post. It’s usefull for me.
But, it is a heck of a lot faster and easier than the alternatives
I like your blog Inspiration.This is one of the user-friendly post.I think this kind of post will help us to get more backlink.
Nice costumes if your designing any thing like these you can sell it on line with online a minimum investment you can increase you sales.
Thank you for this entire technical blog ..
Indian domain names
Coach Bracelets is the very popular accessories in this season. New CoachBracelets in our Coach outlet of this season receives a lot of love and support from the public as soon as it is launched. The new CoachBracelets in Coach outlet appears with the very strong retro style, very fashionable and popular recent years.
Brilliant concept. Its really useful things here. Thanks for sharing good guidance.
To give the best look is the goal of cheap Oakley sunglasses
To give you with that impeccable display, [url=http://www.cheap-oakley-sale.com/oakley-oil-rig-sunglasses-c-10.html/]Oakley Oil Rig Sunglasses[/url] pursuit a little number of the most inventive advances, shades of hue and form. Select from Sports pursuits, Energetic, Polarized, Asian Match, Photochromatic, Signature Sequence and Unique Edition accessible for each women and men. Within the men’s sunglass environs you are competent to strive these newest designs. The Polarized Split Jacket with light-weight perimeters and switchlock technologies which makes lens modifying not hard and speedy or even the M Body Striker in Jet Black giving the snugness and help of the faultless three-point match. Also you are competent to conclude the Oakley Fifty out of 100 Jacket sunglasses faultless to get a everyday display or even the [url=http://www.cheap-oakley-sale.com/oakley-crosshair20-sunglasses-c-1.html/]Oakley Crosshair2.0 sunglasses[/url]which are comprehensive on efficiency. And when you would like to gaze after a slice of setting go away for your Artist Sequence Edition sunglasses boosted by recitals, artwork, set about and sports activities.
Oakley Women’s Sunglasses devotes for you in individual equates and kinds like by no signifies previous to. They’re solely conveyed ahead for present day women who reside by their very own guidelines. You will adore the vibrant Oakley Frogskins Sunglasses Discreet with trounce jewellery in the hinges and in supplement the 6-base quadrangle configuration that stylishly perimeters your encounter. You are competent to in supplement strive the evergreen Correspondent Sunglasses or even the flattering Encounter Glasses- each really are a phenomenal blend of glamour and competence, conceived to give your ascribe a emblem mark new definition.
3P6TWKM1PM 2OOFJBODWG Welcome to Oakley Sunglasses Outlet Store Online Hut to buy cool Cheap Oakley Sunglasses. We Cheap Oakley Flak Jacket, Cheap Oakley Frogskins, Cheap Oakley Fuel Cell, Cheap Oakley Gascan, Cheap Oakley Goggles, Cheap Oakley Holbrook, Cheap Oakley Hot Sale 2011, Cheap Oakley Jacket, Cheap Oakley Jawbone, Cheap Oakley Juliet, Cheap Oakley Jupiter, Cheap Oakley Livestrong, Cheap Oakley M Frame, Cheap Oakley Monster Dog, Cheap Oakley New Releases, Cheap Oakley Oil Rig, Cheap Oakley Polarized and Cheap Oakley Radar at the lowest price when compared with other peer products. All of cheap Oakley Sunglasses Sale are of the best quality and sold at wholesale price. Take action now and put them in your shopping cart to save more.
YNHXFOXWYZKYZY
Replica Oakley Sunglasses are Awesome and Well-liked by the Young
http://www.zacoo.com is an online store providing jewelry beads, jewelry making supplies and jewelry findings
I will register for this web site. I ??m a building room decoration consultant for a time. My embellishing word of advice with the hundred years will be: Don?ˉt overclutter a property. Space is the vital thing. Right up until so when.
I got a definitely valuable website. I’ve been right here analyzing for about an time. I’m a newbie and your achievements is quite a lot an imagination for me.
Our company is specializing in online selling cheap NFL jersey,discount MLB jersey,cheap NHL jersey,Authentic NFL embroidered Jerseys and cheap MLB jerseys and women jerseys,kids and youth jerseys,Replica NFL Jerseys.All of our jerseys are 100% stitched and sewn on, never screen printed so the quality is guaranteed. You can place order directly on our website for the jerseys you like. And then settle payments by ecpay or gspay or Western Union. And if you are a wholesaler, you could contact us by on-line customer service or mail and we will give you the best discount.
[...] Go Go Gadget Flow [...]
North American Hockey League (NHL) 4 finals in the second end of Vancouver Rogers Stadium. Vancouver Canucks first goal in extra time, so that rivals the Boston Bruins ‘sudden death’, which ended 3-2 victory and 2-0 total points in the Stanley Cup finals to stay ahead. Winning goal by striker Abel Ross added 11 seconds left in overtime when hit. At that time, he received a pass from his teammates quickly advancing from the left wing, escaped Bruins goalie Tim Thomas of the block.
With Larry Bird, Robert Parish, Cedric Maxwell and Kevin McHale of the Celtics will win over the Rockets are experts believe that, despite the release of Westward Moses said he got four boys home with him for the Rockets can win this series. Experts and Moses proved wrong. Although the Celtics won, Del Harris was discharged from the rocket team had a stubborn resistance, and ultimately lost 2-4 in the sixth only to.
This wonderful article let me know more things.
The male image, no matter what the age, women cannot resist, in the literary and artistic temperament imply too much temptation to kill love and despair, so Ralph Lauren Outlet of silver tie with Ma Ying-jeo at important occasions many times. Normal men’s wardrobe was this: Ralph Lauren Outlet suit, Ralph Lauren Outlet shirt, tie, Ralph Lauren Outlet Polo t-shirt jeans for reduced and increases with age. Ma Ying-jeo wardrobe seems like many ambitious men, but his charms are revealed in his capacity as a successful politician, maintaining his stature, maintained his moderate, before the program on, will go home with my wife. Although “special fee” case to his decline in satisfaction of public opinion, however, Idol’s appeal will not be reduced. Perhaps the woman is more concerned about his “twist style” persistence-the man of conquest, is so soft.
Thanks a lot for sharing this amazing knowledge with us. This site is fantastic. I always find great knowledge from it.
Very good stuff you may have there, I will be back for a lot more reading quickly.
Must take full advantage of life’s leisure time, do not let any opportunity slip away self-development.
The flow can be broken into several pieces. T
Thanks for sharing these useful information! Hope that you will continue doing nice article like this.
Thank you very much.I like this site.
this, but – not to pop your bubble – this is the Genie Effect, not Cover Flow. The Genie Effect is how the OS X tool
This is a wonderful site where we are getting more information. I really hope more people read this and get what you’re saying, because let me tell you, it is important stuff. I never would’ve thought about it this way unless it runs into your blog. And I have been talking with my friend about, he though it is really interesting as well. Keep up with your good work; I would come back to you. Than you for your sharing!
There is also a long way and then end the night there and then the black end.
Physical activities jerseys aren’t our next best element, because they can be always in mode – that is certainly more than you may say for a variety of that light designer information they have people dressed in! Most real sports fans prefer to display solidarity with regard to their team and for your majority using the company colors is very best way to show customer loyalty.
I have added you in my social bookmark…and i am waiting for your next post.
Nice, configurable code on this, but – not to pop your bubble – this is the Genie Effect, not Cover Flow. The Genie Effect is how the OS X toolbar menu works, the Cover Flow is the angled images in iTunes.