Centrer une image avec htmlText

Portrait de titouille

Aujourd'hui je cherchais à centrer une image dans un TextField dont le texte est passé à la propriété htmlText.

Mon image, dont les dimensions sont de 100 pixels de large pour 80 pixels de haut, est intégrée dans le texte sous un format html de la manière la plus simple qui soit : (désolé mais je suis obligé de mettre les balises P entre crochet sinon elles n'apparaissent pas...)

[p]<img src="monimage" />[/p][p]mon image ci-dessus[/p]

Le but étant d'avoir une image centrée, puis le texte en dessous, il fallait bien entendu appliquer quelques modifications à ce code.

En l'état, mon image était affichée à gauche, avec un léger décalage à gauche (donc pas aligné sur le texte qui pourrait venir en dessous), et le texte à droite de l'image.

Tout d'abord, après quelques recherches, j'ai pu trouver comment supprimer le décalage à gauche. Tout simplement en utilisant la propriété hspace de la balise img. Cette propriété étant par défaut à 8, le décalage est donc normal. En valorisant cette propriété à 0, le décalage disparait et mon image est bien affichée à gauche, alignée avec le texte qui pourrait se trouver en dessous.

[p]<img src="monimage" hspace="0" />[/p][p]mon image ci-dessus[/p]

Mais ceci n'est en réalité pas mon problème. Je voulais simplement vous partager cette petite info. Bref...

Mon image devant être alignée au centre, je me suis penché sur les différentes propriété des balises html intégrable dans un htmlText :

Tout d'abord la balise img autorise une propriété align, mais cette dernière ne prend en charge que les valeurs "left" et "right". Ce n'est donc pas la solution.

En second test, la balise p autorise également une propriété align, qui cette fois prend en charge "left", "right", "justify" et le fameux "center". J'ai espéré que c'était ma solution, mais l'alignement central du paragraphe n'influe aucunement sur l'alignement de l'image.

Le problème est double : faire en sorte que le texte soit situé en dessous de l'image, et aligner l'image au centre. Réglons d'abord le premier problème. Pour placer le texte en dessous de l'image, j'aurai pu rajouter quelques retour chariot (br) afin de tirer le texte vers le bas, mais ce n'est pas très propre d'agir de la sorte. En réalité, l'utilisation de la balise textformat s'avère nécessaire pour solutionner le problème de manière plus propre :

[p]<textformat leading="90"><img src="monimage" /></textformat>[/p][p]mon image ci-dessus[/p]

L'application de la propriété leading permet de définir la hauteur de la zone, et tout ce qui se trouve après sera repoussé du nombre de pixels attribué à cette propriété.
Mon image se trouve donc maintenant au sommet du textField, et le texte se trouve en dessous. Premier problème résolu.

Le second problème étant toujours d'actualité, je m'y suis attelé et c'est la méthode getImageReference du textField qui s'est avérée utile.

Cette méthode permet de récupérer la référence d'une image ou d'un swf chargé dans le texte, sous la forme d'une instance de DisplayObject. Lorsqu'on intègre une image dans du html, le textField va créer une instance de Loader pour charger cette image et l'intégrer dans le textField. Ainsi, il est possible de récupérer directement l'instance de cet objet et de le manipuler comme bon nous semble. Dans le cas de mon problème d'alignement, je désirais récupérer l'instance de DisplayObject casté en Loader, intégrer un écouteur sur le chargement complet de l'image et enfin, manipuler la propriété x de l'image pour la placer au bon endroit dans mon textField. Et ça marche !! Sous réserve d'un petit contournement tout de même. Malheureusement, tenter de traiter l'image directement dans le gestionnaire "complete" du Loader ne fonctionne pas... L'image est accessible, mais il semblerai qu'elle ne soit pas encore affectée au textField.

J'utilise souvent une petite méthode "wait" qui permet de donner un temps d'attente avant d'exécuter une nouvelle fonction. Je passe à cette méthode le nom de la méthode à appeler après le temps d'attente, le délai d'attente ainsi que d'éventuels paramètres. En utilisant cette méthode, avec un temps d'attente de 0, c'est fonctionnel.

Au final, voici le code :

this._text.html = '[p]<textformat leading="90"><img src="monimage" id="myimg" /></textformat>[/p][p]mon image ci-dessus[/p]';
 
var l:Loader = this._text.getImageReference( "myimg" ) as Loader;
if( l != null )
{
	l.visible = false;
	l.contentLoaderInfo.addEventListener( Event.COMPLETE, this._onComplete );
}
 
private function onComplete( event:Event ):void
{
	this.wait( manipulatePic, 0 );	
}
 
private function manipulatePic():void
{
	var myimg:DisplayObject = this._text.getImageReference( "myimg" );
 
	myimg.x = 380 / 2 - myimg.width / 2;			
	myimg.visible = true;
}
 
private var timer:Timer = new Timer( 1000 );
private function wait( nextFunction:Function, time:int=1000, args:Array=null ):void
{
	// anonymous function called by listener when time is elapsed
	var f:Function = function( e:TimerEvent ):void
	{
		timer.removeEventListener( TimerEvent.TIMER_COMPLETE, f );
		nextFunction.apply( this, args );
	}
 
	timer.delay = time;
	timer.repeatCount = 1;
	timer.addEventListener( TimerEvent.TIMER_COMPLETE, f );
	timer.start();
}

Notez le "myimg" en tant qu'identifiant de l'image, ce qui permet de la récupérer ensuite via getImageReference

Et voilà Smile

sources :
Images in a Flash text field or TextArea
Documentation officielle AS3 sur TextField htmlText
Handling image load errors in a textfield

[EDIT]
J'ai pu remarquer quelques petits comportements bizarre en utilisant cette méthode d'alignement et le chargement de css. A la base, j'utilisais un TextFormat appliqué à mon TextField, avec une police embarquée.
J'ai voulu rajouter une css pour pouvoir formater un peu les balises, par exemple H1. Au final, mon texte était un peu comme le suivant :

[p]<textformat leading="90"><img id=" src="monimage" /></textformat>[/p]
<h1>Mon titre</h1>
[p]mon image ci-dessus.
Elle assure[/p]

En supprimant l'affectation de mon TextFormat (this._text.defaultTextFormat = this._tf) mon titre (H1) s'est retrouvé sous l'image. De plus, lorsque je cliquais sur le TextField, les images reprenaient leur place d'origine, aligné à gauche. Je n'ai malheureusement plus réussi à reproduire ce second comportement après avoir fait certaines modifications à mon code HTML. Mais tout ça pour dire que l'application d'un TextFormat change encore le comportement du TextField, même si on applique une css par dessus.

Au final, en gardant appliqué le TextFormat et en rajoutant la css ensuite, mon affichage est correct. Si je n'applique pas le TextFormat, je dois mettre entre balises P mon titre pour qu'il ne se retrouve pas sous l'image.
[/EDIT]

[EDIT 2]
J'ai réussi à reproduire le problème des images qui se remettent à leur position initiale (à gauche) lorsqu'on clique sur le TextField. Après quelques tests, j'ai enfin compris le truc. Tout est en rapport avec la propriété leading de la balise "textformat".
Entre-temps, j'avais rajouté une nouvelle image dans mon code html, pour en avoir une au dessus et une en dessous. 2x la même image, avec un texte entre les deux. J'ai fait quelques modifs sur mon code pour pouvoir appliquer le "centrage" sur toutes les images ayant un id incrémental du genre "image0", "image1", "image2", etc...
Mon image fait 80 de haut. Si je mets un leading de 90, le problème survient. Si je mets un leading de 100 sur la première et un de 90 sur la seconde, le problème survient également. Enfin, si je mets un leading de 100 sur les 2 images, à ce moment-là plus de problèmes. J'ai fait plusieurs tests dans ce sens, et c'est vraiment la valeur de la propriété leading qui implique ce comportement bizarre, donc faites attention à valoriser un leading assez grand par rapport à l'image Wink
[/EDIT 2]