Some thoughts on Flex vs. HTML (or…”how I made my Flex List Images stop flickering.”)
January 23rd, 2007
[ARDemo]: http://demo.quietlyscheming.com/superImage/Tests.html
[FlickerDemo]: http://demo.quietlyscheming.com/superImage/app.html
[Sho]: http://kuwamoto.org/
[FABridge]: http://www.quietlyscheming.com/blog/2006/03/09/flex-ajax-bridge-flickr-roulette-repost/
[source]: http://demo.quietlyscheming.com/source/SuperImage.zip
In the past week or two I’ve been noticing an uptick in the number of posts on the benefits of Flex vs. HTML threading their way through the blogosphere. Now I don’t really want to get involved too deeply in this discussion — HTML/AJAX and Flex have different design centers, and as a good developer you should be deciding what’s the best technology suited for the task at hand (or, [why not use both?][FABridge]). Lots of factors should be affecting your decision — productivity, usability, content type, size of the production team, type and lifetime of the content — and for any given project, those factors might push you in different directions (i.e., some projects types will be more usable implemented in Flex, and some will be more usable implemented in HTML).
But there’ve been a couple of comments I’ve seen that have led me to make this statement: I think it’s a mistake to evaluate Flex purely on the basis of the pros and cons of individual features of the framework. Why? Because unlike HTML, _you are not limited to what you can do with the functionality we provide out of the box._ Flex was written from day one to be an open, extensible framework…anything we can do in our components you can do too (and we even give you the source code to prove it). Don’t like the way Image behaves? Write your own Image tag! Get enough people doing that, and there’ll soon be a wealth of new and improved frameworks and components out there for everyone to choose from.
Now you can argue that this is possible in HTML as well…witness the multitude of AJAX widgets and frameworks sprouting up all over the web world like weeds in the yard. That’s true, and there really is some quality AJAX content available for developers to take advantage of. But I’ll point out three important things that differentiate Flex’s approach from these AJAX libraries when it comes to customization:
* Integration. Flex was built for extension, while AJAX frameworks are a second class citizen in the HTML world. This is not a fault of the AJAX library developers, but a limitation of the HTML/Javascript world. There’s nothing quite as satisfying as writing a new Whirlygig Flex component and using it in MXML with a simple <my:Whirlygig /> tag.
* Power. Most of the components, tags, and APIs you use in flex are written in actionscript on top of a much lower level API provided by the player. That means that in addition to building _up_ on Flex, you can build down as well — as I said, if you don’t like the Image component, go under the covers and build a better one.
* Coherence. Because we exposed the underlying infrastructure from day one, there’s one accepted component framework in Flex that everyone is using/extending. That means lots of components available, lots of help and knowledge transfer available, and a large market for any useful extensions you write. The Ajax framework landscape is craggy and fragmented. That will likely change over time, but it’s not going to be an easy road to get there.
(How about all those other component frameworks Macromedia came out with, you ask? Touche, I would respond (if I knew how to type an accent egue in HTML) Touche. Let’s just say we’re learning our lesson the hard way).
### Putting my Money Where my Mouth Is
Rather than blathering on, I’m going to go ahead and kick off some improvements myself. Since before Flex 1.0 shipped, there’s been three things that bug the hell out of me about our Image component: In rough order from most to least annoying:
* Funky aspect ratio behavior. The Image component will guarantee it renders images at the correct aspect ratio, but how that relates to the amount of space it actually takes up on screen at anything other than its natural size is bizarre, to put it midly.
* reload behavior in a list. A number of people have blogged about this annoying behavior recently, and they’re generally right. Because of some of the details of the Flash Player’s networking layer, there is always a delay of at least a frame between when you request an image url, and when it is actually loaded. That causes ugly flickering in a list (although I will say that if you gave me a list with 10,000 items in it, I’ll take Flex’s flickering-but-fast-virtual-list over HTML’s giant slow loading page, or next 10 items – next 10 items – next 10 items – hell any day of the week).
* no border. It drives me crazy that it’s hard to put a border on an image. You can wrap a container around it, but that’s a bit of a sledgehammer, don’t you think?
I’m tired of working around these issues, so I’m going to do something about it. So last night I went ahead and wrote myself a SuperImage component, which solves all three problems in a nice tidy package. It caches loaded images to avoid flicker, correctly sizes itself to use up screen space efficiently, and can support all the border options the Flex containers support.
While I was at it, I threw in a couple of other enhancements as well:
* source can be a full URLRequest, to allow for more complex HTTP requests (POST, extra headers, etc).
* source can be almost anything that can yield an image…url string, URLRequests, a DisplayObject class, a DisplayObject instance, BitmapData, a Bitmap, or any old DisplayObject,
That’s just a start. I’ll probably add to it over time, building up my arsenal of useful features that I’ve always wanted in an Image but previously have been too afraid to ask (ask Flex that is). [Sho][Sho] has already requested a loading animation, and an event to differentiate the first time an image is loaded from the subsequent reloads it goes through in a list.
Here’s two sample apps I’ve been using to test out the functionality.
### [A SuperImage no-flicker-flickr test.][FlickerDemo]
### [An Image-SuperImage aspectRatio comparison][ARDemo]
The code could use some cleanup, some comments, and a lot more functionality ;) But if you want to get your hands on it before all that happens, here it is:
### [Download the source][source]
Now I’ve said my piece (peace?), but just to reiterate what I wrote above: Sometimes Flex is the right choice, and sometimes it’s not. Flex and Flash are fantastic technologies, in my opinion, but they are not a panacea. Sometime the RIA model isn’t the right choice. Sometime details of the Flash Player make it the wrong technology. But sometimes you’ll find problems specific to the framework, and for those I say: let us know…write a blog…lament and complain, so we can continue to improve on them with every release. But while you’re waiting for the next release, why not try your hand at improving the framework yourself?
January 26th, 2007 at 11:48 am
Very nice, the caching doesn’t seem to be releasing properly though. After doing several searches (and scrolling through all images) the Memory usage doesn’t seem to be going back down. With that minor issue though this is a very cool implementation, I can definitely see uses for this.
January 26th, 2007 at 11:57 am
EDIT: Ok, it does start to clear it, it just took several searches and some time. Probably just hitting it unrealistically quickly. Nicely done, looking forward to the source for this!
January 26th, 2007 at 12:04 pm
Hi Aaron. You’re right. That’s partially configurable in the code…you can set a cache limit on the number of items. Unfortunately, with no reliable way to tell when you’ve been released, there’s no way to guarantee we can automatically release everything in the cache when you’re done…so an app that uses it will either have to live with the fact that there will always be something hanging around in the cache, or add some calls to explicitly manually reset it (which is part of the current API).
Thanks for the feedback. Writing this email, I realized that there is actually something I could do to improve it a bit.
Ely.
January 26th, 2007 at 3:56 pm
Ely,
Nice! I had the same issues with the Image component as you mentioned.
One of the things I’d like is the ability to sharpen or smooth up the image. Since most of the time its not the original size it appears chunky. I can’t remember what that feature was called but it had a tremendous effect. It was a filter or something that enhanced or smoothed the image. Would it be possible to make that an option and turn it on by default?
Can this load in SWF’s? Thanks for this. It should be the next Image Component.
January 26th, 2007 at 6:53 pm
Ely,
Pretty awesome!
The distro does not have a /images/photos/ photo resource for the SuperImage TESTBED. Should I just throw my own set of photos in or is the TESTBED expecting a certain set of image sizes.
Pete Mackie
January 26th, 2007 at 10:14 pm
Judah — that’s an option on the Bitmap class. That should be pretty easy to surface up to the SuperImage from the Bitmap contained within, although there might be limitations when loading content across security domains.
Yes, it should be able to extend to SWFs, with one caveat. If you look at the code for the caching, you’ll see that there are a couple of different options for loading something out of the cache. When it’s a bitmap, and we have security access, the cache will create a new Bitmap wrapped around the same bitmap data if the original instance is in use. With SWF content, that won’t unfortunately be possible. Which means that if the content is still in use elsewhere, it will have to load it fresh from the browser cache, which means getting a one frame flicker. But in scenarios where you only need one instance in use at a time, it should work fine (i.e., the sample app I posted).
January 26th, 2007 at 10:15 pm
Pete — yeah, just throw in any old photo, and update the url in the test application to point to it. Hopefully, any size/aspect ratio should work (assuming I’ve done my job ;))
January 29th, 2007 at 10:11 am
[...] I know there’s a bunch of people who read this blog looking for flex components and sample code, and tend to ignore the longer more considered monlogues I post (I’m not bitter). So in case you’re one of those people, you might have missed the new component I posted last Friday called SuperImage. Read this post to find out more, see the samples, and download the source. [...]
January 29th, 2007 at 12:35 pm
Wonderful.
One minor thing… after leaving the page open for a while I encountered an exception….
Error #2044: Unhandled IOErrorEvent:. text=Error #2036: Load Never Completed.
January 31st, 2007 at 10:15 pm
Thanks Ely! What do you think of this? Is there a chance you can integrate this fix into it? I have this issue on my crappy blog.
http://dougmccune.com/blog/2007/01/31/problem-with-transparent-pngs-in-flex-using-embed/
February 7th, 2007 at 10:49 pm
[...] In keeping with Ely Geenfield’s naming conventions I’ve dubbed this component SuperTabNavigator. It is pretty super after all. This is a subclass of TabNavigator. I was actually able to do everything I wanted to do without having to copy/paste the code of TabNavigator, so using this component shouldn’t add too much to your Flex app’s footprint. The source included also includes the source of previous components I created that allow menu components to scroll. The SuperTabNavigator uses the scrolling version of the PopUpMenuButton for the button to the right of the tabs. So those scrolling menu components are thrown in here. [...]
February 16th, 2007 at 4:37 am
OMG thank you for this!
With my itemRenderer using normal i had tons of server requests for each listitem rerendered when scrolling the list (i’m using TileList) or even resize it’s width!
Ok the caching works pretty good and no unnecessary server requests are done anymore BUT the memory usage of the application is still insanely increasing when i scroll my list or resize it.
Maybe someone can explain where the memory usage comes from (i guess the renderer creates new objects EVERY time a image is listitem is rerendered and does not reuse those already being there?!).
Great work buddy!
February 21st, 2007 at 4:36 am
Thanks, Ely. I can get some ideas from this article for my flex developments, because I was facing some problems like this.
However, now I’m “wrestling” with a similar but different issue: I try to simulate live video streaming by requesting continous jpg from a URL using setInterval.
I use math.random to avoid caching image, but I can’t solve the flickering between each image load. I have tested the code into a browser and just with the FlashPlayer, and also requesting small images saved on local hard drive to avoid long requests, but the same problem happens: flick between images. So, the streaming is impossible in that way because the visual effect of the flick is quite unhappy.
From your knowledge, can you give any idea or “road map” into the rigth direction to solve it?
Thanks!
March 18th, 2007 at 10:40 am
awesome ! these flickering lists were so annoying to browse… thanks
March 29th, 2007 at 8:28 am
We had massive memory issues after about 15 minutes use (we have lots of thumbnails and medium size images in our app). The image cache was the culprit so I told SuperImage to stop caching and always load the image.
Are you releasing an updated version any time soon?
Toby.
March 29th, 2007 at 1:01 pm
[...] I swapped my own SuperImage with Ely Greenfield’s SuperImage today because he has come up with a solution to fix that annoying dancing flickering effect when you use mx:Image controls within a TileList. I soon noticed that the application was using huge amounts of memory and crashed IE6, IE7, and Firefox 2. The problem lies in the Caching class that the SuperImage uses. Ely kindly emailed me with a fix so I thought I would share it… Add this to your app initialization code. [...]
May 1st, 2007 at 11:12 am
Hi Ely, Great Component! I love it. I did run into one issue where the image was not centering correctly within my other component. Digging in, I found that the SuperImage width and height were both remaining at 100, even thought the image and border were 100 X 70.
I found that by adding this line to the end of the updateDisplayList() function fixes the problem:
this.setActualSize(_border.width, _border.height);Is that the best way to fix that, or is there a better way?
June 13th, 2007 at 6:34 am
I can’t seem to get the caching to work properly. I am trying to load ten copies of the same image and it is being re-requested from the server for each one. I’ve tried passing a url as a String as well as a single URLRequest to all, but it still is making ten requests for the exact same image.
July 16th, 2007 at 9:58 am
Not sure if I missed some other properties that allow me to “center” an image, but I just added some code to SuperImage to center the content in the rectangle where the image is supposed to render. This is necessary is scaleContent is true and maintainAspectRatio is also true.
I added this style:
[Style(name="center", type="Boolean", inherit="no")]
and at the bottom of updateDisplayList() things got changed this way:
if(_border != null)
{
_border.setActualSize(contentWidth + borderMetrics.left + borderMetrics.right,
contentHeight + borderMetrics.top + borderMetrics.bottom);
if(center) {
_content.x = borderMetrics.left + (unscaledWidth – contentWidth) / 2;
_content.y = borderMetrics.top + (unscaledHeight – contentHeight) / 2;
} else {
_content.x = borderMetrics.left;
_content.y = borderMetrics.top;
}
}
else
{
if(center) {
_content.x = (unscaledWidth – contentWidth) / 2;
_content.y = (unscaledHeight – contentHeight) / 2;
} else {
_content.x = 0;
_content.y = 0;
}
}
July 16th, 2007 at 3:06 pm
Ok i’ve found that here, afte written an own ImageCache. It works but it has the same problems like your SuperImage Component.
When i use those cached images in a TileList and than change the size of the TileList from maybe 6 Columns to one, than i have wrong visuals on the underlying item.
invalidDisplayList doesnt help. I’ve solved it with only caching the byteArrays an override the updateDisplayList( …) function in the Item renderer which asks the cache for the picture and rerender it .
the reason for my comment ist that this solution is realy bad because it is flickering horrible and have some bad sideeffects.
it would be great if someone would say , hey just put that there to tell your image to redraw
July 27th, 2007 at 6:23 pm
OMFG you’re my hero, I had pretty much given up on having image scrolling in a list be smooth!
September 12th, 2007 at 10:13 am
I have encountered an interesting ‘feature’ when using SuperImage in IE.
I modified the SuperImage class by adding custom context menus and I am using it in a custom item renderer class.
However, the custom context menu would only display in IE on non-cached content. When a cached image was displayed, the custom context menu was replaced by the verbose default context menu.
By setting the cache name to null to prevent SuperImage from caching, I realized that IE was already caching the image requests for the flash player, and the custom context menus displayed consistently, both on cached and non-cached images.
So now, I am performing browser detection in my Flex app, and enabling caching for all browsers except IE, and my context menus work consistently.
Hope this helps someone else.
September 21st, 2007 at 9:32 am
Hi!
Would you mind explaining me about the use of the superimage?
I think that it needs more documentation!
Thanks in advance!
September 27th, 2007 at 3:24 am
How about for the swf or a swfloader in an itemrenderer of a tilelist?
December 9th, 2007 at 11:03 pm
What is the Maximum cache Limit? I have Implemented this super image Class in my application it is working great but what i want to know is will it create a new cache memory whenever we scroll up and down for the same image. I hope if there are large number of images then cache size will be the Problem. Please help me out in this regard. Any how Good Work. Helped me a lot.
November 25th, 2009 at 2:13 am
Great component! One question though, the component does not render transparant png’s correctly. When using the simple mx:Image everything goes well, using SuperImage the png is not transparant..
Any ideas how to fix this?