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:

  1. a background (CircleRenderer)
  2. a scale renderer (CircularScaleRenderer)
  3. three needles (NeedleRenderer)
  4. 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.

Bookmark: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Furl
  • Slashdot
  • StumbleUpon
  • Technorati

Tags:

5 Responses to “Another clock widget for iGoogle, Netvibes, … Step 1: Building a clock with ILOG Elixir”

  1. Jeff Edsell Says:

    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?

  2. Damien Mandrioli Says:

    Hi Jeff,

    You can reuse the code as-is in your AIR app.

  3. Sergio Says:

    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.

  4. Damien Mandrioli Says:

    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!

  5. Flex Examples (with source code) « Sharp Mind Says:

    […] Clock Widget http://blogs.ilog.com/elixir/2008/02/05/clockwidget1/ […]

Leave a Reply