Это очень интересная проблема. Используя предложение Кея, я подумал о методе, который бы возвращал Array
из Rectangle
объектов, ядро которых соответствовало бы местам текста. Я использую множественное число, потому что может быть несколько прямоугольников, если текст переносится по слову.
function getPhraseLocation(phrase:String, field:TextField):Array {
// initialise the return array
var locations:Array = new Array();
// find the first and last chars
var firstChar = field.text.indexOf(phrase);
var lastChar = firstChar+phrase.length;
// determine the bounding rectangle of the first char
var firstCharRect = field.getCharBoundaries(firstChar);
var crtLocation:Rectangle = new Rectangle(firstCharRect.left,firstCharRect.top,firstCharRect.width,firstCharRect.height);
// while there are chars left in the string
var crtChar:uint = firstChar;
while (++crtChar<lastChar)
// if they're on the same line, expand the current rectangle
if (field.getCharBoundaries(crtChar).y==crtLocation.y) crtLocation.width = uint(crtLocation.width)+field.getCharBoundaries(crtChar).width;
// if they're on the next line, due to word wrapping, create a new rectangle
else {
locations.push(crtLocation);
var crtCharRect = field.getCharBoundaries(crtChar);
crtLocation = new Rectangle(crtCharRect.left,crtCharRect.top,crtCharRect.width,crtCharRect.height);
}
// add the last rectangle to the array
locations.push(crtLocation);
// return the array
return(locations);
}
Давайте предположим, что мы создали TextField
примерно так:
var field:TextField = new TextField();
this.addChild(field);
// move the text field to some random coordinates
field.x = 50;
field.y = 50;
// set wordwrap to true, to test the multiline behaviour of our function
field.wordWrap = true;
// set a smaller width than our text
field.width = 300;
// disable selectability, I'm not sure it would work properly, anyway
field.selectable = false;
// fill the textfield with some random html text
field.htmlText = 'Lorem ipsum dolor sit amet, consectetur adipiscing <a href="http://www.stackoverflow.com">elit. Aliquam et</a> elementum lorem. Praesent vitae nunc at mi venenatis auctor.';
Теперь, чтобы получить прослушиватель событий, мы должны создать объект и нарисовать прямоугольники поверх реального текста. Прямоугольники нарисованы в 0% альфа, поэтому они невидимы.
// create a sprite and add it to the display list
var overlay:Sprite = new Sprite();
this.addChild(overlay);
// enable mouse actions on it and make the cursor change on hover
overlay.mouseEnabled = true;
overlay.buttonMode = true;
// call the function that returns the size and position of the bounding boxes
var locationArray:Array = getPhraseLocation('elit. Aliquam et',field);
// draw each rectangle in white transparent fill
for each (var bounds:Rectangle in locationArray) {
overlay.graphics.beginFill(0xff0000,0);
overlay.graphics.drawRect(bounds.x+field.x-overlay.x, bounds.y+field.y-overlay.y, bounds.width, bounds.height);
overlay.graphics.endFill();
}
Затем добавьте прослушиватель событий для MouseOver
:
overlay.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
function mouseOverHandler(evt:MouseEvent):void {
trace('mouse over key phrase');
// do whatever else you want to do
}
К сожалению, поскольку мы рисуем что-то поверх реального текста, ссылки становятся неактивными. Таким образом, мы должны добавить прослушиватели событий для клика, а также:
overlay.addEventListener(MouseEvent.CLICK, clickHandler);
function clickHandler(evt:MouseEvent):void {
navigateToURL(new URLRequest('http://www.stackoverflow.com'));
}
Поскольку мы ранее установили атрибут buttonMode
на true
, мышь изменит свой курсор, ведя себя точно так же, как если бы ссылка в тексте работала.
Я определил множество переменных, чтобы код был проще для понимания. Код может быть сокращен и оптимизирован, но он также должен нормально работать.
Это адский обходной путь для самых простых задач, но он работает. Надеюсь, это полезно.