/* This code emulates the interaction from here: http://www.nytimes.com/2007/01/24/washington/24bush.html?scp=1&sq=2007+state+of+the+union+address&st=nyt Shows frequencies for a few words from State of the Union addresses from 2001-2007, with animation making the circles representing frequencies grow and shrink. */ package { import flash.display.Sprite; import flash.events.*; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; [SWF(width="800", height="600", backgroundColor="#ffffff", frameRate="30")] public class ShowWordTrends extends Sprite { private var wordArrayIndex:Number = 0; private var objArray:Array = new Array(); private var wordArray:Array = new Array(); private var word:TextField; private var leftButton:Sprite; private var rightButton:Sprite; // Set up the frequencies for each work; values ordered from 2007-2001 private function initWordArray():void { wordArray.push({word: "Iraq/Iraqi(s)", values: [34, 16, 27, 24, 22, 2, 0] }); wordArray.push({word: "Tax(es)", values: [9, 9, 10, 19, 11, 7, 27]}); wordArray.push({word: "Economy(ic)", values: [8, 23, 14, 17, 13, 7, 6]}); wordArray.push({word: "Oil", values: [9, 3, 0, 0, 0, 1, 0]}); } // format for text within circles private function setTextFormat():TextFormat { var format:TextFormat = new TextFormat(); format.font = "Verdana"; format.color = 0x000000; format.size = 12; return format; } // format for text displaying the years on the lefthand side private function setTextFormat2():TextFormat { var format:TextFormat = new TextFormat(); format.font = "Verdana"; format.color = 0x7A8B8B; format.size = 11; return format; } // format for the text of the word whose frequency is shown private function setTextFormat3():TextFormat { var format:TextFormat = new TextFormat(); format.font = "Verdana"; format.color = 0x000000; format.size = 13; format.bold = true; return format; } // Right button moves up through the word list // The event listerner here activates the animation // Still need code to grey out inactive button private function rightButtonListener(e:Event):void { if (wordArrayIndex < wordArray.length-1) { wordArrayIndex+=1; stage.addEventListener(Event.ENTER_FRAME, growOrShrinkCircles); } } // Left button moves down through the word list // The event listener here activates the animation // Still need code to grey out inactive button private function leftButtonListener(e:Event):void { if (wordArrayIndex > 0 ) { wordArrayIndex -= 1; stage.addEventListener(Event.ENTER_FRAME, growOrShrinkCircles); } } // Create the graphics for left and right arrow buttons private function createArrowButton(left:Boolean, x:Number, y:Number):Sprite { var sprite:Sprite = new Sprite(); sprite.graphics.lineStyle(1, 0x6F7285); sprite.graphics.beginFill(0xffffff); sprite.graphics.drawRect(0, 0, 22, 18); sprite.graphics.endFill(); sprite.graphics.beginFill(0x000000); sprite.graphics.lineStyle(1, 0x000000); sprite.x = x; sprite.y = y; if (left) { sprite.graphics.moveTo(12,4); sprite.graphics.lineTo(12,14); sprite.graphics.lineTo(7,9); sprite.graphics.lineTo(12,4); sprite.graphics.endFill() } else { sprite.graphics.moveTo(8,4); sprite.graphics.lineTo(8,14); sprite.graphics.lineTo(14,9); sprite.graphics.lineTo(8,4); sprite.graphics.endFill() } return sprite; } // Set up the positions for all the objects in the display private function initializeViz():void { // determined experimentally to make a nice layout var currentX:Number = 30; var currentY:Number = 30; var separatorX:Number = 38; var separatorY:Number = 34; var size:Number = 1; initWordArray(); // create buttons for moving through the word lists // event listeners here are for recognizing mouse clicks leftButton = createArrowButton(true, 10, 10); leftButton.addEventListener(MouseEvent.CLICK, leftButtonListener); addChild(leftButton); rightButton = createArrowButton(false, 160, 10); rightButton.addEventListener(MouseEvent.CLICK, rightButtonListener); addChild(rightButton); // Create the text object to show and position the word word = new TextField(); word.text = "None"; word.setTextFormat(setTextFormat3()); // ugly code to figure out how to center the text between arrow buttons var len:Number = word.getCharBoundaries(word.length-1).x - word.getCharBoundaries(0).x; word.x = 90 - len/2; word.y = 10; addChild(word); // the year numbers are hardcoded here // create the circle objects and their centered label objects for (var year:Number = 2007; year>2000; year--) { currentY += separatorY; var yearLabel:TextField = new TextField(); yearLabel.text = String(year); yearLabel.x = currentX; yearLabel.y = currentY - 10.5; yearLabel.setTextFormat(setTextFormat2()); addChild(yearLabel); var circ:Sprite = createCircle(currentX + currentX + separatorX, currentY, computeCircleRadius(size)); var label:TextField = new TextField(); label.text = String(size); // single digit text centered differently than double-digit text label.x = circ.x + (size<10? -7 : -9); // also have to tweak the y parameter for text labels label.y = circ.y - 10.5; label.setTextFormat(setTextFormat()); label.autoSize = TextFieldAutoSize.LEFT; addChild(circ); addChild(label); // save these objects so we can modify them later objArray.push({circle: circ, textNumber: label}); } } private function createCircle(x:Number, y:Number, radius:Number):Sprite { var sprite:Sprite = new Sprite(); sprite.graphics.beginFill(0x9FB6CD, 0.5); sprite.graphics.lineStyle(); sprite.graphics.drawCircle(0, 0, radius); sprite.x = x; sprite.y = y; return sprite; } public function computeCircleRadius(size:Number):Number { // To make the circle area proportional to size, // take the sqrt(size/pi). To make it visible, // I experimentally determined 8.6 to be a good multiplier return (8.6*Math.sqrt(size/Math.PI)); } // This is where the animation happens. // Each circle has to get bigger or smaller // Each circle's label has to change // Also change the label for the word text private function growOrShrinkCircles(e:Event):void { word.text = wordArray[wordArrayIndex]["word"]; word.setTextFormat(setTextFormat3()); var len:Number = word.getCharBoundaries(word.length-1).x - word.getCharBoundaries(0).x; word.x = 90 - len/2; word.y = 10; // This controls how rapidly the increments in animation happen var incrementSize:Number = 2.0; // Loop through the circle objects, adjusting their size for (var i:Number = 0; i< objArray.length; i++) { var targetSize:Number = wordArray[wordArrayIndex]["values"][i]; var currentWidth:Number = objArray[i]["circle"].width; // width == diameter == 2*radius var targetWidth:Number = computeCircleRadius(targetSize)*2; // This stops the animation if (targetWidth == currentWidth) { removeEventListener(Event.ENTER_FRAME, growOrShrinkCircles); } // In the case below, circle should grow. else if (targetWidth > currentWidth) { objArray[i]["circle"].width += incrementSize; objArray[i]["circle"].height += incrementSize; if (objArray[i]["circle"].width > targetWidth) { objArray[i]["circle"].width = targetWidth; objArray[i]["circle"].height = targetWidth; } // in the case below, circle should shrink } else if(targetWidth < currentWidth) { objArray[i]["circle"].width -= incrementSize; objArray[i]["circle"].height -= incrementSize; if (targetWidth > objArray[i]["circle"].width) { objArray[i]["circle"].width = targetWidth; objArray[i]["circle"].height = targetWidth; } } // Adjust the text label objArray[i]["textNumber"].text = String(targetSize); objArray[i]["textNumber"].x = objArray[i]["circle"].x + (targetSize<10? -7 : -9); } } // The class function public function ShowWordTrends() { initializeViz(); addEventListener(Event.ENTER_FRAME, growOrShrinkCircles); } } }