Link to home
Start Free TrialLog in
Avatar of error77
error77

asked on

Image loading / preloading in Flex

Hi all,

I'm loading some online images and it works fine but when i use the slider to scroll the images it jumps from one to the other.
How could I avoid that please. Either preloading or any other way.
Here is the current code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="loadXML()">
      <mx:Image source="{pics.pic[Math.floor(picSlider.value)].imageurl}"/>
      <mx:Label text="{pics.pic[Math.floor(picSlider.value)].title}"/>
      <mx:Script><![CDATA[
            [Bindable]
            private var pics:XML;
            private function loadXML():void{
                  var loader:URLLoader = new URLLoader();
                  var request:URLRequest = new URLRequest("http://mydomain.com/flash/pics.xml");
                  loader.addEventListener(Event.COMPLETE, onComplete);
                  loader.load(request);
            }
            private function onComplete(event:Event):void {
                  pics = new XML(event.target.data);
            }
      ]]></mx:Script>
     
      <mx:HSlider id="picSlider" minimum="0" maximum="3.9" value="1"/>
</mx:Application>


And the xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>

<pics>
  <pic>
    <imageurl>http://mydomain.com/flash/image1.jpg</imageurl>
    <title>iamge1</title>
  </pic>

 <pic>
    <imageurl>http://mydomain.com/flash/image2.jpg</imageurl>
    <title>iamge2</title>
  </pic>
</pics>

Thanks
Avatar of amurauyou
amurauyou

The Image and Label components cannot recognize the float values sent with HSlider component CHANGE event. It would be better if you use some container (Box for example) with horizontalScrollPolicy=auto where you will add as much Image and Label components as XML data require.
If you still will need HSlider, it would be better to use Canvas and Box inside it, so that when the slider value's changed - the Box x position goes left and it looks like scrolling with HSlider.
Avatar of petiex
This seems to do the trick. It gets rid of the data binding on the XML, too, which is always a good thing.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="loadXML()" width="100%" height="100%">
    <mx:Script><![CDATA[
        private var pics:XML;
        private var even:Boolean;
        private var lastIndex = -1;

        private function loadXML():void {
            var loader:URLLoader = new URLLoader();
            var request:URLRequest = new URLRequest("http://localhost:9080/images/pics.xml");
            loader.addEventListener(Event.COMPLETE, onComplete);
            loader.load(request);
        }

        private function onComplete(event:Event):void {
            pics = new XML(event.target.data);
            picSlider.maximum = pics.children().length() - .1;
            updateImages(null);
        }

        private function updateImages(event:Event):void {
            var idx:int = Math.floor(picSlider.value);
            if (idx != lastIndex) {
                even = !even;
                evenImage.visible = even;
                oddImage.visible = !even;
                if (even) {
                    evenImage.source = pics.pic[idx].imageurl;

                } else {
                    oddImage.source = pics.pic[idx].imageurl;
                }
                picLabel.text = pics.pic[idx].title;
                lastIndex = idx;
            }
        }
        ]]></mx:Script>
    <mx:Fade id="fadeOut" duration="1000" alphaFrom="1.0" alphaTo="0.0"/>
    <mx:Fade id="fadeIn" duration="1000" alphaFrom="0.0" alphaTo="1.0"/>
    <mx:HSlider id="picSlider" minimum="0" maximum="3.9" value="0" change="updateImages(event)" liveDragging="true"/>
    <mx:Label id="picLabel"/>
    <mx:Canvas width="100%" height="100%">
        <mx:Image id="oddImage" showEffect="{fadeIn}" hideEffect="{fadeOut}"/>
        <mx:Image id="evenImage" showEffect="{fadeIn}" hideEffect="{fadeOut}"/>
    </mx:Canvas>
</mx:Application>

Open in new window

To be on the safe side with this, you might change the mx:Application's initialize="loadXML()" to creationComplete="loadXML()". That way, you won't get a null object reference on the off-chance that the client loads the XML before the slider and image objects have been created.
OK, one more thing. With liveDragging="true" on the slider, it is possible to change from one image to the other and back in such a way that you change the visibility from false to true on an image while it is running the fadeOut effect, and you end up with nothing visible.

If you leave liveDragging at the default value of false, that situation should be next to impossible, particularly if you set the Fade durations a little shorter, say 500. Shorter liveDragging durations will also make the situation less likely if you do want to go with liveDragging.
I've been playing with this a bit (because it's more fun than what I should be doing) and I've come up with something that handles the interrupted fadeOut effect pretty well. Here it is:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="loadXML()"
                width="100%" height="100%">
    <mx:Script><![CDATA[
        import mx.events.EffectEvent;

        private var pics:XML;
        [Bindable]
        private var even:Boolean;
        private var lastIndex:int = -1;
        private var currentIndex:int = 0;

        private function loadXML():void {
            var loader:URLLoader = new URLLoader();
            var request:URLRequest = new URLRequest("http://localhost:9080/images/pics.xml");
            loader.addEventListener(Event.COMPLETE, onComplete);
            loader.load(request);
        }

        private function onComplete(event:Event):void {
            pics = new XML(event.target.data);
            picSlider.maximum = pics.children().length() - .1;
            updateImages(null);
        }

        private function updateImages(event:Event):void {

            currentIndex = Math.floor(picSlider.value);
            if (currentIndex != lastIndex) {

                var go:Boolean = true;
                if (fadeOut.isPlaying) {
                    fadeOut.addEventListener(EffectEvent.EFFECT_END, toggleImages);
                    go = false;
                } else {
                    toggleImages(null);
                }

            }

        }

        private function toggleImages(event:EffectEvent):void {
            if (event != null) {
                event.target.removeEventListener(event.type, toggleImages);
                event.target.reverse();
            }
            if (lastIndex != currentIndex) {
                even = !even;
                evenImage.visible = even;
                oddImage.visible = !even;
                if (even) {
                    evenImage.source = pics.pic[currentIndex].imageurl;
                } else {
                    oddImage.source = pics.pic[currentIndex].imageurl;
                }
                picLabel.text = lastIndex + ". " + pics.pic[currentIndex].title;
                lastIndex = currentIndex;
            }

        }

        ]]></mx:Script>

    <mx:Fade id="fadeOut" duration="1000" alphaFrom="1.0" alphaTo="0.0"/>
    <mx:Fade id="fadeIn" duration="1000" alphaFrom="0.0" alphaTo="1.0"/>

    <mx:HSlider id="picSlider" minimum="0" maximum="3.9" value="0" change="updateImages(event)" liveDragging="true"/>
    <mx:Label id="picLabel"/>
    <mx:Canvas width="100%" height="100%">
        <mx:Image id="oddImage" showEffect="{fadeIn}" hideEffect="{fadeOut}"/>
        <mx:Image id="evenImage" showEffect="{fadeIn}" hideEffect="{fadeOut}"/>
    </mx:Canvas>
</mx:Application>

Open in new window

Avatar of error77

ASKER

Hi petiex, sorry about the delay, just arrived :o)
It's a nice effect and a lot better but the issue here is that I need to preload the images or as many as i can.
The idea is that it's going  to have to handle lots of them, so if preloaded it will be really quick.
Does it make sense?

Thanks

ASKER CERTIFIED SOLUTION
Avatar of petiex
petiex
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of error77

ASKER

Thanks :o) That's better. The only problem that I have with it is the it flickers between images.
If the images are preloaded, shouldn't it not do that?
If it just didn't have that flickering it would be great ... can that be avoided?
I don't get any flickering. Without the preloader, however, the image sometimes finishes loading after the fadeIn effect completes. Is it possible your image server is adding no-cache directives in the header? I'll attach the XML I am using, a few wikipedia images, which caches for me with no problem. Do you get the same behavior with that?
<pics>
            <pic> <imageurl>http://www.wunderkabinett.co.uk/gallery/albums/userpics/10002/SmallWorlds2.JPG</imageurl> <title>Flower</title> </pic>
            <pic> <imageurl>http://upload.wikimedia.org/wikipedia/commons/3/33/BennyGoodmanandBandStageDoorCanteen.jpg</imageurl> <title>Benny Goodman</title></pic>
            <pic> <imageurl>http://upload.wikimedia.org/wikipedia/commons/2/20/La_china.jpg</imageurl> <title>Lady</title> </pic>
            <pic> <imageurl>http://www.iconarchive.com/icons/visualpharm/animals/256/Elephant-icon.png</imageurl> <title>Elephant</title> </pic>
      </pics>

Open in new window

Avatar of error77

ASKER

cant get this data to load. Will keep trying
Avatar of error77

ASKER

hmm still not able to use your xml. Any other ideas pls?
I don't know whether the flickering is from the images not caching or from events tripping over each other in the actionscript.

Can you inspect the response headers you get back from the image requests you are able to load?
If they look anything like this:

Expires: <Some date in the past>
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache

then, you need to change the headers the server is setting on the images.

Does the flickering still happen if you set liveDragging=false on the slider?
Avatar of error77

ASKER

Will check it out asap
Avatar of error77

ASKER

Thanks!
Out of curiosity, what was the issue? Were the images not caching, or did you just decide to go with liveDragging=false? Or a combination? Thanks for the expert poinks!
Avatar of error77

ASKER

Well, a combination of both. Thanks a lot! Ps: I don't suppose you could help me on my other question?
It's basically the same code but I need to search the XML file/nodes... I cannot find anything on google on that :-/
Thanks anyway!