Animation in Flash vs. Animation in C#, round II
Here is the second part of the comparisons of Actionscript and C#, with some good examples.
A large surface, is divided in four sections. Clicking on any of the colored section moves
that section into the center, applying the selected animation mode.
I had used this approach for a UI prototype I was experimenting with...
Let's see how I did this functionality with C#. The C# compiler
is very strict in the sense that it will warn you (relentlessly over and
over again) if you do not define a variable and is type or any other error
is found before compiling. Other than that there are many more differences,
but it has the same eventhandlers that in C# are added to the object.
Instead of using a setInterval, I use a Timer, which works exactly the same way. (source (10kb))
C# code
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Diagnostics;
namespace NextGen_Demo_Animation{
public class Form1 : System.Windows.Forms.Form{
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.Label label1;
private System.ComponentModel.IContainer components;
private System.Timers.Timer timer1;
private System.Windows.Forms.ComboBox comboBox1;
//Miguel's init variables declaratio-----------------------------
private int nowquadrant = 1;
private int toquadrant = 1;
//constants defining limits
const int llimit = 50;
const int rlimit = 400;
const int tlimit = 35;
const int blimit = 300;
private float t;
private float d;
private float b;
private float c;
private float a=0;
private float s=0;
private float p=0;
private int timeNow;
private int timeStart;
private int propxStart;
private int propyStart;
private int propxDest;
private int propyDest;
private int timeDest;
//create 2-dimensional array to contain position info for each quadrant
private int[,] Arr_quadrants = new int [,]{
{0,0 },
{0, 0},
{-360, 0},
{0, -264},
{-360, -264}};
//this array stores current start position when click is performed
private int [] Arr_posstart = new int []{0,0};
//---------------------------------------------------------------
public Form1(){
InitializeComponent();
}
protected override void Dispose( bool disposing ){
if( disposing ){
if (components != null){
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent(){
Resources.ResourceManager resources=
new System.Resources.ResourceManager(typeof(Form1));
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.label1 = new System.Windows.Forms.Label();
this.timer1 = new System.Timers.Timer();
this.comboBox1 = new System.Windows.Forms.ComboBox();
((System.ComponentModel.ISupportInitialize)
(this.timer1)).BeginInit();
this.SuspendLayout();
//
// pictureBox1
//
this.pictureBox1.Enabled = false;
this.pictureBox1.Image=
((Drawing.Bitmap)(resources.GetObject("pictureBox1.Image")));
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(800, 600);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// label1
//
this.label1.BackColor = System.Drawing.SystemColors.Info;
this.label1.Location = new System.Drawing.Point(40, 32);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(152, 152);
this.label1.TabIndex = 1;
this.label1.Text = "label1";
//
// timer1
//
this.timer1.Interval = 1;
this.timer1.SynchronizingObject = this;
this.timer1.Elapsed +=
new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
//
// comboBox1
//
this.comboBox1.Items.AddRange(new object[] {
"linear",
"easeinquad",
"easeoutquad",
"easeinoutquad",
"easeincubic",
"easeoutcubic",
"easeinoutcubic",
"easeinquart",
"easeinexpo",
"easeoutexpo",
"elasticout"});
this.comboBox1.Location = new System.Drawing.Point(200, 160);
this.comboBox1.MaxDropDownItems = 11;
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(121, 21);
this.comboBox1.TabIndex = 2;
this.comboBox1.Text = "linear";
//
// Form1
//
this.AutoScale = false;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(442, 338);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.comboBox1,
this.label1,
this.pictureBox1});
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Form1";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.Text = "NextGen Animation Test by Miguel";
this.TopMost = true;
this.MouseDown +=
new System.Windows.Forms.MouseEventHandler(this.Form_MouseDown);
((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
this.ResumeLayout(false);
}
#endregion
static void Main(){
Application.Run(new Form1());
}
//Miguel's methods below------------------------------------------
private void Form_MouseDown(
object sender,
System.Windows.Forms.MouseEventArgs e) {
//This handles the MouseEventHandler for the form
//first find out wich quadrant to go to according to cursor position
determinequadrant(e.X,e.Y);
//initialize the variables, times and timer component
if(!timer1.Enabled && nowquadrant != toquadrant){
t=0;b=0;c=0;d=0;
timeStart =
(int)((DateTime.Now.Second*1000)+DateTime.Now.Millisecond);
timeDest =
(int)(((DateTime.Now.Second+1)*1000)+DateTime.Now.Millisecond);
Arr_posstart[0] = this.Arr_quadrants[nowquadrant,0];
Arr_posstart[1] = this.Arr_quadrants[nowquadrant,1];
timer1.Stop();
timer1.Enabled = false;
timer1.Enabled = true;
}else{
//MessageBox.Show("busy animating. \n\nLeave me alone...");
}
}
private int tween(int prop){
//this is the method that gets repeatedly
//called to calculate position of the image
this.t =
(float)((Now.Second*1000)+Now.Millisecond-timeStart);
this.b =
(float)Arr_posstart[prop];
this.c =
(float)(this.Arr_quadrants[toquadrant,prop] - Arr_posstart[prop]);
this.d =
(float)timeDest - timeStart;
return getFormula(this.comboBox1.SelectedItem.ToString());
}
private int getFormula(string animType){
//adjust formula to selected algoritm from combobox
switch (animType) {
case "linear":
// simple linear tweening - no easing
return (int)(c*t/d+b);
break;
case "easeinquad":
// quadratic (t^2) easing in-accelerating from zero velocity
return (int)(c*(t/=d)*t + b);
break;
case "easeoutquad":
// quadratic (t^2) easing out-decelerating to zero velocity
return (int)(-c*(t=t/d)*(t-2)+b);
break;
case "easeinoutquad":
// quadratic easing in/out-acceleration until halfway,
//then deceleration
if((t/=d/2)<1)return
(int)(c/2*t*t+b);else return(int)(-c/2*((--t)*(t-2)-1)+b);
break;
case "easeincubic":
// cubic easing in - accelerating from zero velocity
return (int)(c*(t/=d)*t*t + b);
break;
case "easeoutcubic":
// cubic easing in - accelerating from zero velocity
return (int)(c*((t=t/d-1)*t*t + 1) + b);
break;
case "easeinoutcubic":
// cubic easing in - accelerating from zero velocity
if ((t/=d/2)<1)return(int)(c/2*t*t*t+b);
else return(int)(c/2*((t-=2)*t*t+2)+b);
break;
case "easeinquart":
// quartic easing in - accelerating from zero velocity
return (int)(c*(t/=d)*t*t*t + b);
break;
case "easeinexpo":
// exponential (2^t) easing in -
//accelerating from zero velocity
if (t==0) return (int)b; else return
(int)(c*Math.Pow(2,(10*(t/d-1)))+b);
break;
case "easeoutexpo":
// exponential (2^t) easing out -
//decelerating to zero velocity
if (t==d) return (int)(b+c); else return
(int)(c * (-Math.Pow(2,-10*t/d)+1)+b);
break;
case "elasticout":
//Elastic easing out: exponentially decaying sine wave
p=(float)(d*(0.3));
if (t==0) return (int)b;
if ((t/=d)==1) return (int)(b+c);
if(a< Math.Abs(c)){a=c;s=p/4;}else{s =
(float)(p/(2*Math.PI)*Math.Asin(c/a));};
return (int)(a*Math.Pow(2,-10*t) *
Math.Sin( (t*d-s)*(2*Math.PI)/p )+c+b);
break;
default:
return (int)(c*t/d+b);
break;
}
}
private void determinequadrant(int x, int y){
switch(nowquadrant){
case 1:
if(x > rlimit){
if(y < blimit) toquadrant = 2; else toquadrant = 4;
}else{
if(y > blimit) toquadrant = 3;
}
break;
case 2:
if(x < llimit){
if(y < blimit) toquadrant = 1; else toquadrant = 3;
}else{
if(y > blimit) toquadrant = 4;
}
break;
case 3:
if(x > rlimit){
if(y < tlimit) toquadrant = 2; else toquadrant = 4;
}else{
if(y < tlimit) toquadrant = 1;
}
break;
case 4:
if(x < llimit){
if(y < tlimit) toquadrant = 1; else toquadrant = 3;
}else{
if(y < tlimit) toquadrant = 2;
}
break;
}
}
private void timer1_Elapsed(
object sender,
System.Timers.ElapsedEventArgs e) {
label1.Text = "t: "+t + "\nb: " + b+"\nc: " +
c + "\nd: " + d + "\ntimeDest :"+timeDest +
"\ntimeStart: "+timeStart + "\nxpos: " + pictureBox1.Location.X +
"\nYpos: " + this.pictureBox1.Location.Y + "\nDateTime.Now = " +
(int)((DateTime.Now.Second*1000)+DateTime.Now.Millisecond) +
"\n\n"+timeDest +"==" +
(int)((DateTime.Now.Second*1000)+DateTime.Now.Millisecond );
//if time has run out, then reset and stop
if(
(int)timeDest <=
(int)((DateTime.Now.Second*1000)+DateTime.Now.Millisecond) ||
(timeDest-1000) >
(int)((DateTime.Now.Second*1000)+DateTime.Now.Millisecond) ) {
this.timer1.Stop();this.timer1.Enabled =
false;t=0;b=0;c=0;d=0;a=0;s=0;p=0;
this.pictureBox1.Location=new System.Drawing.Point(
this.Arr_quadrants[toquadrant,0], this.Arr_quadrants[toquadrant,1]);
nowquadrant = toquadrant;
}else{
this.pictureBox1.Location =
new System.Drawing.Point(tween(0), tween(1));
}
}
}
}
Now lets's see how it is replicated in Actionscript. This Flash movie uses scripted animation with the help
of Robert Penner's easing algoritms. The original tween method in Actionscript was originally
written by Zeh, but I have modified it a bit. (source (7kb))
Flash Actionscript code
// ActionScript Document by Miguel Moreno (started: 04/18/2003)
//==========================================================================
function init(){
//assign the eventHandlers to each of the boxes
for(i=1;i<=4;++i){
//embeds the id as a property into the box
shell["box_"+i].ID = i;
shell["box_"+i].useHandCursor = false;
shell["box_"+i].onRelease = function(){
if(this.ID != select_id)box_click(this.ID)};
}
//defines the positions for the shell and final position of the selected box
//Arr_posx_shell = new Array(0, -922, 0, -922);
//Arr_posy_shell = new Array(0, 0, -692, -692);
Arr_posx_shell = new Array(-40, -360, -40, -360);
Arr_posy_shell = new Array(-40, -40, -270, -270);
//populates the array and the dropdown combobox
Arr_combo = new Array("linear",
"easeinquad",
"easeoutquad",
"easeinoutquad",
"easeincubic",
"easeoutcubic",
"easeinoutcubic",
"easeinquart",
"easeinexpo",
"easeoutexpo",
"elasticout");
combobox.setDataProvider(Arr_combo);
}
//run init() function
init();
//eventHandler when box is clicked on - starts the animation
function box_click(id){
shell.tween(
["_x","_y", "_xscale", "_yscale"], [Arr_posx_shell[id-1],
Arr_posy_shell[id-1], 78.1, 78.1], 1,
Arr_combo[combobox.getSelectedIndex()], undefined, "adjust_menu", id);
}
// ActionScript Document tween animation code
MovieClip.prototype.tween=function(
prop,
propDest,
timeSeconds,
animType,
delay,
callback,
clickid){
// Sets default values if undefined/invalid
if (timeSeconds < 0.01){timeSeconds = 2;}
if (
animType == undefined ||
animType == ""){animType = "easeOutExpo";} // default equation!
if (delay == undefined){delay = 0;}
// Starts tweening.. prepares to create handling mcs
var properties = new Array(); // Properties. Ex, "_x"
var oldProperties = new Array(); // Old value. Ex, 0
var newProperties = new Array(); // New (target) value. Ex, 100
if (typeof(prop) == "string") {
// Single property
properties.unshift(prop);
oldProperties.unshift(this[prop]);
newProperties.unshift(propDest);
}else {
// Array of properties
for (var i in prop) oldProperties.unshift (this[prop[i]]);
for (var i in prop) properties.unshift (prop[i]);
for (var i in propDest) newProperties.unshift (propDest[i]);
}
for (var i=0; i< oldProperties.length; i++) {
if (newProperties[i] != undefined) {
this.$_tweenCount++;
var newMC = "_doTweenHolderMovieClip_"+properties[i];
//if it already exists, delete it
if (this[newMC]) {
this[newMC].removeMovieClip();
this.$_tweenCount--;
}
//creates the temporary object and sets its properties-------//
this.createEmptyMovieClip(newMC, ++this.$_tweenIndex); //
this[newMC]._prop = properties[i]; //
this[newMC]._propStart = oldProperties[i]; //
this[newMC]._propDest = newProperties[i]; //
this[newMC]._timeStart = getTimer(); //
this[newMC]._timeDest = getTimer()+(timeSeconds*1000); //
this[newMC]._animType = animType; //
this[newMC]._delay = delay; //
this[newMC]._callback = i == 0 ? callback : undefined; //
this[newMC]._clickid = clickid//------------------------//
//simulates a Timer function call as it
//performs function call on every frame pass
this[newMC].onEnterFrame = function() {
// if time has run out trigger block------------------------//
if (this._timeDest + (this._delay*1000) <= getTimer()) { //
// Past the destiny time: ended. //
if (!this.ended) { //
// Ended, first frame. Set it to its final value //
//to make sure there are no discrepancies //
this._parent[this._prop] = this._propDest; //
//
if (this._callback != undefined) { //
// Calls this._callback from the _parent scope //
_parent._parent[this._callback](this._clickid); // //
} //
//
ended = true; // to be deleted next frame (below) //
}else{ //
this._parent.$_tweenCount--; //
this.removeMovieClip(); //
}//-----------------------------------------------------//
}else{
//if not, keep calling below function
this.ended = false;
this._parent[this._prop] = findTweenValue (
this._propStart,
this._propDest,
this._timeStart,
getTimer()-(this._delay*1000),
this._timeDest,
this._animType);
}
}
}
}
}
//tween animation algorithms ------------------------------------------------
this.findTweenValue=function(
_propStart,
_propDest,
_timeStart,
_timeNow,
_timeDest,
_animType){
var t = _timeNow - _timeStart;
var b = _propStart;
var c = _propDest - _propStart;
var d = _timeDest - _timeStart;
poep = "t = " + t + "\nb = " + b + "\nc = " + c + "\nd = " + d
switch (_animType.toLowerCase()) {
case "linear":
// simple linear tweening - no easing
return c*t/d + b;
break;
case "easeinquad":
// quadratic (t^2) easing in - accelerating from zero velocity
return c*(t/=d)*t + b;
break;
case "easeoutquad":
// quadratic (t^2) easing out - decelerating to zero velocity
return -c *(t/=d)*(t-2) + b;
break;
case "easeinoutquad":
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
break;
case "easeincubic":
return c*(t/=d)*t*t + b;
break;
case "easeoutcubic":
return c*((t=t/d-1)*t*t + 1) + b;
break;
case "easeinquart":
return c*(t/=d)*t*t*t + b;
break;
case "elasticout":
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return -(a*Math.pow(2,10*(t-=1)) *
Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
break;
case "easeinoutcubic":
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
break;
case "easeinexpo":
// exponential (2^t) easing in - accelerating from zero velocity
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
break;
case "easeoutexpo": //selected by defaulr
// exponential (2^t) easing out - decelerating to zero velocity
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
break;
}
}