News Ticker a scorrimento orizzontale con Flash e XML

Creare una lista di news in stile JavaScript

Inserito il 21-01-2007

Realizzare un NewsTicker è relativamente semplice, grazie all'utilizzo delle classi dedicate, XML per il parsing (link esterno) del file .xml esterno, e Tween (easing functions) per la gestione dello scorrimento del testo. In questo articolo affronteremo parte per parte le fasi della realizzazione, in modo da rendere il discorso fruibile anche solo in parte per realizzare più varianti allo stesso lavoro.


- Download dell'esempio di applicazione

Parsing del file XML

Baseremo il nostro articolo sul seguente file xml, un file con una struttura piuttosto semplice, appositamente pensato per lo scopo finale del nostro newsticker:

<?xml version="1.0" encoding="iso-8859-1"?>
<news>
	<item>
		<date>17/01/2007</date>
		<title>Titolo prima news</title>
		<url>http://www.somesite.com/news.php?id=1</url>
	</item>
	<item>
		<date>18/01/2007</date>
		<title>Titolo seconda news</title>
		<url>http://www.somesite.com/news.php?id=2</url>
	</item>
	<item>
		<date>19/01/2007</date>
		<title>Titolo terza news</title>
		<url>http://www.somesite.com/news.php?id=3</url>
	</item>
</news>

Come si può notare dalla struttura, lo scopo del file news.xml è quello di associare un determinato url di dettaglio ad un titolo ed una data, in modo che ogni data+titolo, una volta cliccato, ci porti ad una nuova pagina con l'intera news in dettaglio, passiamo perciò al parsing:

// blocco la timeline principale fino a che il parsing non sarà andato a buon fine
stop();
// creo una stringa a cui assegno il nome del file esterno da caricare
var xmlFile:String = new String("news.xml");
// inizializzo un array, che utilizzerò come oggetto contenitore dei dati provenienti da XML
var parsed:Array = new Array();
// inizializzo un'istanza della classe XML per la lettura dei dati esterni
var parser:XML = new XML();
// attivo la proprietà ignoreWhite dell'istanza di XML, in modo di evitare che gli spazi vuoti del documento esterno
// interferiscano con il parsing dei nodi XML
parser.ignoreWhite = true;
// richiamo l'evento onLoad della classe XML che sancisce l'esatto momento in cui Flash ha ricevuto i dati dall'esterno -> if(this.loaded)
// oppure non è stato in grado di comunicare con il file XML, restituendo l'errore conseguente -> "Impossibile aprire news.xml"
parser.onLoad = function(ok) {
	if (this.loaded) {
		// creo una variabile temporanea a cui associo tutti i nodi <item> sotto forma di elementi di un array
		var temp = this.firstChild.childNodes;
		// su questa variabile temporanea faccio un ciclo
		for (var i = 0; i<temp.length; i++) {
			// ed inserisco ad ogni iterazione un object all'interno dell'array
			// l'object sarà costituito dagli elementi presenti nel file XML
			// "data" che corrisponde al primo elemento di ogni nodo item (gli array hanno base 0, quindi il primo nodo sarà [0])
			// "title" corrisponde al secondo elemento, quindi [1]
			// "url" corrisponde al terzo elemento, quindi [2]
			parsed.push({date:temp[i].childNodes[0].firstChild.toString(), title:temp[i].childNodes[1].firstChild.toString(), url:temp[i].childNodes[2].firstChild.toString()});
			// una volta terminato il ciclo e quindi riempito il nostro array, passiamo al frame 2 per utilizzare i dati appena raccolti
			if (i>=temp.length-1) gotoAndStop(2);
		}		
	} else {
		trace("Impossibile aprire "+xmlFile);
	}
};
// dopo aver definito l'evento onLoad, attivo il caricamento del file esterno, con il metodo load della classe XML
parser.load(xmlFile);

Riempimento del campo di testo

Arrivati a questo punto, abbiamo a disposizione i nostri dati, che utilizzeremo nel frame 2, dove ci saremo occupati di posizionare a nostro piacimento, un MovieClip composto da due layer, uno sarà un rettangolo della dimensione che preferiamo (io l'ho fatto 200x20), che utilizzeremo come maschera del campo di testo e determinerà quindi la visibilità del nostro newsticker, l'altro sarà appunto il nostro campo di testo (con font incorporato) che utilizzeremo come contenitore del testo esterno formattato in html, perciò ricapitolando avremo:
- newsticker
-- mask (maschera del layer sottostante)
-- testo (textfield dinamico con caratteri incorporati (link esterno).)

Utilizziamo il nostro array con il seguente codice, per riempire il campo di testo con le informazioni

// ancora una volta stoppiamo la timeline
stop();
// utilizziamo la proprietà autoSize della classe TextField, per fare in modo che il campo si adatti alla dimensione del contenuto
newsticker.testo.autoSize = "left";
// rendiamo il nostro campo html=true per fare in modo che i tag html che inseriremo vengano letti come tali
newsticker.testo.html = true;
// applichiamo un ciclo in cui leggeremo ogni valore di tipo object inserito nell'array "parsed"
for (var k = 0; k<parsed.length; k++) {
	// aggiungiamo il testo al campo con questa riga:
	//
	// aggiungo l'indirizzo della pagina con la notizia intera, accedendo alla proprietà "url" di ogni object contenuto nell'array
	// aggiungo la data accedendo alla proprietà "date" di ogni object contenuto nell'array
	// aggiungo il titolo accedendo alla proprietà "title" di ogni object contenuto nell'array
	newsticker.testo.htmlText += "<a href='"+parsed[k].url+"'>"+parsed[k].date+" "+parsed[k].title+"</a> - ";

}

Animare il textfield

Il passo successivo obbligato è quello di far muovere continuativamente il nostro campo di testo, operazione resa facile dalla classe Tween di cui abbiamo già visto in un precedente articolo.
Per assegnare la funzione direttamente al nostro clip utilizzeremo un modo di estendere i metodi della classe MovieClip, interna al filmato, la clausola prototype, in queto modo potremo richiamare la funzione direttamente sul clip newsticker, utilizzando i suoi percorsi per gestire anche le azioni che vedremo in seguito.

// creo una funzione prototipo per la classe MovieClip
MovieClip.prototype.animate = function() {
	// inizializzo una variabile che tenga traccia della dimensione massima della maschera (nel nostro caso è 200)
	// in questo modo ottengo un punto di partenza da cui far cominciare l'animazione
	var limit = this.mask._width;
	// inizializzo una variabile che mi indichi il punto in cui l'animazione dovrà finire
	// visto che l'animazione va da destra a sinistra, il valore sarà negativo, per ottenerlo moltiplico un vaolre positivo per -1
	var end = (this.testo._width)*-1;
	// sposto il campo di testo all'inizio del percorso di animazione
	this.testo._x = limit;
	// applico la tween che muove il campo di testo da "limit" a "end"
	this.t = new mx.transitions.Tween(this.testo, "_x", mx.transitions.easing.None.easeNone, limit, end, this.testo._width/20, true);
	// ogni volta che l'animazione finisce, viene fatta ricominciare da capo
	this.t.onMotionFinished = this.t.start;
};
// richiamo la funzione sul nostro MovieClip "newsticker"
newsticker.animate();

Bloccare animazione al passaggio del mouse

L'ultimo passo del nostro percorso consiste nel bloccare l'animazione, al momento del "rollover" sul newsticker e farla continuare al momento del "rollout". Sarebbe facile utilizzando i metodi dedicati della classe MovieClip (onRollOver e onRollOut), se non ci fosse un problema di interferenza tra questo metodo e il codice html con i links, contenuto nel testo... In pratica, detto in parole spicciole, se funziona uno, non funziona l'altro. E allora come porre rimedio a questa incopatibilità? Grazie alla classe Mouse, che ci mette a disposizione un evento che verifica quando il mouse si muove, e grazie al codice che scriveremo ora, ci aiuterà a capire quando ci troviamo in rollover e in rollout sul nostro clip newsticker.

// inizializzo una variabile booleana per verificare se quando il mosue si muove è sul newsticker e di conseguenza ne deve "uscire fuori" (rollout)
var rollout:Boolean = false;
// creo un oggetto "ascoltatore" che tiene traccia degli eventi applicabili alla classe Mouse
var mouseListener:Object = new Object();
// utilizzo l'evento onMouseMove per verificare se il mouse si trova in un determinato momento fuori/dentro newsticker
mouseListener.onMouseMove = function() {
	// se la variabile rollout è false, vuol dire che sto entrando (rollover) nel clip newsticker
	// se invece è true, vuol dire che ne sto uscendo (rollout)
	if (!rollout) {
		// se vado sul newsticker blocco l'animazione (if)
		// se ne esco, la riattivo (else)
		if (newsticker.mask.hitTest(_xmouse, _ymouse)) {
			newsticker.t.stop();
		} else {
			newsticker.t.resume();
			rollout = true;
		}
	} else {
		// quando rientro nel newsticker riabilito il primo blocco if/else
		if (newsticker.mask.hitTest(_xmouse, _ymouse)) {
			rollout = false;
		}
	}
};
// aggiungo l'ascoltatore alla classe Mouse, in modo da fargli verificare gli eventi sopra descritti
Mouse.addListener(mouseListener);

Questo è tutto, le applicazioni pratiche dei singoli passaggi possono essere molteplici, così come l'intera applicazione utilizzabile ad esempio per creare un lettore di RSS per il proprio sito. Spero come al solito che la cosa sia gradita, alla prossima.