Wed 5 Jul 2006
Live “reflection” component
Posted by Narciso Jaramillo under design | development | Flex48 Comments
UPDATE 2/21/2008: See this post for the latest version of Reflector.as. I’ll reintegrate it into the source code linked to from this post eventually, but for now you’ll need to download the code from this post, then replace Reflector.as with the version from the newer post.
Okay, enough highfalutin’ chatter. This entry actually has (arguably) useful code in it.
The reflection effect is destined to become one of those gratuitous UI clichés, like brushed metal and gleaming highlights. But hey, the kids seem to like it, so I figured I’d try to make a similar component in Flex.
Before jumping into the explanation, here’s the demo:
You can drag the panel around to watch the reflection follow it, and play with the sliders to tweak the look of the reflection.
I’m not the first to attempt this: Trey Long created a reflection effect in an early beta of Flex 2 (the demo pointed to from that link no longer works with the shipping version of Flash Player 9). However, his version created static reflections—so, for example, you could see the reflection of a Button, but if you moused over or clicked on it, the reflection wouldn’t update to reflect (no pun intended) the visual changes to the Button.
I decided to make the reflection “live”—as you interact with the components being reflected, the reflection actually updates in (near) real-time. I also componentized the reflector itself, so you can just target it at a component and it automatically positions itself appropriately. I used code very similar to Trey’s reflection filter to render the reflection bitmap, though the way I actually draw it to the screen is a little different.
Read on for an explanation of the code:
The reflection component is called Reflector
, in the com.rictus.reflector
package. If you take a look at the demo application, LiveReflection.mxml,
you’ll see that the invocation of the Reflector
component is quite simple:
<reflector:Reflector target="{panel}" alpha="{alphaSlider.value}" falloff="{falloffSlider.value}"/>
Reflector
is a UIComponent
, so you can stick it anywhere a UIComponent
goes. It defines two properties:
target
: the component you want to reflect. The reflector automatically tries to position itself below this component, so the target component should live inside an absolutely-positioned container (like a Canvas or a Panel withlayout="absolute"
), and the reflector should be inside the same container.falloff
: a value between 0 and 1 that represents how much of the target component to reflect. 0 means to reflect none of the component, 1 means to reflect all of it, and values in between essentially set the ratio of the height of the component that will be reflected. In the demo, this defaults to 0.6.
In addition, you’ll also want to set the alpha
value to a low number (it defaults to 0.3 in the demo). Play around with the sliders in the demo to get a feel for how these values affect the look of the reflection.
To see how it works, look at the source code for Reflector
. Reflector
listens for updateComplete
events on its target, which fire every time the target’s updateDisplayList()
method is called. The Reflector
then updates its own display list to draw the reflection.
Here are some notes on the various methods involved.
set target()
: This is where we register our listeners for events on the target. Note that when we register for the updateComplete
event, we pass true
for the useCapture
argument to addEventListener()
. This is necessary because updateComplete
isn’t a “bubbling” event—it doesn’t automatically pass up to listeners that are set on ancestors of the updated item. For example, in the demo, if we didn’t get update events from descendants, you wouldn’t see the reflection update when you twiddle one of the sliders; you would only see it update if the outer Panel itself changed.
We can get around this by attaching a “capture” listener for the event; because of the way the Flash event mechanism works, this lets us see update events for all descendants of the target component, not just the component itself. I’ll have more to say about capture in a future entry.
handleTargetUpdate()
, handleTargetMove()
, handleTargetResize()
: When the target is updated, we call invalidateDisplayList()
, which tells Flex to redraw us (by calling updateDisplayList()
) at the next available opportunity. If the target is moved or resized, we update our own position and size to match.
createBitmaps()
: In order to avoid allocating and reallocating bitmaps every time we redraw, we allocate the bitmap storage once in createBitmaps()
. We throw away these cached bitmaps whenever the target or the size of the target changes, but otherwise we can just keep redrawing into them. We also create the alpha gradient in createBitmaps()
, since that gradient doesn’t need to change unless the target or its size changes:
updateDisplayList()
: This is where we do the actual drawing of the reflection. The algorithm is pretty close to Trey’s original code:
- Draw the target component into
_targetBitmap
. - Combine the target bitmap with the pre-calculated alpha gradient and blit the result into
_resultBitmap
. - Create a matrix transform to flip the image upside down.
- Use
beginBitmapFill()
anddrawRect()
to blit the bitmap into the display list.
And that’s it! Now, some caveats.
- You may notice that I set
showDataTips="false"
on the sliders in the demo. I turned them off because the data tips don’t actually show up in the reflection (like vampires, I guess), since they’re not children of the Panel. - I also set
liveDragging="true"
on the sliders. Without this, they don’t seem to send outupdateComplete
events as they’re being dragged. (One glitch in the demo is that if you click on the slider track rather than dragging it, the reflection doesn’t update as the slider animates to its new position, presumably because update events don’t get sent out during the animation even ifliveDragging
is set.) - This isn’t visible on the black background, but normally Panels have a small dropshadow around them. That dropshadow doesn’t get drawn into the reflection; I’m guessing that
BitmapData.draw()
doesn’t render bitmap filters that are attached to the target component.
In a future entry, I’ll describe the draggable panel component that’s included in this demo.
Please let me know if you find a bug, or have any questions about how it works.
(Addendum: Someone noted that this code is GPL’ed. I did this because Trey’s original code was GPL’ed, and I didn’t go through the motions of reinventing the wheel. Future code I publish will likely be under some flavor of Creative Commons license, for easier reuse.)
digg this |
del.icio.us | 48 Comments
fantastic work
Very seriously cool! 🙂
Cool!! Thanks
I was blown away by how schmic this looked – the black background with this effect is killer!
I’ve also made a mod to the original component, check it out: http://blog.wrench.com.au/2006/07/08/live-reflection-component-with-a-twist/
[…] Jason Langdon took my reflection component and made a new version that adds blur, which actually makes it look a good deal more realistic (higher levels of blur make it look like it’s on a less polished “table”). Very cool. […]
[…] a very cool flex reflection exampleIf you go and have a look at the call you’ll see that he makes the all important updateDisplayList call override protected function updateDisplayList(unscaledWidth: Number, unscaledHeight: Number): void { // This function is called by the framework at some point after invalidateDisplayList() is called. if (_target != null) { // Create our cached bitmap data objects if they haven’t been created already. createBitmaps(_target); […]
forgive me i’m a bit new to actionscript and flash i was wondering how to apply this effect, i am not quite sure how to use the supplied zip file. As for the reflection following the item itself is there a way to prevent that?
qwik,
The reflection effect will only work in Flex, not Flash. To apply it to a Flex component, just create a tag next to the component you want to apply the reflection to, and set its target to be the ID of the component, as in the example LiveReflection.mxml file.
(You would probably substitute specific numbers for the alpha and falloff values; the databinding expressions there are just for the demo, to make the values come from the sliders in the demo UI.)
The “following” behavior is baked into the component, but you could probably hack it out in Reflection.as by setting a flag in handleTargetMove() so that the “move” inside it only happens once.
Hey
I love your effect but I’ve sometimes an error message :
ArgumentError: Error #2015: Invalid BitmapData.
at flash.display::BitmapData$iinit()
at com.rictus.reflector::Reflector/::createBitmaps()
at com.rictus.reflector::Reflector/com.rictus.reflector:Reflector::updateDisplayList()
at mx.core::UIComponent/validateDisplayList()
at mx.managers::LayoutManager/::validateDisplayList()
at mx.managers::LayoutManager/::doPhasedInstantiation()
at mx.managers::LayoutManager/validateNow()
at mx.effects::Tween$/::timerHandler()
at flash.utils::Timer/flash.utils:Timer::_timerDispatch()
at flash.utils::Timer/flash.utils:Timer::tick()
I’m using flex builder 2.0.1
How can I fix this ?
Thanks
aub: Thanks for letting me know. I haven’t seen this error, but I also haven’t tested it extensively under 2.0.1. I’ll see if I can reproduce the problem.
Thanks,
I hope I’ll hear form you soon.
I was looking for a solution but I was not able to find it.
cya
I just tested it on the new flex builder 2.0.1. Nothing wrong with the code, it run smoothly. I guess just be carefull how you deploy it on you machine because u might end up putting the code on the wrong directory.
One small bug I just found up on the coding is when u delete the width and height size on the dragpanel, the drag and reflection goes hay wire. The reason I’m doing this is just make the panel resize on it own. If any you can solve this thing please let me know.
Cheers
From bIeDY (KL, Malaysia)
[…] I have used Narciso Jaramillo’s LiveReflection component to achieve this. Thanks Narciso. […]
Great stuff. However, there is a little issue with the object. You need to check for a target.width or target.height of 0 before calling the createBitmaps() method. If you don’t then you’ll see an Invalid: BitmapData as reported by aub.
You need to redefine the updateDisplayList() function as:
override protected function updateDisplayList(unscaledWidth: Number, unscaledHeight: Number): void {
// This function is called by the framework at some point after invalidateDisplayList() is called.
if (_target != null) {
// Check to see if the target has a width and height
if (_target.width == 0 || _target.height == 0)
return;
// Create our cached bitmap data objects if they haven’t been created already.
createBitmaps(_target);
….
This fix is required for the mx:Image object.
I think the blog software stripped the tag in the last post because it had the lt and gt in it.
Thanks for the bugfix, Barry. I’ll update the source soon with your fix.
thanks Barry 😉
To use this component in Flex by using the reflector tag is quite straightforward, I’m wondering how I would do this with AS3 in flex builder…
I’ve tried creating a new reflector object and then setting the settings with the proper dot notation, but it seems like it cant find the definition for “com.rictus.reflector.Reflector”, even though its in the build path.
Illias–are you trying to use this from a pure AS3 project (not using the Flex framework)? If so, I don’t think it would work at all, since Reflector subclasses the Flex UIComponent class.
Yes actually thats what I’m trying to do, so wouldnt I just add the swc file that contains the Flex UIComponent class. I think its flex.swc, as I already have framework.swc imported. Anyway I’ll give it a try and see, unless someone else has a better idea.
I need create photo reflection by using flash actionscript only…
But I dont know how to build it…
Sorry I also dont know how to use this source….
Who can help me?
Thanks….
ahann–You can’t use the source as-is, but I think you should be able to take the same basic algorithm used by updateDisplayList() and createBitmaps() in the Reflector component and just take out references to Flex concepts like UIComponent. I’m not a Flash AS guru, though, so don’t know for sure how exactly you would hook it into your other code. Sorry!
nj-Nvm…Thanks for your comment…
Great component…looks really schmick. I found that adding the following line to “handleTargetResize” function makes it deal with resizes better.
height = _target.height;
//move the reflection to its proper place
move(_target.x, _target.y + _target.height);
invalidateDisplayList();
Great work though, has given me heaps of ideas…
Can it uses with commercial site ?
I have tried read about “Creative Commons license” but it had many types of license.
I don’t sure for type of this components license
Thanks
Unfortunately, this particular version is GPLed, so can’t be used in a commercial app. It should be easy enough to recreate “from scratch”, though. I’ll try to make a non-GPL version in the future that’s safe for use anywhere. Sorry about that.
[…] http://www.rictus.com/muchado/2006/07/05/live-reflection-component/ […]
[…] Live “reflection” component http://www.rictus.com/muchado/2006/07/05/live-reflection-component/ […]
Changing
handleTargetResize
to use this for height calculation helps:
height = _target.height * _falloff; // shouldn’t be taller than it needs to or it blocks
That way it doesn’t try and make something as big as the app and possibly cover other controls invisibly.
[…] All the various feeds are collated into a single application for a single tag .The mode of access being a baisc HTTPService request.The app also uses this nifty little Reflection component of Narciso Jaramillo. […]
Hi
Great work DONT MISS IT
But I have a situation:
Canvas, VBox, HBox or other // root level
HorizontalList // 1 level
Buttons // 2 level
and set the target point to “root level” and I have flicker of the buttons when Mouse Move Over. If the target point is to “1 level” everything is ok but if there is one more level of UIComponent Container the look&feel goes crazy.
Anybody with same problems?
[…] component simply scrolls through a list of labels horizontally. It makes use of the reflection component from Narciso (nj) Jaramillo and TweenLite, a very lightweight tweening package. Before […]
to Mohk, i have the same issue with one button and an horizontal scroll bar in a panel ! (button blinking when mouse is over)
To avoid it, i added :
“if (event.target is Button) return;” in the handleTargetUpdate function.
It seems that the “_targetBitmap.draw(_target, new Matrix());” is in cause, but i don’t know why…
Ps: sorry for my poor english.
Amazing work !!!
Only one question… might be silly tho. How can I apply a reflection to a TitleWindow that Pops with pop up manager? Is it possible?
Thank in advance
I expanded the functionality of the reflector component from “much ado about something” and Jason:
– skewing
– reflection angle
– xOffset, yOffset
http://www.vulcansoft.com/flex/reflection/reflection.html
Enjoy!
Hi…
I can’t get this component to work when I want to get reflection of a button that has zoom effect set. I roll my mouse over a button and it zooms in but reflection under still has the original size.
As I just yesterday started playing with FLEX every kind of help would be much appreciated.
I know someone was touching on this earlier about applying the reflection to the children of a given element. For example if I want to apply the reflection to all the images returned by a given array. Anyone figured this out?
[…] have written me over the past few months to mention that they’ve had trouble getting the Reflection component to work in various cases. I haven’t had time to look at each of the problems, but I do have […]
Hey everyone–take a look at http://www.rictus.com/muchado/2008/02/21/updated-reflection-component/ for an updated version to see if it fixes your problems. Thanks!
[…] One of the things you have to do is grab the BitmapData from the UIComponent. You can do this by using a function written by Andrew Trice he explains it more in his post, but basically the function creates a blank Empty Matrix to hold the BitmapData. This is also a very popular way of creating reflections. […]
hey,
i want a gap of 2 pixels between the original object and the reflectorobject, i tried this:
transform.translate(0, _target.height+2);
the gap has the color of the bordercolor of the canvas. why?
thanks
[…] Live reflection component http://www.rictus.com/muchado/2006/07/05/live-reflection-component/ […]
Now someone just has to make one that takes things like light source into account when dragging it around. Great work, fun stuff. if only I knew something about Flash/Flex.
[…] Live “reflection” component http://www.rictus.com/muchado/2006/07/05/live-reflection-component/ […]
Perhaps you should have gone with a BSD style license like most other UI developers (Apple and others use it) instead of the GPL which requires that anyone such as myself who is writing a distributable application ALSO provide the souyrce code if I incorporate your handful of bits. Consiodering this code would make up less than 1% of the app and is trivial and has been done by many others …. I don;’t think your licensing requirements are worth it. If you were really stuck on the “cool” image the GPL has, you could have gone with the LGPL, which is good for things such as this, and I have used that many times in the past myself for code I wish others to use, but without the restrictions and requirements of the GPL. You might want to reconsider your approach to licensing and read the terms you are expecting others to follow and ask yourself “is it worth it for what I am offering in return?”
Hi,
I noticed a weird thing when using this in Flex 4 (don’t know if it applies to Flex 3 as well) – when setting a new component as the target, the previous one would display briefly. It was more obvious to me because I was applying a fade effect during a state transition to the target component, then switching the target to a different component during the exitState handler.
Anyway, I fixed it by calling graphics.clear() in the clearCachedBitmaps() function.
Cheers,
Chris
The application is really cool. How we can set the height and width of the reflection. If we have the falloff is .5 then it should take only half of the original height.
[…] Live “reflection†component http://www.rictus.com/muchado/2006/07/05/live-reflection-component/ […]