Build an OOP Tag Cloud in ActionScript 3.0

In this tutorial I’ll show you how to build a flexible, animatable tagcloud using an Object Oriented Programming approach. I don’t believe in a right or wrong method, but rather several degrees of efficiency. If you have any constructive criticism on my code feel free to comment.

That said, lets start!

Step 1: How to Think Cloud

This step is the most important one as it will dictate all the following steps. I start by looking at what I want to achieve and then break it into pieces, here’s my line of thought:

I want to be able to add multiple tag clouds in a page. I want it to be simple and customizable. So what do I need to build this tag cloud?

I need a word array, a color, a font, minimum and maximum size definitions, oh and I need tag cloud elements to store that information, these elements should be textField based. Since I want several clouds the obvious choice is to create an instanceable tagCloud class that in this case will extend a Sprite.

Here’s what my main function should look like:

var tagCloud:TagCloud = new TagCloud(words,font,color,minFontsize,maxFontsize,fullsize)

As you can tell there are plenty of parameters which need to be defined, the following will walk you trough the process. Create the following files:

  • MainTagCloud.fla – this file will instantiate the tagcloud
  • TagCloud.as – this is the class that will create the tagcloud
  • TagCloudElement.as – this is the element that will populate the tagcloud

Step 2: Building the Mother Class

Open TagCloud.as and write this code

package 
{

	public class TagCloud extends Sprite 
	{
		
		public function TagCloud($word_array:Array,$font="Arial",$minFontSize:Number=10,$maxFontSize:Number=30,$elementColor:Number=0xffffff,$fullsize:Number=200):void 
		{
			//here I assign the variables I receive to the class's variables
			wordArray = $word_array;
			font = $font
			minFontSize = $minFontSize
			maxFontSize = $maxFontSize
			elementColor = $elementColor
            fullsize = $fullsize
			//after setting the variables I build the cloud
			buildTagCloud();
		}
}

import these libraries:

	import flash.text.Font;
	import TagCloudElement; // I'll get to this one later on
	import flash.display.Sprite;
	import flash.events.Event;

define these variables:

		public var cloudElements:Array;
		private var wordArray:Array;
		private var word:String;
		private var relevancy:Number;
		private var size:int;
		private var element:TagCloudElement;
		private var minFontSize:Number;
		private var maxFontSize:Number;
		private var elementColor:Number;
		private var font:String;
		private var wordLength:int
        private var fullsize:Number

You’ll end up with something like this:

package 
{
	//First import these packages:
	import flash.text.Font;
	import TagCloudElement; // I'll get to this one later on
	import flash.display.Sprite;
	import flash.events.Event;

	//Create a class that will extend a sprite
	public class TagCloud extends Sprite 
	{
		//we need these variables to be abble to create the tagCloud
		public var cloudElements:Array;
		private var wordArray:Array;
		private var word:String;
		private var relevancy:Number;
		private var size:int;
		private var element:TagCloudElement;
		private var minFontSize:Number;
		private var maxFontSize:Number;
		private var elementColor:Number;
		private var font:String;
		private var wordLength:int
        private var fullsize:Number
		
		public function TagCloud($word_array:Array,$font="Arial",$minFontSize:Number=10,$maxFontSize:Number=30,$elementColor:Number=0xffffff,$fullsize:Number=200):void 
		{
			//here I assign the variables I receive to the class's variables
			wordArray = $word_array;
			font = $font
			minFontSize = $minFontSize
			maxFontSize = $maxFontSize
			elementColor = $elementColor
            fullsize = $fullsize
			//after setting the variables i build the cloud
			buildTagCloud();
		}
	}
}

Step 3: Construct Your Main Function

Here’s the main function that will build our cloud.

private function buildTagCloud() {
			//create an element array
			cloudElements = new Array();
			//gets the words lenght so i can iterate trought them and create the elements
			wordLength = getSingleWordList(wordArray).length
			for (var i=0; i<wordLength; i++) {
				//this function returns me an array, its basically a filter, read more about it later on
				word = getSingleWordList(wordArray)[i]
				//this function uses the wikipedia formula to calculate the element size
				size = setElementSize(word, wordArray, minFontSize, maxFontSize);
				//creates a new element
				element = new TagCloudElement(word, size, font, elementColor);
				//stores the new element in the array
				cloudElements[i] = element
				//sets the transparency based on the size
				cloudElements[i].alpha=size/maxFontSize
				//just a random way to display the cloud elements
				cloudElements[i].x = Math.random() * fullsize
				cloudElements[i].y = Math.random() * fullsize
				addChild(cloudElements[i]);
				//performs a hit test trought the created objects
				cloudHitTest(i)
			}
		}

Step 4: Adding a Word Counter

Let’s see how many words we’re dealing with.

private function countWord($word:String,$array:Array):int {
		var count:int=0;
			for (var i:int=0; i<$array.length; i++) {
				if ($array[i].toLowerCase()==$word.toLowerCase()) {
					count+=1;
				}
			}
			return (count);
		}

Step 5: Set the Element Size

I set the element size by using a formula found on wikipedia:

function setElementSize($word:String, $array:Array, $minSize:Number, $maxSize:Number):Number {
			var $size:Number = $maxSize * countWord($word, $array) / $array.length
			$size *= $minSize
			return $size
		}

Step 6: Creating a Single Word List

This calls a filter for the array.

private function getSingleWordList($source:Array):Array {
			var $array:Array=$source.filter(singleWordFilter);
			return $array;
		}

Now set the filter rules.

private function singleWordFilter(element:*, index:int, arr:Array):Boolean {
			if(arr[index+1]){
				if (arr[index].toLowerCase()!=arr[index+1].toLowerCase()) {
					return true;
				} else {
					return false;
				}
			}else {
				return false;
			}
		}

Step 7: How to HitTest

We’re going to need to test for overlapping positions.

		private function cloudHitTest($i) {
			for (var a:int=0; a < $i; a++) {
				//if HITS
				if (cloudElements[a].hitTestObject(cloudElements[$i])) {
					//Reposition
					cloudElements[$i].x = Math.random() * fullsize
					cloudElements[$i].y = Math.random() * fullsize
					addChild(cloudElements[$i]);
					//and test again
					cloudHitTest($i)
				}
			}
			
		}

Step 8: Setting up an Element Getter

This is just a getter of an element by name, in case I need one over the main timeline.

		public function getElementByName($name:String):TagCloudElement {
			var $auxCloudElement:TagCloudElement;
			for (var i:int=0; i < wordLength; i++) {
				if (cloudElements[i].word == $name) {
					$auxCloudElement =  cloudElements[i]
				}
			}
			return $auxCloudElement
		}

Step 9: Inside the Element Class

package 
{
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.Font;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFieldAutoSize;
	import flash.text.AntiAliasType;
   	import flash.text.GridFitType;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;


	public class TagCloudElement extends Sprite 
	{
		public var word:String;
		public var urlpath:String;
		
		private var textCloudFormat:TextFormat;
		private var textCloud:TextField;
		
		public var font:String;
		public var size:Number;
		public var color:Number;
		
		// Same constructor as TagCloud, the element extends a Sprite
		// and Builds the Element based on a TextField
		public function TagCloudElement($word:String, $size:Number = 10, $font:String = "Arial", $elementColor:Number = 0xffffff):void 
		{
			word = $word
			font = $font
			size = $size
			color = $elementColor
			
			buildElement();
		}
		
		private function buildElement() {
			//creates the textformat
			textCloudFormat = new TextFormat();
			// defines the font size and color
			textCloudFormat.font = font
			textCloudFormat.size = size
			textCloudFormat.color = color
			// creates a textField
			textCloud = new TextField();
			// embeds the font
			textCloud.embedFonts=true;
			//sets the antialias to readable equivalent
            textCloud.antiAliasType=AntiAliasType.ADVANCED;
			//defines its text
            textCloud.text=word
			//defines its size as automatic
            textCloud.autoSize=TextFieldAutoSize.LEFT;
			//fit to pixel
			textCloud.gridFitType = GridFitType.PIXEL
			// unselectable text
			textCloud.selectable = false;
			// assigns the textformat to the textfield
			textCloud.setTextFormat(textCloudFormat)
			// adds the MouseEvents listeners
			textCloud.addEventListener(MouseEvent.ROLL_OVER,rollOverCloudElement)
			textCloud.addEventListener(MouseEvent.ROLL_OUT,rollOutCloudElement)
			textCloud.addEventListener(MouseEvent.CLICK,clickCloudElement)
			addChild(textCloud);
		}
		
		private function rollOverCloudElement(e:MouseEvent){
			e.target.textColor = 0x666666;
		}
		private function rollOutCloudElement(e:MouseEvent){
			e.target.textColor = color
		}
		// I've made a link to a twitter search using the word selected.
		private function clickCloudElement(e:MouseEvent){
			navigateToURL(new URLRequest("http://search.twitter.com/search?q="+e.target.text),"_blank");
		}
	}
}

Step 10: Implementation

Now, all that's left to be done is to implement this class in a real .fla file with all the stuff that you are accustumed to (ie:timeline) :P

You'll need to create a font so you can display the textFields, I embeded an Arial font.

Then in the first frame of your .fla import the TagCloud class, set a stage.align to the top left (so that we can find the stage middle position without much work) and create a new instance of the font we just added to the library:

import TagCloud;

stage.align = StageAlign.TOP_LEFT
var wordArray:Array;
var tagCloud:TagCloud;
var arial:Arial = new Arial();//sets a new instance of Arial (already in the library)

function init() {
	//creates an array to populate the cloud
	wordArray = new Array("In","this","fashion,","text","clouds","may","become","a","generally","applied","tool","for","managing","growing","information","overload","by","using","automated","synthesis","and","summarization","In","the","information","saturated","future","or","the","information","saturated","present");
	//sorts the array alphabetically so i can filter later on
	wordArray.sort();
	// creates a new tagCloud instance
	tagCloud = new TagCloud(wordArray,arial.fontName,15,20,0x000000);
	// center's it to stage
	tagCloud.x = stage.stageWidth*0.5-tagCloud.width*0.5
	tagCloud.y = stage.stageHeight*0.5-tagCloud.height*0.5
	//adds to stage
	addChild(tagCloud);
}

init();

Step 11: Build an RSS Feed Request

Now we need to grab a feed from somewhere so we can cloud it. I chose the CNN news feed. To be able to load an XML you need 4 objects including a urlRequest that will be used as a path to the feed.

var requestFeed:URLRequest = new URLRequest("http://rss.cnn.com/rss/cnn_world.rss");
//an urlLoader so that we can load the request we need to make
var loaderFeed:URLLoader = new URLLoader()
// a XML object so we can store the data we recieve from the feed
var xmlFeed:XML;
//and last but not least a title array that i can explode the words from...
var titleWords:Array;

Step 12: The Initialization Method

Now inside our main function I need to add the complete event handler to the request so that it can be called upon a successful load.

function init() {
	loaderFeed.addEventListener(Event.COMPLETE,onFeedComplete)
	//I will need the wordArray to be instantiated so I can store the words inside the feed
	wordArray = new Array()
	//we are ready to load the XML now
	loaderFeed.load(requestFeed);
	
}

Step 13: The Data Structure

The data structure is stored inside the e.target.data so we create the XML here by doing:

function onFeedComplete(e:Event){
	xmlFeed = new XML(e.target.data)
	//after viewing the source of the rss feed I noticed the structure was something like channel.item.title so i'm using the titles as my word source.
	//I need to make an array to store all the words of a title and then add each on of those words inside the word array
	//for this I cycle through them

	for(var i:uint=0;i<xmlFeed.channel.item.length();i++){

Step 14: Building the Word List

Instanciate the titleWords in every iteration so that you have a clean array everytime we have a new title.

		
titleWords = new Array()
		//to make single words I split them on "space"
		titleWords = xmlFeed.channel.item[i].title.split(" ")
		//after them being split i iterate them to be added to the wordArray
		for(var j:uint=0;j<titleWords.length;j++){
			//i use lowercase so i don't have any duplicated words
			wordArray.push(titleWords[j].toLowerCase());
		}
		
	}
    //after that being done I sort the word array alphabetically
	wordArray.sort();
	//and I start the tagCloud
	startTagCloud();    

Step 15: Starting the Tag Cloud

Now we have all the elements we need to make this tag cloud.

try{
		tagCloud = new TagCloud(wordArray,arial.fontName,20,40,0xFFFFCD,300);
	}catch(e:Error){
		startTagCloud()
	}
	//all that is left is to define an X and a Y
	tagCloud.x = stage.stageWidth*0.5-tagCloud.width*0.5
	tagCloud.y = stage.stageHeight*0.5-tagCloud.height*0.5
	//and adding it to the stage
	addChild(tagCloud);
	//tadaaa we are done.. 
}

//don't forget to initialize the main function :)
init();

Step 16: The Final Code

Here's the complete code for you to read fully.

import TagCloud;
stage.align = StageAlign.TOP_LEFT

var wordArray:Array;
var tagCloud:TagCloud;
var arial:Arial = new Arial();

var requestFeed:URLRequest = new URLRequest("http://rss.cnn.com/rss/cnn_world.rss");
var loaderFeed:URLLoader = new URLLoader()
var xmlFeed:XML;
var titleWords:Array;

function init() {
	loaderFeed.addEventListener(Event.COMPLETE,onFeedComplete)
	wordArray = new Array()
	loaderFeed.load(requestFeed);
}

function onFeedComplete(e:Event){

  xmlFeed = new XML(e.target.data)
  
  	for(var i:uint=0;i<xmlFeed.channel.item.length();i++){
  		titleWords = new Array()
  		titleWords = xmlFeed.channel.item[i].title.split(" ")
  		for(var j:uint=0;j<titleWords.length;j++){
  			wordArray.push(titleWords[j].toLowerCase());
		}
	}
  wordArray.sort();
  startTagCloud();
}

function startTagCloud(){
  try{
 	  tagCloud = new TagCloud(wordArray,arial.fontName,20,40,0xFFFFCD,300);
   }catch(e:Error){
  	  startTagCloud()
	}
  tagCloud.x = stage.stageWidth*0.5-tagCloud.width*0.5
  tagCloud.y = stage.stageHeight*0.5-tagCloud.height*0.5
	
  addChild(tagCloud);
}

init();

Conclusion

I could have used linked lists and while loops to make this a bit faster, but you'll find it reasonably quick. One final note: be sure to set the random size big enough or you'll get a stackOverFlow error when the cloudElement can't find a place to be put.

I hope you liked this tutorial, thanks for reading!

  • David Nash

    Nice

  • http://labs.dariux.com Dario Gutierrez

    Bruno definitely this is an excellent tut. Nice code.

  • http://skillz-commmunity.net Shurandy Thode

    Great tut.

  • http://www.xitestudiomagazine.com Roberto XSM

    Very good. Thanks

  • http://www.w2point.com Web 2.0

    Great work :)

  • André

    very nice, very well coded man, thanks a lot for this tutorial

  • Jirka

    hi, i have problem with open .fla file. I have CS3. Can you save it for my version and send it? Thank you very much

    • Bruno Crociquia
      Author

      Here you go,

      it will only exist for the next 24h or 20downloads…

      http://tiny.cc/upJyp

    • http://snaptin.com Ian Yates
      Staff

      Thanks Bruno,

      I grabbed a copy and included it in the existing source.zip. CS3 is therefore now available from the source link at the top of the page.

      • Bruno Crociquia
        Author

        Thanks Ian

  • Pingback: Down the Foxhole – Last Week on FlashTuts+

  • Pingback: Adobe Flash: 35+ Animated Trainings | oOrch Blog

  • http://www.flashden.net/user/wangruyi wang ruyi

    nice tutorial!

  • http://www.pixelmaestro.com Pixel Maestro

    There is a WordPress TagCloud worth looking at http://www.roytanck.com/tag/wp-cumulus/

    Flash source files are in the Development version
    http://wordpress.org/extend/plugins/wp-cumulus/download/

  • http://www.flashframer.com Flash Framer

    OOP always fries my brain but you did a great job. Thanks!

  • Pingback: Twitted by seacloud9

  • Pingback: 45+ Advanced Adobe Flash Actionscript Trainings | Master Design

  • Maria Luiza

    Maravilhoso, muito bom

  • Pingback: Advanced Adobe Flash Actionscript Trainings « Flash Criminals

  • user

    hey,

    first of all i appreciate your tutorial.
    but i have the problem that the source files only work for
    your tag cloud example with predefined “words” in your tagCloud.

    if i edit the main with the new code, or generate it completly new
    with your tutoial i get an error “only expected 5 arguments” referring
    to your constructor for your tagCloud, but which you want to initalize
    witz 6 arguments:
    [...]
    tagCloud = new TagCloud(wordArray,arial.fontName,20,40,0xFFFFCD,300);
    [...]

    even if i fix this (reducing it to the 5 arguments it won’t load or work
    with the xml file from cnn.rss or any other.

    i would appreciate your help, or instead see the complete source file with
    the coude from this tutorial and not with the example without the url loader.

    thanks in advance.
    greetz.

    • Bruno Crociquia
      Author

      Sorry, for only reading your question now. Use mainTagCloud_cs3.fla version inside, as it has the xml loader instead of the other fla that has a static name array.

      remember, you can’t test the swf in your local machine, because we are accessing http we can only work within a network sandbox. meaning if you want to run inside your computer you need to either host a server in your machine, or edit your flash player security settings.

      Cheers

  • kedicik

    i found bag! Because i don’see last word in array

  • http://complexcortex.com Aaron

    awesome tutorial! I like that you go from scratch in your code, really helps to walk through the process. Thanks!

  • http://sara444@wordpress.com sara

    hai i am clearning flash current send me some samples of AS2 in flash.That will help me for the future

  • nic

    Hi, I have problem with the.fla file.
    I’m currently using CS3. Can you save it in CS3 format and send it again?
    Thanks. It’s a good tutorial.

  • Pingback: Free I Share 分享资源 分享快乐 » Blog Archive » 45个高级Flash Actionscript应用

  • olivier

    Great tutorial! It save my nerve !

  • Alex

    Super tutorial. So easy and clear. Saved me plenty of time.Thank u

  • http://www.aidanapps.co.uk Aidan Mack

    Good tut, i like the hitTest method. But it suffers from an issue I have had before. What if it caunt find a place to add an element and you get a “Stack overflow occurred.”

    What I do is count the number of times the for loop occurs. If its tried to add it say… 50 times and fails I “break” the for statement and just let it place the element where ever.

  • http://www.asrd.info sergio

    Thanks very much, i have been squeezing my brain for two days and many loops/hittests and didnt get a proper way to do it randomly. Your app helped me a lot

  • http://www.yellowshark.com David C.

    Hi!

    First of all, this is a very nice tutorial. Thank you!

    Now to my problem:
    How can I add a hand cursor on mouseover the titles? My words are from an XML-File.

    I think I would have to dynamically create MovieClips with “useHandCursor” and “buttonMode” and assign (and placing them above) them to the Arrays which are holding the words?

    Any help would be highly appreciated!

    Thanks!

  • JohnS

    Nice, very comprehensive tutorial. However, I have a question, how difficult would be to make it to rotate, like the WP cumulus plugin or like this Flash tag cloud: http://www.flashxml.net/tag-cloud.html ?

  • Pingback: 45+ Advanced Tutorials of Adobe Flash ActionScript « CSS Tips

  • Pingback: 45+ Advanced Tutorials of Adobe Flash ActionScript | JS Tips