Browse the notebook

Category: UI

Previous and Next Buttons

var prevBtn:Sprite = makeArrow(100, 100);
prevBtn.rotation = 180;
var nextBtn:Sprite = makeArrow(130, 100);

var index:int = 0;
var vals:Array = [1,2,3,4,5,6,7,8,9,10,11];
var leng:int = vals.length;

trace(vals[index]);

prevBtn.addEventListener(MouseEvent.CLICK, onPrev);
function onPrev(evt:MouseEvent):void {
	index--;
	if (index < 0){
		index = leng - 1;
	}
	trace(vals[index%leng]);
}
nextBtn.addEventListener(MouseEvent.CLICK, onNext);
function onNext(evt:MouseEvent):void {
	index++;
	trace(vals[index%leng]);
}

function makeArrow(xp:Number, yp:Number):Sprite {
	var s:Sprite = Sprite(addChild(new Sprite()));
	s.buttonMode = true;
	with(s.graphics) beginFill(0x666666), moveTo(0,-10), lineTo(20, 0), lineTo(0,10);
	s.x = xp;
	s.y = yp;
	return s;
}

Previous and next buttons….

Read snippet: Previous and Next Buttons

Infinity Scroller (repeating navigation)

var buttonsWidth:Number = 0;
var buttonsA:Sprite = new Sprite();
var buttonsB:Sprite = new Sprite();
var nav:Sprite = new Sprite();

var buttonData:Array = ["one", "two", "three", "four", "five", "six", 
                                    "seven", "eight", "nine", "ten"];

buildNav();

function buildNav():void{
	nav.addChild(buttonsA);
        nav.addChild(buttonsB);
        addChild(nav);
	buildButtons(buttonsA);
	buttonsWidth = buttonsA.width;
	buildButtons(buttonsB);
	buttonsB.x = buttonsWidth;
}

function buildButtons(container:Sprite):void{
	for (var i:int = 0; i rightSide){
			velX = -navSpeed;
		}
		if (nav.x < -buttonsWidth){
			nav.x = -navSpeed;
		}
		if (nav.x > -navSpeed){
			nav.x = -buttonsWidth 
		}
	}
	velX *=.9;
	nav.x += velX;
}

This snippet creates a navigation that will scroll to the left or right forever – the buttons will simply repeat. I’m not a big fan of this kind of navigation – especially if you have lots of buttons, but it seems to be a common request. This technique can be modified for use in a side-scroller style game.

Just added a new syntax highlighter, please posts comments if you have any issues viewing this snippet.

Read snippet: Infinity Scroller (repeating navigation)

Relative Positioning

var size:Array = [1, 1.5, .5, 1, .4, 1, 1, 1, .2, 1.1]
var boxes:Array = new Array();
var spacing:Number = 4;
var container:Sprite = Sprite(addChild(new Sprite()));
container.x = container.y = 100;

for (var i:int = 0; i<size.length; i++){
	var box:Sprite = makeBox();
	var prev:int = i - 1;
	box.scaleX= box.scaleY = size[i];
	if (i == 0){
		box.y = 10;
	}else{
		// here's the trick
		// if you animate the height property you need to do this again and again:
		box.y = boxes[prev].y + boxes[prev].height/2+ box.height/2 + spacing
	}
	boxes.push(box);
}

function makeBox():Sprite{
	var box:Sprite = Sprite(container.addChild(new Sprite()));
	with (box.graphics) beginFill(0xFF0000), drawRect(-50,-10, 100, 20);
	return box;
}

Sometimes you need to position a bunch of Sprites or MovieClips that are different sizes – and you want to keep the spacing between them the same. This snippet shows a simple example of this.

For more info you could also do this tutorial that I wrote on learningactionscript3.com

Read snippet: Relative Positioning

Slider Navigation

var thumbNum:Number = 20;
var spacing:Number = 10;
var thumbs:MovieClip = new MovieClip();
addChild(thumbs);
for (var i:int = 0; i<thumbNum; i++){
	var t:MovieClip = new MovieClip();
	with(t.graphics) beginFill(0x666666), drawRect(0,0,100,50);
	t.x = i * (t.width + spacing);
	t.y = 5;
    t.buttonMode = true;
	thumbs.addChild(t);
}
var minX:Number = stage.stageWidth - thumbs.width - spacing;
var destX:Number = thumbs.x = spacing;
var velX:Number = 10;
var stageThird:Number =  stage.stageWidth / 3;
var right:Number = stageThird * 2;
var left:Number  = stageThird;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	if (mouseX > right){
	   destX -= velX;
	}
	if (mouseX < left){
		destX += velX;
	}
	if (destX < minX){
		destX = minX;
	}
	if (destX > spacing){
		destX = spacing;
	}
	thumbs.x += (destX - thumbs.x) /4;
}

This snippet shows a technique for a common type of navigation.

Read snippet: Slider Navigation

Snap to Grid

[SWF(width = 600, height = 400)]

// draw the same grid as yesterday
var tileSize:int = 40;
var cols:int = stage.stageWidth / tileSize;
var rows:int = stage.stageHeight / tileSize;
var grid:Sprite = Sprite(addChild(new Sprite()));
grid.graphics.lineStyle(0,0x000000);
var i:int = 0;
for (i = 1; i<cols; i++){
	var posX:Number = i * tileSize
	grid.graphics.moveTo(posX, 0);
	grid.graphics.lineTo(posX, stage.stageHeight);
}
for (i = 1; i<rows; i++){
	var posY:Number = i * tileSize
	grid.graphics.moveTo(0, posY);
	grid.graphics.lineTo(stage.stageWidth, posY);
}

//
// -- add a circle that snaps to the grid when dragged
//
var circle:Sprite = Sprite(addChild(new Sprite()));
with (circle.graphics) beginFill(0xFF0000), drawCircle(0,0,10);
circle.x = circle.y =  tileSize * 3;
circle.buttonMode = true;

circle.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
function onDown(evt:MouseEvent):void {
	addEventListener(Event.ENTER_FRAME, onRunSnapping);
}

function onRunSnapping(evt:Event):void {
	circle.x =  Math.round(mouseX / tileSize) * tileSize;
	circle.y =  Math.round(mouseY / tileSize) * tileSize;
}

stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
function onUp(evt:MouseEvent):void {
	removeEventListener(Event.ENTER_FRAME, onRunSnapping);
}

This builds on yesterdays post by adding a draggable red circle that snaps to the grid. This is the real trick:

circle.x =  Math.round(mouseX / tileSize) * tileSize;
circle.y =  Math.round(mouseY / tileSize) * tileSize;
Read snippet: Snap to Grid

Dial UI, Record Scratch etc…

var angOffset:Number = 0;
var percent:Number  = 0;

var dial:Sprite = Sprite(addChild(new Sprite()));
with(dial.graphics) lineStyle(1, 0x000000), beginFill(0xCCCCCC), drawCircle(0,0,100), lineTo(0,0);
dial.x = stage.stageWidth / 2;
dial.y = stage.stageHeight / 2;

dial.addEventListener(MouseEvent.MOUSE_DOWN, onDialDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onStageUp);
function onDialDown(evt:MouseEvent):void {
	calcOffset();
	dial.addEventListener(Event.ENTER_FRAME, onRotateDial);
}

function calcOffset():void {
	angOffset = Math.atan2(mouseY - dial.y, mouseX - dial.x) / Math.PI * 180 - dial.rotation;
}

function onRotateDial(evt:Event):void {
	dial.rotation =  Math.atan2(mouseY - dial.y, mouseX - dial.x) / Math.PI * 180 - angOffset;
	percent = dial.rotation;
	if (percent < 0){
		percent += 360;
	}
	percent /= 360;
	// range 0 - 1
	trace(percent);
}

function onStageUp(evt:Event):void {
	dial.removeEventListener(Event.ENTER_FRAME, onRotateDial);
}

The above shows the meat of what you need to create some kind of dial UI… this would also work well if you need to create an interactive turntable (record scratching etc…).

Everytime I needed to create dials for UI, the client wanted left -> right mouse motion rather than a circular… like what you see in Reason. Someone recently asked me how to do a circular motion style dial and this is the technique I came up with.

Read snippet: Dial UI, Record Scratch etc…

Procedural Pattern – Navigation

var currentBtn:MovieClip;

var nav:Sprite = new Sprite();
nav.x = nav.y = 20;
addChild(nav);

createBtns();

nav.addEventListener(MouseEvent.CLICK, onClickBtn);

function createBtns():void{
	for (var i:int = 0; i<10; i++){
		var btn:MovieClip = new MovieClip();
		with(btn.graphics) beginFill(0x666666), drawRect(-10,-10,20,20);
		btn.x = i * (btn.width + 10);
		btn.buttonMode = true;
		btn.num = i;
		btn.alpha = .5;
		nav.addChild(btn);
	}
}

function onClickBtn(evt:MouseEvent):void {
	//
	// this is the important part
	//
	if (currentBtn){
		currentBtn.scaleX = currentBtn.scaleY = 1;
		currentBtn.alpha = .5;
	}
	currentBtn = MovieClip(evt.target);
	currentBtn.scaleX = currentBtn.scaleY = 1.3;
	currentBtn.alpha = 1;
	trace("current button:", currentBtn.num);
	//
}

This code will create 10 boxes that represent buttons on a navigation. When you click a box it indicates that it is selected by scaling up and changing alpha. Simple enough…

The term Procedural Pattern is just a spin on the idea of Design Patterns. I’ve come up with lots of small patterns to solve simple recurring problems over the years. Most of these relate to things like drawing programs, ecards and games.

Read snippet: Procedural Pattern – Navigation