January 06, 2010

Simple animation in JavaFX

Having two weeks off during the Christmas holidays gives you some time to create something in JavaFX. I had already used JavaFX before, but animations were a part I didn't yet have any experience in. So I was thinking of remaking a simple banner application that a client once needed for his website. That version was written in Flash and was created by a third party. You can have a look at that animation at http://vi.be.

The application is quite simple. It reads in an XML-file which contains a list of banners. Each banner has an image, a title, a description and a URL. After the XML-file has been read, it displays the banners one after another. The transition between two different banners is done by using a simple fading out/fading in animation. Doing this animation in JavaFX is simple.

First I create a variable to hold the current opacity and bind this to the opacity of the banner. The SingleBanner class used below is nothing more than a CustomNode in which the image, the title and the description are placed.

var bannerOpacity = 1.0;

insert SingleBanner {
banner : banner // the POJO containing all banner info
visible : false // initially all banners are invisible
opacity : bind bannerOpacity // bind the opacity variable to the banner

// when the mouse is hovering over the banner, pause the timeline
onMouseEntered : function(event : MouseEvent) {
timeline.pause();
}
onMouseExited : function(event : MouseEvent) {
timeline.play();
}
} into banners;


To do the actual animation in JavaFX, we need to create a Timeline. A Timeline does nothing more than perform actions at specified "keyframes". You can execute this Timeline once, a few times or even infinitely. Here's the code for our Timeline:

var timeline : Timeline = Timeline {
repeatCount : Timeline.INDEFINITE
keyFrames: [
at (4.75s) {
bannerOpacity => 1.0
},
at (4.85s) {
bannerOpacity => 0.0 tween Interpolator.EASEOUT
}
at (5.0s) {
bannerOpacity => 1.0 tween Interpolator.EASEIN
}
]
}


The syntax is easy to understand and you can clearly see what the Timeline does. 4.75 second after the Timeline was started, it will assign 1.0 to the variable bannerOpacity. Between 4.75 and 4.85 seconds the opacity will be brought down to zero using an EaseOut interpolation. Finally, the opacity will be brought back up to 1.0 with an EaseIn interpolation. The trick is now to make the current banner invisble when the opacity reaches 0. At the same time, the next banner in the array will be made visible. We can do that easily using a replace trigger.

var bannerOpacity = 1.0 on replace {
if (bannerOpacity == 0.0) {
counters[index].active = false;
if (index + 1 >= sizeof(banners)) {
index = 0;
} else {
index++;
}
counters[index].active = true;
}
}

var index = 0;
var banner = bind banners[index] on replace oldBanner {
oldBanner.visible = false;
banner.visible = true;
}


That's all that is required to do some simple animation. The code for the project can be downloaded here. There are only two problems that I couldn't resolve:

  • I have not yet found a way how to open a URL from within JavaFX. This can be achieved with: AppletStageExtension.showDocument(banner.link, "_new");

  • In browser mode the loading of the images takes a long time. In desktop mode it seems to go a lot quicker.



Below you can have a look at the banner application itself.


11 comments:

adder said...

Hi!!

Great work! Goeie werk!

I have been studying your code and and have no problem understanding your explanation or syntax.

I am struggling with the .jsp and XML syntax file. It seems to be very sensitive and any slight unexpected change cause it to break. Could you kindly give an example of this. I am not a good programmer and come from a PHP background. Javafx and xml is all new to me and I am still learning!
Thanks in advance and really like your tutorial!

adder said...

Help!!!

After 7days & 7 nights I still cant figure this out.

I have my own xml file with tags: title, description, source, url.
It wont work! I get a blank screen. As far as I can gather it must be something with the parsing of the image.link function.

When I use the default bannerxml.jsp file it works. I have a bannerxml.xml file. Am I missing something in it?

Joeri Sykora said...

Hey adder,

thanks for having a look at the code.

The program reads in an XML file from a URL. In my sample I point the program to a jsp page which generates XML code. You can just as easily point the program directly to an XML file (as you are trying) or even a PHP page. As long as the response from the URL is valid XML code, there won't be a problem. Of course, the XML code itself must follow the correct structure. Could you perhaps show me the XML code you are using or put a link somewhere so I can have a look at it? Make sure that the text within the title and description tags are enclosed in CDATA tags.

adder said...
This comment has been removed by the author.
adder said...

Hi Joeri,

Thanks for the prompt response!

I dont have a server where I can publish the xml at the moment. I am working from an apache2 server on ubuntu 10.04 localhost although I have tried on another development server here.

Here is a piece of my xml file:
>?xml version="1.0" encoding="UTF-8"?<
>items<
>item<
>title<>![CDATA[1]]<>/title<
>description<>![CDATA[1]]<>/description<
>source/source<
>url/url<
>/item<
>/items<

(I changed the <> order for posting ability)

I have changed the tag in your original. I cannot see it being referenced. Even if I do add it, it makes no difference. I have also copied your original images to the img folder for testing instead of my own and no change to the result. It wont parse the XML file.

I have also tried changing bannerLocation : "{__DIR__}bannerxml.xml" pointing to a local file without success.

I have also paid extra attention that its not a firefox, IE or safari issue having noticed some instability on testing with different browsers.

I can manipulate the ApiClient.fx file and change he banner.title and banner.description to static text and that works fine.

My logic tells me its something with my xml file. I am really stuck here and would appreciate any help you could provide. I dont want to use Adobefx for security and other reasons. I really like the Java alternative.

Thanks for your support so far, really appreciate it!

Cheers,
A

Joeri Sykora said...

Well, the xml looks ok from what you posted, except for the source and url tags, but that could be the result of a typo? Just to make sure however, the tags should be:
>source/source<
>url/url<

Of course, the link to the image must exist.

If all above is correct, you could always do some simple debugging. See for instance if the parseBanners method is being called by using a println() inside the onEvent method. Another thing that you can check is the exception field of the event. This field will be set when an exception occurred while parsing the xml. You can then call event.exception.printStackTrace() or println("{event.exception.message}").

Joeri Sykora said...

Ok, ignore the first part from my comment written above, because it seems that it gets escaped by blogger as well :). So, I assume you wrote the source and url tags correctly.

adder said...

Hi Joeri!

:) Yeah the escape characters doesn't read well!

Yes the tags are correct. I have managed to upload the xml to a live server: http://www.securityicon.co.uk/bannerxml.xml

Ok so trying to alter the code actually requires that one knows what he is doing :) Not a jedi javafx master yet but practising my skills is a lot of fun!

I am reading up on the link you sent and also started editing the code in the parseBanners method to the tune of java.lang.System.out.println(event.text); Once successful I will try my hand on the exception statement.

If you could kindly have a look at my xml file then this would really save me allot of time!

Once again can't thank you enough!

Chat soon,
A

Joeri Sykora said...

I replaced the url of the bannerLocation to your url and of course, it worked :). In other words, there is at least nothing wrong with your xml.

One thing I noticed though, was that it took some time before the images were displayed. I took a quick look and I saw that I didn't specify backgroundLoading : true. By default, images are not loaded in the background, which cause them to block the entire UI while downloading the image. Since you are downloading 11 images, it can take a while before anything shows up.

So, specifying backgroundLoading : true in SingleBanner.fx in the Image declaration should fix this (right below line 53 'url : banner.image'). This will allow your first image to be displayed as soon as it's available, while the other images are being downloaded. Note that it can still take some time to download the images, which makes it appear as if the images are not being displayed. But they should eventually appear.

adder said...

Dear Joeri,

I give up!

If the XML is right then its not working with my configuration. I am going to try and install a new dev machine and try it from there. I love the concept and will let you know when I got it working.

BTW can I contact you offline?

Cheers,
A

adder said...

Right!!

After many moons has passed I finally got some results.

I use a mac snow leopard machine with netbeans 6.9.1 for development. I noticed some very sporadic results.

Whenever I use the ImageView component nothing in the browser will show. Even in an app with only a couple of lines of code. When I build the app to run as standard execution this worked fine. I then installed a virtual machine, Windows XP and found similar result using IE instead of firefox. However if I build the component for a web interface and deploy it on the apache server it works fine without having it worked in the browser before! Bizzarre!! I have been through your code many times and after getting a book on Javafx I now understand most of it. Nothing wrong here only with Netbeans and the various java components and flavours. Really hard to learn these things if things aren't working like it is supposed to. Never now if it is you or the application.

Thanks again for a great tutorial. It has really helped me allot to get on with Javafx.

Cheers,
A