Vixiom Axioms

January 22, 2007

Trim the fat with HAML

Filed under: Ruby on Rails Alastair @ 9:02 am

The announcement of Rails 1.2 wasn’t the only interesting post on weblog.rubyonrails.org, I also found out about HAML a templating system for Rails that cleans up your code and banishes those ugly ASP type tags.

If you look at the two partials of a form you’ll see that you not only save a few lines of code (and even more characters), but it’s much more legible and easier to maintain down the road.

<div class=“block”>
    <h2>Details</h2>
    <dl>
        <dt class=“required”><label for=“country_continent_id”>Continent:</label></dt>
        <dd>
            <%= f.collection_select(:continent_id, @continents, :id, :name) %>
        </dd>
        <dt class=“required”><label for=“country_name”>Country:</label></dt>
        <dd>
            <%= f.text_field :name %>
        </dd>
        <dt class=“required”><label for=“country_content”>Content:</label></dt>
        <dd>
            <%= f.text_area :content, :rows=>10 %>
        </dd>
    </dl>
</div>

pre-HAML _form.rhtml

.block
  %h2 Details
  %dl
    %dt.required
      %label{:for => country_continent_id} Continent:
    %dd= f.collection_select(:continent_id, @continents, :id, :name)
    %dt.required
      %label{:for => country_name} Country:
    %dd= f.text_field :name
    %dt.required
      %label{:for => country_content} Content:
    %dd= f.text_area :content, :rows=>10

post-HAML _form.haml

In HAML divs are implicit so you can just type the class name (.block) and it will give you a div for free, tags start with ‘%’ (%p, %table, %h1) and are auto-closed.

HAML installs as a plugin and any .haml files will overide their .rhtml brothers. On the HAML site there’s a great tutorial and reference.

There’s already a TextMate bundle for HAML here.

Digg! submit Trim the fat with HAML to stumbleupon.com submit Trim the fat with HAML to del.icio.us submit Trim the fat with HAML to reddit.com Like this post? subscribe to the feed.

January 19, 2007

Rails 1.2 is go!

Filed under: Ruby on Rails Alastair @ 6:29 pm

I’ve been building a couple of sites on ‘edge’ rails and just in time the official 1.2 release is released.

Digg! submit Rails 1.2 is go! to stumbleupon.com submit Rails 1.2 is go! to del.icio.us submit Rails 1.2 is go! to reddit.com Like this post? subscribe to the feed.

January 12, 2007

Scripting animation in ActionScript

Filed under: ActionScript, Flash Alastair @ 10:45 am

Scripting animation has many benfits including improved performance, more control, and most importantly it saves time. There’s a great animation library called Fuse which let’s you animate everything from a MovieClip’s position to the anmount of blur. While a complete animation library it’s also pretty complicated with pages and pages of documentation and I - being a lazy programmer - like to keep things simple so I wrote my own very basic Tweener class.

It let’s you animate any property (or multiple properties) that isn’t controlled by a filter. It also has simple ‘onStart’ and ‘onComplete’ callbacks for letting you know when it starts and finishes animating. Here’s a FLA with an example, and the code…

In your FLA file (the only confusing bit is that I shortened the easeStrengths and easeTypes ‘r’ = ‘Regular’ and ‘eio’ = ‘easeInOut’ see the class for the details)…

import com.vixiom.animation.Tweener;

tweener = new Tweener();
// event listeners
tweener.addEventListener(“onStart”, this.onStart);
tweener.addEventListener(“onComplete”, this.onComplete);
// this tween is relative to it’s start position 
// addTween (movieClip, property, easeStrength, easeType, beginValue, finsihValue, seconds, delay, relative?)
tweener.addTween(circle, “_x”, “r”, “eio”, circle._x, 400, 2, 0, true);
// these tweens use fixed values
tweener.addTween(circle2, “_x”, “r”, “eio”, 50, 450, 1, 2);
tweener.addTween(circle2, “_alpha”, “r”, “eio”, 0, 100, 1, 2);
// start the animation
tweener.start();

// the onStart method
function onStart ()
{
    trace(“// on start!”);
}

// the onComplete method
function onComplete ()
{
    trace(“// on complete!”);
}

The Tweener class…

/**
   @class Tweener
   @author Alastair Dawson
   @copyright 2007 Vixiom Communcations, LLC
*/

import mx.transitions.*;
import mx.transitions.easing.*;
import mx.utils.Delegate;

class com.vixiom.animation.Tweener
{
    private var tweens:Array;
    private var tweenerFinished:Function;
    private var runTime:Number;

    private var delayID:Number;
    private var callBackID:Number;

    private var i = 0;

    // event dispatching
    function dispatchEvent() {};
    function addEventListener() {};
    function removeEventListener() {};

    /**
    * Constructor
    */
    public function Tweener ()
    {
        // initialize as a broadcaster
        mx.events.EventDispatcher.initialize(this);

        // tweens array
        tweens = [];

        // runtime
        runTime = 0;
    }

    /**
    * Add Tween
    *
    *   @param      mc      movieClip
    *   @param      p       property
    *   @param      es      easeStrength
    *   @param      et      easeType
    *   @param      b       begin value
    *   @param      f       finish value
    *   @param      s       seconds (duration)
    *   @param      d       delay (duration in seconds)
    *   @param      r       relative (move relative to current position, false by default)
    *
    */

    // add properties
    public function addTween (mc:MovieClip, p:String, es:String, et:String, b:Number, f:Number, s:Number, d:Number, r:Boolean)
    {
        tweens[i] = new Tween();

        tweens[i].obj = mc;
        tweens[i].prop = p;

        if (r == true) {
            tweens[i].begin = tweens[i].obj[tweens[i].prop] + b;
            tweens[i].finish = tweens[i].obj[tweens[i].prop] + f;
        } else {
            tweens[i].begin = b;
            tweens[i].finish = f;
        }

        tweens[i].duration = s;
        tweens[i].useSeconds = true;
        tweens[i].func = convertEase(es,et);

        // set delay
        tweens[i].delay = d;

        // set this tween’s runTime
        tweens[i].runTime = s + d;

        // increment
        i++;
    }

    // start
    public function start ()
    {
        for (var j = 0; j < i; j++)
        {
            // does it have a delay if so set interval
            if (tweens[j].delay != undefined && tweens[j].delay != 0) {
                tweens[j].delayID = setInterval (this, “startTween”, (tweens[j].delay * 1000), tweens[j]);
            } else {
                startTween(tweens[j])
            }

            // check if it’s the longest running tween
            if (tweens[j].runTime > runTime) {
                runTime = tweens[j].runTime;
            }
        }

        // onStart
        var eventObj:Object={target:this,type:“onStart”}
        dispatchEvent(eventObj);

        // onComplete
        callBackID = setInterval (this, “onComplete”, (runTime * 1000));
    }

    // startTween
    private function startTween(t:Object)
    {
        t.start();
        clearInterval(t.delayID);
    }

    // stop
    private function stop()
    {
        for (var j = 0; j < i; j++) {
            tweens[j].stop();
        }
    }

    // resume
    private function resume()
    {
        for (var j = 0; j < i; j++) {
            tweens[j].resume();
        }
    }

    // rewind
    private function rewind()
    {
        for (var j = 0; j < i; j++) {
            tweens[j].rewind();
        }
    }

    // fforward
    private function fforward()
    {
        for (var j = 0; j < i; j++) {
            tweens[j].fforward();
        }
    }

    // callBack
    private function onComplete()
    {
        // broadcast message
        var eventObj:Object={target:this,type:“onComplete”}
        dispatchEvent(eventObj);

        // clear interval
        clearInterval(callBackID);
    }

    // convert easeStrenght
    private function convertEase (es:String, et:String):Object
    {
        var easeStrength:String = es;
        var easeType:String = es;
        var esObj:Object;

        switch (es) {
            case “b”:
                esObj = Bounce;
                break;
            case “k”:
                esObj = Back;
                break;
            case “e”:
                esObj = Elastic;
                break;
            case “n”:
                esObj = None;
                break;
            case “r”:
                esObj = Regular;
                break;
            case “s”:
                esObj = Strong;
                break;
            default:
                esObj = None;
        }

        switch (et) {
            case “e”:
                et = “easeNone”;
                break;
            case “ei”:
                et = “easeIn”;
                break;
            case “eo”:
                et = “easeOut”;
                break;
            case “eio”:
                et = “easeInOut”;
                break;
            default:
                et = “easeNone”;
        }

        return esObj[et];

    }

    /**
    * Event dispatcher
    *
    *   @param      d       data
    *   @param      et      eventType
    *
    */
    function dispatch(et)
    {
        // broadcast message
        var eventObj:Object={target:this,type:et}
        dispatchEvent(eventObj);
    }

}

That’s it! no too heavy man :P

*UPDATE*

If you’re using this in a class it’s best to use Delegate to access the onComplete or onStart methods so you don’t lose scope

tweener.addEventListener(“onComplete”, Delegate.create (this, this.onComplete));
Digg! submit Scripting animation in ActionScript to stumbleupon.com submit Scripting animation in ActionScript to del.icio.us submit Scripting animation in ActionScript to reddit.com Like this post? subscribe to the feed.

Powered by WordPress