Another clock widget for iGoogle, Netvibes, … Step 1: Building a clock with ILOG Elixir
As mashup plateforms like iGoogle or Netvibes become more and more popular in the web 2.0 world, it might be interesting to see how Flex stuff can be integrated with them. The widget we are going to create is probably one of the most classic ones: an analog clock. Searching for “clock” in iGoogle widgets library gives more than 10 pages of results! We will see, in a first post, how to create an analog clock with ILOG Elixir Gauges Framework, then, in a second post, we will integrate it in mashup platforms using the UWA (Universal Widget API) provided by Netvibes. This API has the advantage to be compatible with a lot of mashup platforms like iGoogle, iPhone or Vista.
Creating an analog clock with ILOG Elixir Gauges Framework
An Analog clock may be seen as a circular gauge with:
- a start angle equal to its end angle
- two logical scales (hours, minutes, seconds):
- hours scale: from 0 to 12
- minutes and seconds scale: from 0 to 60
- one scale renderer, which corresponds to minutes, with labels from 1 to 12
- three needles (hours, minutes, seconds)
- a background and a needle cap
Defining logical scales
The first one corresponds to minutes and seconds, as we defined first, it will be the default scale. The second one corresponds to hours.
Scales are defined like this:
<ilog:scales>
<ilog:CircularLinearScale id="sixtyScale" startAngle="90" endAngle="90"
minimum="0" maximum="60" majorTickInterval="5" minorTickInterval="1"/>
<ilog:CircularLinearScale id="twelveScale" startAngle="90" endAngle="90"
minimum="0" maximum="12" majorTickInterval="1" minorTickInterval="1"/>
</ilog:scales>
Defining visual elements
Our clock is made of the following visual elements:
- a background (
CircleRenderer) - a scale renderer (
CircularScaleRenderer) - three needles (
NeedleRenderer) - a needle cap (
CircleRenderer)
The style of the clock is defined using filters, fill and stroke properties of visual elements. The scale renderer minor interval corresponds to minutes or seconds but its labels corresponds to hours: we will consequently define a label function to get correct labels from 1 to 12:
private function formatLabel(t:TickItem):String
{
var v:Number = Number(t.value);
return String(v==0?12:v/5);
}
Animating needles
we now have to animate needles, using a Timer that will dispatch an event once per second (every 1000 milliseconds).
Needles values are computed on each tick as following:
private function init():void{
timer.addEventListener(TimerEvent.TIMER, timerListener);
setTime(0, 0, 0);
timer.start();
}
private function timerListener(e:TimerEvent):void
{
var currentTime:Date = new Date();
var h:Number, m:Number, s:Number;
h = currentTime.hours + (currentTime.minutes * 60 + currentTime.seconds) / 3600;
m = currentTime.minutes;
s = currentTime.seconds;
setTime(h, m, s);
}
private function setTime(h:Number, m:Number, s:Number):void
{
hourNeedle.value = h;
minuteNeedle.value = m;
secondNeedle.value = s;
}
The complete code for this simple analog clock can be found here:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:ilog="http://www.ilog.com/2007/ilog/flex" backgroundColor="0xFFFFFF"
backgroundGradientColors="[]" layout="absolute"
creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.effects.easing.*;
import ilog.gauges.TickItem;
private var timer:Timer = new Timer(1000);
private function formatLabel(t:TickItem):String
{
var v:Number = Number(t.value);
return String(v==0?12:v/5);
}
private function init():void
{
timer.addEventListener(TimerEvent.TIMER, timerListener);
setTime(0, 0, 0);
timer.start();
}
private function timerListener(e:TimerEvent):void
{
var currentTime:Date = new Date();
var h:Number, m:Number, s:Number;
h = currentTime.hours + (currentTime.minutes * 60 + currentTime.seconds) / 3600;
m = currentTime.minutes;
s = currentTime.seconds;
setTime(h, m, s);
}
private function setTime(h:Number, m:Number, s:Number):void
{
hourNeedle.value = h;
minuteNeedle.value = m;
secondNeedle.value = s;
}
]]>
</mx:Script>
<mx:LinearGradient angle="0" id="needleGradient">
<mx:GradientEntry color="0xEEEEEE"/>
<mx:GradientEntry color="0xFFFFFF"/>
<mx:GradientEntry color="0xEEEEEE" alpha="0.9"/>
<mx:GradientEntry color="0xFFFFFF"/>
</mx:LinearGradient>
<mx:Array id="clockFilters">
<mx:DropShadowFilter distance="0.8"/>
</mx:Array>
<ilog:CircularGauge id="clockg" width="100%" height="100%" backgroundAlpha="0"
fontWeight="bold" alpha="1">
<ilog:scales>
<ilog:CircularLinearScale id="sixtyScale" startAngle="90" endAngle="90"
minimum="0" maximum="60" majorTickInterval="5" minorTickInterval="1"/>
<ilog:CircularLinearScale id="twelveScale" startAngle="90" endAngle="90"
minimum="0" maximum="12" majorTickInterval="1" minorTickInterval="1"/>
</ilog:scales>
<ilog:elements>
<ilog:CircleRenderer radius="50%" id="back" filters="{clockFilters}">
<ilog:fill>
<mx:LinearGradient angle="45">
<mx:GradientEntry color="0xFFFFFF"/>
<mx:GradientEntry color="0xEEEEEE"/>
</mx:LinearGradient>
</ilog:fill>
</ilog:CircleRenderer>
<ilog:CircularScaleRenderer radius="48%" filters="{clockFilters}" alpha="0.6"
color="0xFFFFFF" labelFunction="formatLabel" labelFontSize="18%"
majorTickWidth="3%" majorTickLength="14%" minorTickWidth="1.5%"
minorTickLength="7%">
<ilog:minorTickRenderer>
<mx:Component>
<ilog:RectangleTickRenderer>
<ilog:fill>
<mx:LinearGradient angle="90">
<mx:GradientEntry color="0xDDDDDD" alpha="1"/>
<mx:GradientEntry color="0xFFFFFF" alpha="1"/>
</mx:LinearGradient>
</ilog:fill>
</ilog:RectangleTickRenderer>
</mx:Component>
</ilog:minorTickRenderer>
<ilog:majorTickRenderer>
<mx:Component>
<ilog:RectangleTickRenderer>
<ilog:fill>
<mx:LinearGradient angle="-90">
<mx:GradientEntry color="0xDDDDDD" alpha="1"/>
<mx:GradientEntry color="0xFFFFFF" alpha="1"/>
</mx:LinearGradient>
</ilog:fill>
<ilog:stroke>
<mx:Stroke color="0xEEEEEE" alpha="0.5"/>
</ilog:stroke>
</ilog:RectangleTickRenderer>
</mx:Component>
</ilog:majorTickRenderer>
</ilog:CircularScaleRenderer>
<ilog:NeedleRenderer fill="{needleGradient}" id="minuteNeedle" radius="42%"
baseRadius="{-0.2*hourNeedle.percentRadius*hourNeedle.height/100}"
pointRadius="100%" filters="{clockFilters}"/>
<ilog:NeedleRenderer id="hourNeedle" radius="30%"
baseRadius="{-0.2*hourNeedle.percentRadius*hourNeedle.height/100}"
filters="{clockFilters}" fill="{needleGradient}" scale="{twelveScale}"
pointRadius="100%" click="{hourNeedle.invalidateDisplayList()}">
</ilog:NeedleRenderer>
<ilog:NeedleRenderer id="secondNeedle" radius="46%" animationDuration="200"
baseRadius="{-0.2*hourNeedle.percentRadius*hourNeedle.height/100}"
filters="{clockFilters}" fill="{needleGradient}" pointRadius="100%"
thickness="1%" easingFunction="{Exponential.easeOut}"/>
<ilog:CircleRenderer radius="3%" alpha="1" id="cap">
<ilog:filters>
<mx:DropShadowFilter distance="1"/>
</ilog:filters>
<ilog:fill>
<mx:LinearGradient angle="45">
<mx:GradientEntry color="0xCCCCCC"/>
<mx:GradientEntry color="0xFFFFFF"/>
</mx:LinearGradient>
</ilog:fill>
<ilog:stroke>
<mx:Stroke color="0xCCCCCC"/>
</ilog:stroke>
</ilog:CircleRenderer>
</ilog:elements>
</ilog:CircularGauge>
</mx:Application>
You can download the Flex Builder project of the application as a .zip file here. To run it, you will also need to download ILOG Elixir 1.0.
Tags: custom gauge








May 14th, 2008 at 3:17 pm
I’m just beginning to look into Flex, but I really like the look of that clock — any chance of getting that as an AIR app?
May 19th, 2008 at 3:38 am
Hi Jeff,
You can reuse the code as-is in your AIR app.
August 19th, 2008 at 5:42 pm
hi, very nice. does the clock take the internal clock time?, or it use a gmt time, because if the internal clock is wrong the time of the application is wrong too.
August 20th, 2008 at 2:06 am
Hi Sergio,
The clock uses internal clock time. You’re right, it is not perfect, Maybe NNTP could help to enhance this clock accuracy.
Thanks!
August 28th, 2008 at 3:24 am
[…] Clock Widget http://blogs.ilog.com/elixir/2008/02/05/clockwidget1/ […]