Kawasaki Yusuke - Animation.Cube-0.04

Documentation | Source

NAME

Animation.Cube -- Rotating Cube Animation Effect

SYNOPSIS

2-Images-Rotation-Mode:

    var cube = new Animation.Cube( img_id, "next.jpg" );
    cube.rotate();

N-Images-Loop-Mode:

    var list = [
        "first.jpg",
        "second.jpg",
        "third.jpg",
        "forth.jpg"
    ];
    var cube = new Animation.Cube( img_id, list );
    cube.rotate();

DESCRIPTION

This library provides a rotating cube animation effect.

METHODS

cube = new Animation.Cube( id_or_elem, "next.jpg" );

This constructor method returns a new Animation.Cube object. First argument is a <img> element or its id. 2-Images-Rotation-Mode is used when second argument is a image's URL.

cube = new Animation.Cube( id_or_elem, ["1st.jpg","2nd.jpg",...] );

N-Images-Loop-Mode is used when second argument is a array for URLs. Number of images is not limited. I think four images is best for intuitive cube's shape.

cube.rotate();

This method starts a animation effect.

cube.finish();

This method forces to stop a animation effect if it's running.

PROPERTIES

Following properties are available:

    cube.onComplete = function (cur) { ... };
    cube.onInterval = function (cur) { ... };
    cube.background = "#000000";
    cube.waitSeconds = 1.000;
    cube.rotateSeconds = 0.500;
    cube.minResolution =  2;
    cube.maxResolution = 32;
    cube.cursor = 0;
    cube.clockWise = true;

AUTHOR

Yusuke Kawasaki http://www.kawa.net/

COPYRIGHT AND LICENSE

Copyright (c) 2005-2006 Yusuke Kawasaki. All rights reserved. This program is free software; you can redistribute it and/or modify it under the Artistic license. Or whatever license I choose, which I will do instead of keeping this documentation like it is.

// Animation.Cube

if ( typeof(Animation) == "undefined" ) Animation = function () {};

Animation.Cube = function ( ifrom, idest ) {
    this.imageLoaded = false;           // next image is loaded or not
    this.startOnLoad = false;           // start commanded received
    this.is_running = false;            // all done flag
    var init = this.initialize(ifrom,idest);
    if ( ! init ) return;
    return this;
};

Animation.Cube.VERSION = "0.04";
Animation.Cube.prototype.onComplete    = null;      // callback function
Animation.Cube.prototype.onInterval    = null;      // callback function
Animation.Cube.prototype.background    = null;
Animation.Cube.prototype.waitSeconds   = 0.000;
Animation.Cube.prototype.rotateSeconds = 0.500;
Animation.Cube.prototype.viewDistance  = 2.000;     // or 1.414, 1.732
Animation.Cube.prototype.minResolution =  2;
Animation.Cube.prototype.maxResolution = 32;
Animation.Cube.prototype.cursor        =  0;
Animation.Cube.prototype.repeat        = true;
Animation.Cube.prototype.clockWise     = true;

Animation.Cube.prototype.initialize = function ( ifrom, idest ) {
    this.canvas = document.createElement( "div" );
    this.canvas.style.position = "relative";

    // canvas base
    this.imgBase = new Animation.Cube.Image( this.canvas );
    var ebase = this.imgBase.fromElement( ifrom );
    if ( ! ebase ) return;

    this.canvasWidth  = ebase.offsetWidth;
    this.canvasHeight = ebase.offsetHeight;
    ebase.parentNode.insertBefore( this.canvas, ebase );

    var imglist;
    if ( typeof(idest) == "string" ) {
        imglist = [ ebase.src, idest ];
        this.repeat = false;
    } else {
        imglist = idest;
        this.repeat = true;
    }
    this.imgBuffer = [];

    var __this = this;
    var __len  = imglist.length;
    var trigcheck = function (elem) {
        for( var i=0; i<__len; i++ ) {
            if ( ! __this.imgBuffer[i].loaded ) return;
        }
        __this.imageLoaded = true;
        if ( __this.startOnLoad ) {
            __this.startOnLoad = false;
            __this.beginAnimation();
        }
    };

    for( var i=0; i<imglist.length; i++ ) {
        this.imgBuffer[i] = new Animation.Cube.Image( this.canvas );
        this.imgBuffer[i].onLoad = trigcheck;
        this.imgBuffer[i].fromURL( imglist[i] );
    }

    return true;
};

Animation.Cube.appendEvent = function( elem, type, func ) {
    if ( elem.addEventListener ) {
        return elem.addEventListener( type, func, false );
    } else if ( elem.attachEvent ) {
        return elem.attachEvent( "on"+type, func );
    }
};

Animation.Cube.prototype.rotate = function() {
    if ( this.is_running ) return;      // already started
    if ( this.imageLoaded ) {
        this.beginAnimation();
    } else {
        this.startOnLoad = true;  // wait until image loaded
    }
};

Animation.Cube.prototype.beginAnimation = function() {
    if ( this.background ) {
        this.canvas.style.background = this.background;
    }

    var curnext = this.nextCursor(this.cursor);

    this.imgBase.elem.style.visibility = "hidden";

    this.rotation = this.clockWise ? 0.0 : 1.0;
    if ( this.clockWise ) {
        this.imgLeft  = this.imgBuffer[this.cursor];
        this.imgRight = this.imgBuffer[curnext];
    } else {
        this.imgLeft  = this.imgBuffer[curnext];
        this.imgRight = this.imgBuffer[this.cursor];
    }

    this.startWise = this.clockWise;

    this.is_running = true;
    this.timer = new Animation.Cube.Timer(this);    // timer object
    this.timer.start();
};

Animation.Cube.prototype.nextCursor = function(cur) {
    if ( this.clockWise ) {
        cur ++;
        cur = cur % this.imgBuffer.length;
    } else {
        cur --;
        if ( cur < 0 ) cur += this.imgBuffer.length;
    }
    return cur;
};

Animation.Cube.prototype.dispEdge = function() {
    if ( this.timer ) {
        if ( this.timer.is_running() ) this.timer.stop();
        this.timer = null;
    }
    if ( ! this.is_running ) return;

    this.imgLeft.hideClip( 0 );
    this.imgRight.hideClip( 0 );

    if ( this.startWise == this.clockWise ) {
        this.cursor = this.nextCursor(this.cursor);
    }
    this.startWise = null;
    this.imgBase.elem.src = this.imgBuffer[this.cursor].elem.src;
    this.imgBase.elem.style.width  = this.canvasWidth +"px";
    this.imgBase.elem.style.height = this.canvasHeight+"px";
    this.imgBase.elem.style.visibility = "visible";
};

Animation.Cube.prototype.nextAnimation = function() {
    this.dispEdge();
    var __this = this;
    var func = function () {
        if ( ! __this.is_running ) return;
        __this.beginAnimation();
    }
    if ( this.onInterval ) this.onInterval(this.cursor);
    if ( ! this.is_running ) return;
    setTimeout( func, this.waitSeconds*1000 );
};

Animation.Cube.prototype.finish = function() {
    this.dispEdge();
//  this.canvas.parentNode.removeChild( this.canvas );  // clear canvas
    this.is_running = false;
    if ( this.onComplete ) this.onComplete(this.cursor);
};

Animation.Cube.prototype.loop = function(secs,diff,count) {
    if ( ! this.is_running ) return;
    if ( ! this.clockWise ) diff = - diff;
    this.rotation += diff * 0.001 / this.rotateSeconds;

    var fincheck = false;
    if ( this.rotation > 1.0 ) {
        this.rotation = 1.0;
        fincheck = true;
    } else if ( this.rotation < 0.0 ) {
        this.rotation = 0.0;
        fincheck = true;
    }

    this.display( this.rotation );

    if ( fincheck ) {
        if ( this.repeat ) {
            this.nextAnimation();
        } else {
            this.finish();
        }
        return false;
    } else {
        return true;
    }
};

Animation.Cube.prototype.display = function( prog ) {
    var alpha = (1.0-2.0*prog)/4.0*Math.PI;
    var sina = Math.sin( alpha );
    var cosa = Math.cos( alpha );
    var ah = this.viewDistance / ( this.viewDistance +(cosa+sina));
    var bh = 1;
    var ch = this.viewDistance / ( this.viewDistance +(cosa-sina));
    var aw = cosa * ah;
    var bw = sina;
    var cw = -cosa * ch;

    var ow = 0;
    if ( aw >  Math.SQRT1_2 ) ow = aw-Math.SQRT1_2;
    if ( cw < -Math.SQRT1_2 ) ow = cw+Math.SQRT1_2;
    if ( ow ) {
        aw -= ow;
        bw -= ow;
        cw -= ow;
    }

    if ( aw > bw ) {
        this.dispPanel( this.imgRight, bw, bh, aw, ah);
    } else {
        this.imgRight.hideClip( 0 );
    }
    if ( cw < bw ) {
        this.dispPanel( this.imgLeft, cw, ch, bw, bh );
    } else {
        this.imgLeft.hideClip( 0 );
    }
};

Animation.Cube.prototype.dispPanel = function( img, x1, h1, x2, h2 ) {
    if ( x1 > x2 ) {
        var x0 = x1;
        x1 = x2;
        x2 = x0;
        var h0 = h1;
        h1 = h2;
        h2 = h0;
    }

    var lx = Math.round( Math.SQRT1_2 * this.canvasWidth * (x1+Math.SQRT1_2) );
    var lh = Math.round( this.canvasHeight * h1 );
    var ly = Math.round((this.canvasHeight-lh)/2);
    var rx = Math.round( Math.SQRT1_2 * this.canvasWidth * (x2+Math.SQRT1_2) );
    var rh = Math.round( this.canvasHeight * h2 );
    var ry = Math.round((this.canvasHeight-rh)/2);

    var reso = this.canvasWidth;
    if ( ry != ly ) {
        reso = Math.round(Math.abs(2.0*(rx-lx)/(ry-ly)));
        if ( reso > this.maxResolution ) reso = this.maxResolution;
        if ( reso < this.minResolution ) reso = this.minResolution;
    }

    var ww = rx-lx;
    var clipnum = 0;
    for( var ix=lx; ix<rx; ix+=reso ) {
        var prog = (ix-lx)/(rx-lx);
        var iy = Math.round( ly + (ry-ly)*prog );
        var ih = Math.round( lh + (rh-lh)*prog );
        var iw = reso;
        if ( ix+iw > this.canvasWidth ) iw = this.canvasWidth - ix;
        img.dispClip( clipnum, ix,iy,iw,ih,prog,ww );
        clipnum ++;
    }
    img.hideClip( clipnum );
};

//  Animation.Cube.Image class

Animation.Cube.Image = function( work ) {
    this.plane = null;
    this.work = work;           // work div element
    this.elem = null;           // image element
    this.onLoad = null;         // callback function
    this.clipbuf = [];
    this.loaded = false;
    this.lasthided = null;
    return this;
};

Animation.Cube.Image.prototype.getClip = function( num ) {
    if ( this.clipbuf[num] ) return this.clipbuf[num];
    var line = this.elem.cloneNode(true);
    line.style.position   = "absolute";
    line.style.display    = "";
    line.style.visibility = "hidden";
    this.clipbuf[num] = line;
    return line;
};

Animation.Cube.Image.prototype.hideClip = function( num ) {
    var lastclip = this.clipbuf.length;
    if ( this.lasthided != null ) lastclip = this.lasthided;
    for( var i=num; i<lastclip; i++ ) {
        this.clipbuf[i].style.visibility = "hidden";
    }
    this.lasthided = num;
};

Animation.Cube.Image.prototype.dispClip = function( num,left,top,width,height,prog,xcomp) {
    var line = this.getClip( num );
    var offset = Math.round(xcomp*prog);
    var lstyle = line.style;
    lstyle.left   = (left-offset)+"px";
    lstyle.width  = xcomp+"px";
    lstyle.top    = top+"px";
    lstyle.height = height+"px";
    lstyle.visibility = "visible";
    lstyle.clip = "rect(0px,"+(offset+width)+"px,"+(height)+"px,"+offset+"px)";
    this.work.appendChild( line );
};

Animation.Cube.Image.prototype.fromElement = function( elem ) {
    if ( typeof elem == "string" ) {
        elem = document.getElementById(elem);
    }
    if ( ! elem ) return;
    if ( elem.tagName != "IMG" ) return;
    this.elem = elem;
    this.loaded = true;
    return elem;
};

Animation.Cube.Image.prototype.fromURL = function( url ) {
    var elem = document.createElement( "img" );    // new image
    elem.src = url;
    elem.style.visibility = "hidden";
    elem.style.position = "absolute";
    this.work.appendChild( elem );
    this.checkLoaded( elem );
    this.elem = elem;
    return elem;
};

Animation.Cube.Image.prototype.checkLoaded = function( orig ) {
    var check = orig.cloneNode(true);                   // image to check
    var __this = this;
    var ldfunc = function(e){
        if ( ! e && window.event ) e = window.event;    // IE event
        __this.loaded = true;
        check.parentNode.removeChild( check );
        if ( __this.onLoad ) __this.onLoad();
    };
    Animation.Cube.appendEvent( check, "load", ldfunc );        // onload image
    this.work.appendChild( check );
};

//  Animation.Cube.Timer class

Animation.Cube.Timer = function (target) {
    this.target = target;
    this.started = false;
    this.stoped = false;
    this.count = 0;
    var __this = this;
    this.next = function(){
        if ( __this.stoped ) return;
        var now_time = (new Date()).getTime();
        if ( ! __this.begin_time ) __this.begin_time = now_time;
        if ( ! __this.prev_time ) __this.prev_time = now_time;
        var spent_time = now_time-__this.begin_time;
        var diff_time = now_time - __this.prev_time;
        __this.prev_time = now_time;
        var flag = __this.target.loop(spent_time,diff_time,__this.count++);
        if ( flag ) {
            setTimeout( __this.next, 1 );
        } else {
            __this.stop();
        }
    };
    return this;
};

Animation.Cube.Timer.prototype.start = function () {
    this.started = true;
    this.stoped = false;
    this.next();
};

Animation.Cube.Timer.prototype.now = function () {
    return (new Date()).getTime();
};
Animation.Cube.Timer.prototype.stop = function () {
    this.stoped = true;
};

Animation.Cube.Timer.prototype.is_running = function () {
    return ( this.started && ! this.stoped );
};

/*
// ========================================================================

=head1 NAME

Animation.Cube -- Rotating Cube Animation Effect

=head1 SYNOPSIS

2-Images-Rotation-Mode:

    var cube = new Animation.Cube( img_id, "next.jpg" );
    cube.rotate();

N-Images-Loop-Mode:

    var list = [
        "first.jpg",
        "second.jpg",
        "third.jpg",
        "forth.jpg"
    ];
    var cube = new Animation.Cube( img_id, list );
    cube.rotate();

=head1 DESCRIPTION

This library provides a rotating cube animation effect.

=head1 METHODS

=head2 cube = new Animation.Cube( id_or_elem, "next.jpg" );

This constructor method returns a new Animation.Cube object.
First argument is a <img> element or its id.
2-Images-Rotation-Mode is used when second argument is a image's URL.

=head2 cube = new Animation.Cube( id_or_elem, ["1st.jpg","2nd.jpg",...] );

N-Images-Loop-Mode is used when second argument is a array for URLs.
Number of images is not limited. 
I think four images is best for intuitive cube's shape.

=head2 cube.rotate();

This method starts a animation effect.

=head2 cube.finish();

This method forces to stop a animation effect if it's running.

=head2 PROPERTIES

Following properties are available:

    cube.onComplete = function (cur) { ... };
    cube.onInterval = function (cur) { ... };
    cube.background = "#000000";
    cube.waitSeconds = 1.000;
    cube.rotateSeconds = 0.500;
    cube.minResolution =  2;
    cube.maxResolution = 32;
    cube.cursor = 0;
    cube.clockWise = true;

=head1 AUTHOR

Yusuke Kawasaki http://www.kawa.net/

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2005-2006 Yusuke Kawasaki. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the Artistic license. Or whatever license I choose,
which I will do instead of keeping this documentation like it is.

=cut
// ========================================================================
*/