J2ME Polish
J2ME Polish 2.4 Documentation
Enough Software

CSS Animations

With J2ME Polish you can animate the user interface by animating CSS attributes.
Without further ado let's have a look at an example that shows a form with 4 StringItems and 1 ImageItem:

Syntax of CSS Animations

You can animate almost all CSS attributes by adding -animation at the end of the CSS attribute:

menuitem {
	background-color: white;
}

menuitem:hover {
	font-color: white;
	background-color: black;
	background-color-animation {
		on: focus;
		range: white..black;
		duration: 300ms;
	}
}

You can animate any color, integer and percentage CSS attributes. Each animations supports following attributes:

  • on: name of the event that triggers the animation like focus (gains focus), defocus (loses focus), show (element is shown), show-first (shown for the very first time), hide (element is hidden) or your own application specific event names.
  • range: the start and end point of the animation separated by two dots, e.g. blue..red or 0%..50%. The allowed values depend on the CSS attribute in question. You can separate several animation phases by using commas, e.g.
    font-color-animation {
       range: red..blue, blue..green;
    }
    
    When you have the same end- and start-value you can abbreviate it by just using two dots to the next value: range: red..blue..#0f0;
  • duration: The duration of the animation. When there are several animation ranges, you can either specify the complete length or you can specify comma separated durations for each range: duration: 300ms, 1s;. The default duration is 1000ms / 1s.
  • delay: The delay between the event and the start of the animation. You can specify delays for each animation phase by comma separating delay values: delay: 2s, 200ms;. By default there is no delay (0ms).
  • repeat: The number of times this animation should be repeated. Use once if it only should run at the first occurrence of the event. Use always to run this animation until the affected UI element receives a new style. Use a numeric value n to repeat the animation n times.The default value for repeat is 0, meaning that the animation is processed once and not repeated afterwards. repeat: always;
  • fire-event: The event that is fired when this animation ends. You can use this mechanism to chain several independent animations: fire-event: myCustomEvent;
  • function: The function by which values are modified during the animation. Supported values are ease, ease-in, ease-out, linear, exponential-in and exponential-out. ease results in a slow beginning (ease-in) or ending (ease-out) or both a smooth ending and beginning (ease). The linear function will change the value purely linear to the passed time. exponential-in starts slowly and then accelerates exponentially, exponential-out begins exponentially and then decelerates. The ease function is the default function: function: ease;

Reacting to Events

With the on attribute you specify on which event an animation should be started. You can react to any kind of events either system generated or your own custom events (like "message-received", for example).

Available J2ME Polish events are listed here:

  • show: triggered when a screen or item is shown. Note that the item does not need to be visible necessarily.
  • hide: triggered when a screen or item is hidden.
  • focus: triggered when an item receives the focus. Use this trigger in the [style-name]:hover style:
    .menuItem {
    	font-color: #222;
    }
    .menuItem:hover {
    	font-color: #eee;
    	background-color: black;
    	font-color-animation {
    		on: focus;
    		range: #222..#eee;
    		duration: 500ms;
    	}
    }
    
  • defocus: triggered when an items loses the focus.
  • press: triggered when an item is pressed, e.g. by clicking and holding the FIRE button of the phone. Use such animations in the [style-name]:press style:
    .menuItem {
    	font-color: #222;
    }
    .menuItem:hover {
    	font-color: #eee;
    	background-color: black;
    	font-color-animation {
    		on: focus;
    		range: #222..#eee;
    		duration: 500ms;
    	}
    }
    .menuItem:press {
    	font-color: #eee;
    	background-color: black;
    	font-color-animation {
    		on: press;
    		range: #eee..#f00..#eee;
    		duration: 200ms, 100ms;
    	}
    }
    
  • unpress: triggered when the item is not pressed anymore, e.g. by releasing the FIRE key on the phone.

You can easily use and react to your own custom events - you only need to tell J2ME Polish about such events by calling EventManager.fireEvent( String name, Object source, Object data ). Let's clarify this by an example:

In the polish.css file use the custom event name in the on attribute:

.messageIndicator {
	background-color-animation {
		on: message-received;
		range: #fff..#0f0..#fff;
		repeat: 1;
		duration: 500ms;
	}
}

In your application simply notify J2ME Polish about the event when it occurs. Provide the UI element that should consume the event as the source in EventManager.fireEvent():

import de.enough.polish.event.EventManager;

public class MessageConsumer {
	public void init() {
		//#style messageIndicator
		this.messageIndicator = new StringItem();
		...
	}
	public void onMessageReceived( Message msg ) {
		this.messageIndicator.setText( "You have " + (++this.unreadMessage) + " messages" );
		EventManager.fireEvent( "message-received", this.messageIndicator, null );
	}
}

You can also use de.enough.polish.ui.UiAccess for triggering animation events for J2ME Polish components like the title or the menubar:

  • UiAccess.fireEvent( String name, Screen screen, Object data ): fires the event for the screen and all its embedded UI elements. Please note that this method traverses all UI components and may take a while.
  • UiAccess.fireEvent( String name, Item item, Object data ): fires the event for the given item and all it's subitems.
  • UiAccess.fireEventForTitleAndMenubar( String name, Screen screen, Object data): fires the specified event for the screen's title and menubar.

Let's move out the menubar and the title by firing a custom event. First we define the animations in the screen's title and menubar styles:

backgrounds {
	imageTitle {
		type: mask;
		mask: imageTitleMask;
	 	mask-color: blue;
	 	background: imageTitleTmp; 
	 	opacity: 200;
	}
	imageTitleMask {
		type: simple;
		color: blue;
	}
	imageTitleTmp {
		type: vertical-gradient;
		top-color: #7A7A7A;
		bottom-color: #323232;
	}
}
.screenImage
{
	padding: 2%;
	padding-vertical: 1%;
	background-color: #000;
	title-style: imageTitle;
	menubar-style: imageMenubar;
}

.imageTitle extends title{
	background: imageTitle;
	y-adjust-animation {
		on: goFullscreen;
		range: 0%..-100%;
	}
	y-adjust-animation {
		on: leaveFullscreen;
		range: -100%..0%;
	}
}
imageMenubar extends menubar {
	background {
		type: vertical-gradient;
		top-color:  rgb(98, 120, 139);
		bottom-color: rgb(53, 75, 92);
	}
	y-adjust-animation {
		on: goFullscreen;
		range: 0%..100%;
	}
	y-adjust-animation {
		on: leaveFullscreen;
		range: 100%..0%;
	}
}

Now we can hide the menubar and title by using UiAccess:

UiAccess.fireEventForTitleAndMenubar( "goFullscreen", myImageScreen, null );

Showing the title and menubar again is done by firing the leaveFullscreen event:

UiAccess.fireEventForTitleAndMenubar( "leaveFullscreen", myImageScreen, null );

Chaining CSS Animations

You can chain several animations for different CSS attribute with the fire-event attribute. In this example we start animating the background colors of the screen first - at the end we fire the intro-finish event. This event is then picked up by one of the string items which plays an animation before starting yet another one.

.introScreen {
	layout: vertical-center;
	padding: 2%;
	padding-top: 8%;
	background {
		type: combined;
		foreground: snowflakes;
		background: gradient;
	}
	background-snowflakes-flake-color-animation {
		on: show;
		range: white..#600;
		duration: 12s;
		delay: 3s;
		repeat: once;
	}
	background-snowflakes-number-of-flakes-animation {
		on: show;
		range: 5..10;
		duration: 10s;
		repeat: once;
	}
	background-snowflakes-max-flake-size-animation {
		on: show;
		range: 10..16;
		duration: 10s;
		repeat: once;
	}
	background-vertical-gradient-top-color-animation {
		on: show;
		range: #eef..#a00..#200..#000;
		duration: 6s, 4s, 2s;
		fire-event: intro-finish;
		repeat: once;
	}
	background-vertical-gradient-bottom-color-animation {
		on: show;
		range: #009..black;
		duration: 10s;
		repeat: once;
	}
}

.introText {
	padding-top: 3px;
	padding-bottom: 3px;
	font-color: #d00;
	font-style: bold;
}

.introText1 extends introText {
	x-adjust: -110%;
	x-adjust-animation {
		on: intro-finish;
		range: -100%..0%;
		duration: 3s;
		fire-event: intro-text1-finish;
	}
}

.introText2  extends introText {
	layout: right;
	x-adjust: 110%;
	x-adjust-animation {
		on: intro-text1-finish;
		range: 100%..0%;
		duration: 3s;
		fire-event: intro-text2-finish;
	}
}

.introImage {
	padding-left: 20%;
	opacity: 0;
	opacity-animation {
		on: intro-text1-finish;
		range: 0..200..0..180..0..255;
		duration: 100ms,100ms,100ms,100ms, 300ms;
		delay: 1500ms,0ms, 500ms, 300ms,100ms;
	}
}

Since we fire events by one UI element that should be consumed by other UI elements, we need to remap such events. You can do this by calling EventManager.remapEvent(String eventName, Object remappedSource)::

private void showIntro() {
       //#style introScreen
       Form form = new Form(null);
       //#style introText1
       StringItem introText1 = new StringItem( null, "When the night falls...");
       form.append(introText1);
       //#style introText2
       StringItem introText2 = new StringItem( null, "...vampires come out!");
       form.append(introText2);
       ImageItem imageItem = null;
       try {
       	Image image = Image.createImage("/vampire.png");
       	//#style introImage
       	imageItem = new ImageItem( null, image, ImageItem.PLAIN, null);
       	form.append(imageItem);
       } catch (Exception e) {
       	//#debug error
       	System.out.println("Unable to load image " + e );
       }
       //#style introText3
       StringItem introText3 = new StringItem( null, "Only you can stop them!");
       form.append(introText3);
       //#style pressKeyText
       StringItem pressKeyText = new StringItem( null, "Press Fire");
       pressKeyText.setDefaultCommand(this.cmdSkipIntro);
       form.append(pressKeyText);
       form.setCommandListener(this);
       
       EventManager manager = EventManager.getInstance(); 
       manager.remapEvent("intro-finish", introText1);
       manager.remapEvent("intro-text1-finish", introText2);
       if (imageItem != null) {
       	manager.remapEvent("intro-text1-finish", imageItem);
       }
       manager.remapEvent("intro-text2-finish", introText3);
       manager.remapEvent("show-press-key", pressKeyText);
       
       DeviceControl.lightOn();
       this.display.setCurrent(form);
   }

This chaining results in the following animation:

Simple CSS Animations

Let's have a look at a simple example:

.mainCommand {
	margin-top: 2px;
	padding: 5px;
	padding-left: 20%;
	font-color: #333;
	font-style: bold;
	font-size: medium;
	layout: expand;
}

.mainCommand:hover {
	font-color: #ccc;
	background-color: #ddd;
	background-color-animation {
		on: focus;
		range: #ddd..#333;
		duration: 1s;
	}
}

The above example animates the background color of elements from a light to very dark gray (range: #ddd..#eee;) when they receive the focus (on: focus;) in 1 second (duration: 1s;):

You can also also animate normal numerical values, in the following example we animate the horizontal position of all elements. This animation is triggered when the elements are shown for the first time (on: show-first). We delay the 'show' event that is triggered for each element by 200ms by using show-delay: 200ms; in the corresponding screen's style. This delays the start of the animations, so that not all elements are moved at the same time.

.mainScreen {
	background-color: #ddd;
	layout: vertical-center;
	show-delay: 200ms;
}

.mainCommand {
	margin-top: 2px;
	padding: 5px;
	padding-left: 20%;
	font-color: #333;
	font-style: bold;
	font-size: medium;
	layout: expand;
	x-adjust: -100%;
	x-adjust-animation {
		on: show-first;
		range: -100%..0%;
	}
}

.mainCommand:hover {
	font-color: #ccc;
	background-color: #ddd;
	background-color-animation {
		on: focus;
		range: #ddd..#333;
		duration: 1s;
	}
}

Sample Application

Please have a look at the animation sample application for a real world example.

back to top