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..redor0%..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 defaultdurationis1000ms/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 nodelay(0ms). - repeat:
The number of times this animation should be repeated. Use
onceif it only should run at the first occurrence of the event. Usealwaysto run this animation until the affected UI element receives a new style. Use a numeric valuento repeat the animation n times.The default value forrepeatis 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-inandexponential-out.easeresults in a slow beginning (ease-in) or ending (ease-out) or both a smooth ending and beginning (ease). Thelinearfunction will change the value purely linear to the passed time.exponential-instarts slowly and then accelerates exponentially,exponential-outbegins exponentially and then decelerates. Theeasefunction 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]:hoverstyle:.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]:pressstyle:.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.
