Статьи

Создание облака тегов ООП в ActionScript 3.0

В этом уроке я покажу вам, как создать гибкое, анимируемое облако тегов, используя подход объектно-ориентированного программирования. Я не верю в правильный или неправильный метод, а скорее в несколько степеней эффективности. Если у вас есть конструктивная критика моего кода, не стесняйтесь комментировать.

Тем не менее, давайте начнем!




Этот шаг является наиболее важным, поскольку он будет диктовать все последующие шаги. Я начинаю с того, что смотрю на то, чего хочу достичь, а затем разбиваю его на кусочки.

Я хочу иметь возможность добавить несколько облаков тегов на странице. Я хочу, чтобы это было просто и настраиваемо . Так что мне нужно для создания этого облака тегов?

Мне нужен массив слов, цвет, шрифт, определения минимального и максимального размера, и мне нужны элементы облака тегов для хранения этой информации, эти элементы должны быть основаны на textField. Поскольку я хочу несколько облаков, очевидным выбором является создание экземпляра класса tagCloud, который в этом случае расширит Sprite.

Вот как должна выглядеть моя основная функция:

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

Как вы можете сказать, существует множество параметров, которые необходимо определить, следующее поможет вам в этом процессе. Создайте следующие файлы:

  • MainTagCloud.fla — этот файл будет создавать теги
  • TagCloud.as — это класс, который будет создавать tagcloud
  • TagCloudElement.as — это элемент, который будет заполнять tagcloud

Откройте TagCloud.as и напишите этот код

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
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();
        }
}

импортировать эти библиотеки:

1
2
3
4
import flash.text.Font;
import TagCloudElement;
import flash.display.Sprite;
import flash.events.Event;

определить эти переменные:

1
public var cloudElements:Array;

Вы получите что-то вроде этого:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package
{
    //First import these packages:
    import flash.text.Font;
    import TagCloudElement;
    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();
        }
    }
}

Вот основная функция, которая будет строить наше облако.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)
           }
       }

Посмотрим, с какими словами мы имеем дело.

1
2
3
4
5
6
7
8
9
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);
       }

Я устанавливаю размер элемента, используя формулу, найденную в Википедии:

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

Это вызывает фильтр для массива.

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

Теперь установите правила фильтра.

01
02
03
04
05
06
07
08
09
10
11
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;
            }
        }

Нам нужно будет проверить перекрывающиеся позиции.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
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)
        }
    }
     
}

Это просто получатель элемента по имени, на случай, если он мне нужен на основной временной шкале.

1
2
3
4
5
6
7
8
9
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
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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»);
        }
    }
}

Теперь все, что осталось сделать, — это реализовать этот класс в реальном файле .fla со всеми вещами, к которым вы привыкли (т.е. с временной шкалой): P

Вам нужно будет создать шрифт, чтобы вы могли отображать textFields, я встроил шрифт Arial.

Затем в первом кадре вашего .fla импортируйте класс TagCloud, установите stage.align в верхнем левом углу (чтобы мы могли найти среднее положение рабочей области без особых усилий) и создайте новый экземпляр шрифта, который мы только что добавили в библиотека:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
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,0×000000);
    // 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();

Теперь нам нужно взять канал откуда-то, чтобы мы могли его скрыть. Я выбрал канал новостей CNN. Чтобы иметь возможность загружать XML, вам нужно 4 объекта, включая urlRequest, который будет использоваться в качестве пути к каналу.

1
2
3
4
5
6
7
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;

Теперь внутри нашей основной функции мне нужно добавить полный обработчик событий в запрос, чтобы он мог быть вызван при успешной загрузке.

1
2
3
4
5
6
7
8
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);
     
}

Структура данных хранится внутри e.target.data, поэтому мы создаем XML здесь, выполнив:

1
2
3
4
5
6
7
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++){

Создавайте titleWords в каждой итерации, чтобы у вас был чистый массив каждый раз, когда у нас появляется новый заголовок.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
         
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();

Теперь у нас есть все элементы, необходимые для создания этого облака тегов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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();

Вот полный код, который вы можете прочитать полностью.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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();

Я мог бы использовать связанные списки и циклы while, чтобы сделать это немного быстрее, но вы найдете это достаточно быстро. Последнее замечание: не забудьте установить достаточно большой случайный размер, иначе вы получите ошибку stackOverFlow, когда cloudElement не может найти место для размещения.

Надеюсь, вам понравился этот урок, спасибо за чтение!