Scaling Up and Down

Download the code for Interactive Animation

We've created switch behaviors that turn on and off the cycling and movement of a sprite. Next we'll add a couple more switch behaviors that animate a sprite's scale and opacity.

The two new switch behaviors are scaling-up-down.js and alpha-up-down.js. They're a little different than than switch behaviors that turned on and off an animated behavior—with each click, they either increase or decrease an animated behavior.

You'll need a contemporary browser with javascript turned on to be able to see this page

Setup Script

Let's look at the setup script that adds the behaviors to the sprite's behavior array. You can find the script under js/projects/interactive-animation/scaling-alpha-up-down-setup.js.

var ScalingAlphaUpDownSetup = function (setupArgs) {
  "use strict";
  //setupArgs currently passes in a reference to the project's instantiation of the Main class
  if (!setupArgs) {
	  setupArgs = {};
  }
  
  this.blueRect = {
	spriteProps:{name:'blue rect', x: 600, y: 400, w: 1200, h: 800, fillColor:'#0e5190', visible:true}, 
	painters:[new RectPainter()], 
	behaviors:[]
  };
  this.myBackground = new ShapeLineGraphic(this.blueRect);
  	
  
  this.catSheet = [
	{ x:0, y:0, w:400, h:220 },
	{ x:400, y:0, w:400, h:220 },
	{ x:800, y:0, w:400, h:220 },
	{ x:1200, y:0, w:400, h:220 },
	{ x:1600, y:0, w:400, h:220 },
	
	{ x:0, y:220, w:400, h:220 },
	{ x:400, y:220, w:400, h:220 },
	{ x:800, y:220, w:400, h:220 },
	{ x:1200, y:220, w:400, h:220 },
	{ x:1600, y:220, w:400, h:220 },
	
	{ x:0, y:440, w:400, h:220 },
	{ x:400, y:440, w:400, h:220 },
	{ x:800, y:440, w:400, h:220 },
	{ x:1200, y:440, w:400, h:220 },
	{ x:1600, y:440, w:400, h:220 },
  ];
			  
  this.catAnim = {
	spriteProps:{name:'catAnim', x:410, y:340, w:400, h:220, XYscale: [0.2, 0.2], alpha: 0.4, graphics:['img/cat-sprite-sheet.png'], sheets:[this.catSheet], rotation: 0.2,  visible:true}, 
	painters:[new BitmapPainter()], 
	behaviors:[new Cycling({cycleTime:60}), new ScalingUpDown({sps: 1.0}), new AlphaUpDown({aps: 0.4, alphaMin: 0.4, alphaMax: 1.0})]
  };
  
  this.myCatAnim = new BitmapGraphic(this.catAnim);

  this.sprites = [this.myBackground, this.myCatAnim];

  return this;		
};


ScalingAlphaUpDownSetup.prototype.getSprites = function() {
  "use strict";
  return this.sprites;	
};

Define Behaviors

This setup script is very similar to the other switch behaviors' setup scripts. Let's look at the code that defines our sprite's behaviors. Here's that line of code:

behaviors:[new Cycling({cycleTime:60}), new ScalingUpDown({sps: 1.0}), new AlphaUpDown({aps: 0.4, alphaMin: 0.4, alphaMax: 1.0})]

We have three behaviors in our behaviors array: Cycling, ScalingUpDown, and AlphaUpDown. Cycling simply cycles through the sprite-sheet frames. ScalingUpDown and AlphaUpDown are our switch behaviors. They create the interaction that changes the sprite's size and opacity with each click.

Look at the object literal we pass to ScalingUpDown:

 new ScalingUpDown({sps: 1.0})

We send ScalingUpDown a name-value pair of sps and 1.0. sps stands for scale per second. Its number is a percentage—1.0 is 100 percent. An sps set to 1.0, scales the sprite by 100 percent each second.

Here's the object literal we send to AlphaUpDown:

 new AlphaUpDown({aps: 0.4, alphaMin: 0.4, alphaMax: 1.0})

Our object literal has three name-value pairs: aps: 0.4, alphaMin: 0.4, alphaMax: 1.0. aps stands for alphas per second. Like scale, the alpha value is a percentage—1.0 is a 100 percent. An aps of 0.4 changes the sprite's opacity by 40 percent every second. For the AlphaUpDown behavior we limit the range of opacity with alphaMin and alphaMax. We use those properties in the AlphaUpDown behavior to keep the opacity within a range of 40 percent to 100 percent.

Define SpriteProps

Before we leave the setup script, let's quickly look at the sprite's spriteProps object literal:

spriteProps:{name:'catAnim', x:410, y:340, w:400, h:220, XYscale: [0.2, 0.2], alpha: 0.4, graphics:['img/cat-sprite-sheet.png'], sheets:[this.catSheet], rotation: 0.2, visible:true}, 

Notice the XYscale and alpha properties. The behaviors change those two properties. We align our alpha property to the alphaMin property passed to AlphaUpDown—they're both 0.4 or 40 percent. We do that because both ScaleUpDown and AlphaUpDown are a little different than the other switch behaviors. The previous switch behaviors only start animating their properties after the mouse clicks on them—the behaviors kick in after that first mouse click.

ScaleUpDown and AlphaUpDown don't turn on and off an animated behavior; instead, they increase or decrease a property with each click. When they start up, their switchOn property defaults to false and they have logic in their execute methods that decreases the scale or opacity of the sprite when switchOn is false. So they don't wait for a first click. They just start scaling down or bringing the opacity down. If we set the opacity at the lower limit of the opacity range, the sprite looks like it's waiting for that first click. In reality, it's just at the lower limit of its property's range, waiting for a click to start increasing the value of that property.

Behavior's Execute Logic

Both ScaleUpDown and AlphaUpDown inherit from SwitcherBehavior. They're just like the other switch behaviors in that regard. Their execute methods are similar and also a little different than the other switch behaviors. Let's look at ScaleUpDown's execute method (you can find the script at js/behaviors/scale-up-down.js):

ScalingUpDown.prototype.execute = function(sprite, context, time) {
  "use strict";	
  //call clickable so it can update its mouseIsOver property with the sprite's current mouseIsOver property
  this.clickable.execute(sprite, context, time);
  		
  if (this.lastTime !== 0.0) {	//skip the first time until you have an accurate time count on this.lastTime
  	var timeDelta = time - this.lastTime;
	if (this.switchOn) {
	  sprite.XYscale[0] += timeDelta/1000 * this.sps;
	  sprite.XYscale[1] += timeDelta/1000 * this.sps;
	} else {
	  sprite.XYscale[0] -= timeDelta/1000 * this.sps;
	  sprite.XYscale[1] -= timeDelta/1000 * this.sps;
	  console.log("scale " + sprite.XYscale[0] + " " + sprite.XYscale[1]);
	}
  }	
  this.lastTime = time;		
};

Recall that sps is scale per second—the percentage amount we want to scale every second. We're using timeDelta (timethis.lastTime) to determine the number of milliseconds since execute was last called. We divide timeDelta by 1000 and multiply the result by sps to determine how much to scale for the current execute call. That calculation is very similar to the calculation we performed in MovingOnOff's execute. For MovingOnOff we calculate the velocity in pixels per second; here we calculate the scale as a percentage per second.

What's new in ScaleUpDown is the way we use this.switchOn in a conditional statement to either scale up or scale down the sprite. Let's focus on that conditional statement:

if (this.switchOn) {
	sprite.XYscale[0] += timeDelta/1000 * this.sps;
	sprite.XYscale[1] += timeDelta/1000 * this.sps;
} else {
	sprite.XYscale[0] -= timeDelta/1000 * this.sps;
	sprite.XYscale[1] -= timeDelta/1000 * this.sps;
}

If switchOn is true, we increase the sprite's scale. If switchOn is false, we decrease the scale. We do a similar thing in AlpahUpDown—if switchOn is true, we increase the opacity; if switchOn is false, we decrease the opacity.

Another Puzzle

Notice that our sprite seems to have some problems scaling. It looks like it flips up and down. Why does that happen? How could you prevent the flipping?

Remember that we set a lower and upper limit on the AlphaUpDown behavior. Take a look at that script's execute method and find the logic that limits the opacity range (see js/behaviors/alpha-up-down.js). You could do a similar thing for ScaleUpDown.

See if you can add some logic to ScaleUpDown so it no longer flips up and down but instead stays within a scale range.