Зертханалық жұмыс №21

Тақырыбы:Flash ортасында мәтіндік ақппаратпен жұмыс істеу.

Мақсаты: Flash ортасында класстарды құруды және қолдануды үйрену.

Бақылау сұрақтары:

1. Мәтіндік ақппарат

2. Мәтін түрлері

3. Кірістірілген шрифт

Студенттің өзіндік жеке жұмыс тапсырмалары:

1. Статикалық мәтінді құрып оған анимация жасаңыз

2. Динамикалық мәтінді құрып оның жазылуын бағдарламалық түрінде орынданыз

3. Шағын ойын жасап оның нәтижелерін динамикалық мәтінге шығарыңыз

4. Келесі жобаны орынданыз

Мысалы1

Статикалық мәтінді құрып – ерекшелеу - ПКМ ­­- Break apart- Break apart - әріптерді клип ретінде қолданамыз

Мысалы2

if (drag.hitTest(target1)) {

dyn.text = "da";}

else {

dyn.text = "net";

}

Проект 13: Текстовые эффекты

Вопреки сложившейся традиции, проект этой главы не будет связан с созданием объемного и сложного кода. Мы разработаем два довольно простых текстовых эффекта. Первый, самый элементарный, будет представлять собой текст, вращающийся по кругу. Немного набив руку, мы займемся созданием эффекта, в котором текст будет собираться из прилетающих с разных сторон букв.

Исходные файлы всех эффектов имеются в папке «Текстовые эффекты» папки «Проект 13» архива примеров. Просмотрите их, чтобы иметь представление, над чем мы будем работать ближайшие пару часов.

Текстовый круг

Расположить буквы текста по кругу — это задача, схожая с рисованием круга. Чтобы се решить, нужно задать параметрическую систему уравнений окружности, вычислить координаты для каждой буквы и разместить исходя из них соответствующие текстовые поля. Попробуйте, следуя описанной идее, самостоятельно реализовать эффект вращающегося по кругу текста. Если не получится, прочитайте описание авторского решения поставленной проблемы.

Текстовый круг — это весьма красочный эффект, который вполне может пригодиться на практике.

Поэтому имеет смысл написать создающий его код в форме переносимого метода класса MovieClip:

MovieClip.prototype.roundText = function (eff:String, depth:Number, text_p: String, x:Number, у:Number, format:Text.Format, N:Number) {}

Метод roundText будет принимать 7 параметров:

• eff — имя клипа, который будет являться носителем эффекта. Должно быть прописано в фор-

ме строки;

• depth — глубина, занимаемая клипом с эффектом;

• text_p — текст, который метод должен отобразить в форме окружности;

• х и у — координаты центра текстового круга;

• format — объект класса TextFormat, определяющий особенности форматирования текста;

• n — количество оборотов, которые текст должен совершать в секунду.

При запуске метода roundText в первую очередь должен быть создан клип-носитель эффекта:

var effect:MovieClip = this.CreateEmptyMovieClip(eff, depth);

Далее мы должны определить, какого радиуса нужно сделать круг, чтобы в нем смогли разместиться, не пересекаясь, все буквы текста. Очевидно, что длина окружности, образуемой текстом, должна быть сопоставима с его длиной при отображении в виде горизонтальной строки. Она даже будет чуть больше, так как при размещении по кругу символы смещены относительно друг друга как по горизонтали, так и по вертикали. Поэтому длину строки нужно вычислить с определенным запасом. Для этого подсчитаем, сколько пикселей займет данный текст, если все символы заменить буквой «W» (самая широкая буква). Определить же ширину символа, заданного в некотором стиле, позволяет метод getTextExtent():

var text_width:Number = format.getTextExtent("W").width*text_p.length;

Узнав приблизительную длину окружности, в которой сможет отобразиться весь текст, вычислить радиус не составит труда (Iокр = 2πR ):

var radius:Number=text_width/(2*Math.PI);

Чтобы буквы текста можно было разместить по кругу, каждая из них должна располагаться в отдельном текстовом поле. Поэтому далее необходимо запустить цикл из char_numb итераций, где char_numb — количество символов в строке:

var char_numb:Number = text_p.length; for (var i = 0; i<char_numb; i++) {}

В цикле в первую очередь мы должны вычислить координаты для создаваемого текстового поля. Для этого нужно использовать параметрические уравнения окружности (с ними мы уже встречались, когда изучали программное рисование):

X = X0 + R ⋅cos(ϕ)

Y = Y0 + R ⋅cos(ϕ)

Здесь X и Y — искомые координаты; Х0 и Y0 — координаты центра окружности; R — радиус; ϕ

— угол поворота отрезка, соединяющего точку (X, Y) и центр окружности.

Чтобы символы равномерно распределились по окружности, ее нужно разделить на столько секторов, сколько в тексте имеется знаков. Определить положение n-го символа по приведенным формулам можно, вычислив угол ср как произведение величины угла сектора и n:

phi = 2*Math.PI/char_numb*i; х_р = x+radius*Math.cos(phi); y_p = y+radius*Math.sin(phi);

Определив необходимые координаты, создаем текстовое поле, нужным образом настраиваем его и отображаем соответствующую ему букву в заданном пользователем стиле:

effect.createTextField("pole"+i, i, x_p, y_p, 0, 0); with (effect["pole"+i]) {

text=text_p.substring(i, i+1), autoSize=true, setTextFormat(format); }

Протестировав созданный код, вы увидите, что того, чтобы текст расположился по (ругу, мы уже добились. Теперь необходимо сделать так, чтобы он вращался.

Реализовать вращение текста очень просто. Для этого необходимо достаточно час-го пересчитывать координаты символов, прибавляя к углу ϕ в задающих их уравнениях, на который текст должен повернуться. Вполне приемлемой частотой такой модификации будет частота смены кадров в фильме:

effect.n=0, effect.x=x, effect.y=y, effect.radius=radius, effect.fr=N, effect.step=2*Math.PI/char_numb; effeet.onEnterFrame = function():Void { var I:Number = 0;

while (this["pole"+i]!= undefined) { this["pole"+i]._x = this.x+this.radius*Math.cos(this.step*(I +this.n)); this["pole"+i]._y = this.y+this.radius*Math.sin(this.step*(I +this.n)); i++;

}

this.n += 2*Math.PI/12*this.fr;

};

Приведенный код довольно прозрачен и в пояснении в нем нуждается только два фрагмента:

• Первая строка. Мы сохраняем в виде свойств клипа effect все параметры, которые требуются для кода в обработчике onEnterFrame. Это необходимо сделать, так как соответствующий метод будет активизироваться тогда, когда активизации метода roundText() уже не будет существовать и, следовательно, обратиться к ее локальным переменным и аргументам будет невозможно.

• Свойство n хранит угол, на который повернулся текстовый круг по сравнению с начальным положением. При каждой активизации обработчика onEnterFrame оно наращивается на угол, определяемый делением угла, на который круг поворачивается за одну секунду, на стандартную частоту кадров.

Готово (рис. 13.32). Тестируем метод и. если все в порядке, переходим к созданию второго, более сложного эффекта.

Рис. 13.32. Текстовый круг

Побуквенная сборка

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

На первый взгляд может показаться, что реализовать задуманный эффект очень просто. Действительно, помещаем каждый символ в отдельное текстовое поле, вычисляем исходя из его индекса координаты и перемещаем объект в нужную точку. Однако, подумав о конкретной реализации данного подхода, от него мы сразу откажемся. Почему? Хотя бы потому, вес символы имеют разную ширину. Поэтому определить позицию буквы, просто умножив ее индекс на ширину символа, не получится. Конечно, можно воспользоваться моноширинным шрифтом. Это решит проблему разной ширины символов, но тут же возникнут другие сложности. Как рассчитать позиции символов в случае многострочных полей? Как сделать так, чтобы текст в поле мог быть задан в нескольких стилях? Как учесть особенности форматирования текста (тип выравнивания, поля, отступы)? Очевидно, что разрешить все эти «как» напрямую вряд ли возможно.

В сложных ситуациях, подобных описанной, стоит искать не прямые, а косвенные решения. Таким решением в нашем случае будет следующийалгоритм:

1. Создаем поле с нужным текстом. Цвет для шрифта задаем белым. При этом текст сольется с

фоном, и будет казаться, что его просто нет.

2. Создаем точно такое же поле, как и в первом пункте, в точке со случайными координатами. Вместо всех символов, кроме одной, случайно определяемой буквы, отображаем пробелы. Чтобы особенности форматирования текста при этом не нарушились, применяем моноширинный шрифт.

3. Единственную букву в тексте второго поля делаем черной. При этом создастся впечатление,

что появился только один символ.

4. Заставляем второе поле двигаться по направлению к первому. При этом будет казаться, что

символ летит на положенное ему место.

5. Когда координаты полей совпадут, удаляем поле с символом. В первом же поле делаем види-

мой букву, занимающую ту же позицию, что и «прилетевший» символ.

6. Далее повторяем действия пунктов 2—5 до тех пор, пока весь текст не будет собран. Естественно, что «прилетать» должны лишь еще не отображенные символы. Поэтому в специальном массиве необходимо хранить сведения о том, цвет каких букв первого поля все еще белый.

Согласитесь, что описанный алгоритм позволяет весьма изящно справляться со всеми сложностями, которые не дают реализовать задуманный эффект менее изощренным способом. Написать же материализующий его код совсем несложно.

Отвечать за создание поля, в которое «прилетят» символы, будет функция createMain(), принимающая в качестве параметров координаты точки, в которой должен располагаться его верхний левый угол:

function createMain(x:Number, у:Number):Void {}

При вызове функции createMain() в первую очередь создаем требующееся поле, настраиваем его характеристики и отображаем переданный пользователем текст. Хранить соответствующую строку будет специальная переменная text_p:

this.createTextField("pole1", 0, х, у, 0, 0); pole1.text = text_p;

pole1.autoSize="left", pole1.embedFonts=pole1.border-true, pole1.selectable=false;

Все приведенные настройки поля довольно очевидны — кроме одной. Поле просто необходимо сделать невыделяемым. Иначе, при выделении, невидимые ранее символы отобразятся — и сразу станет ясно, как был реализован эффект.

Далее настраиваем стиль текста. В качестве гарнитуры прописываем какой-нибудь моноширинный шрифт (например, Courier New). Оптимальным же вариантом будет встроить данный шрифт в фильм. При этом, во-первых, эффект будет красивее, а во-вторых, исчезнет риск сбоя из-за того, что у пользователя не окажется нужного шрифта.

Итак, встраиваем моноширинный шрифта фильм и даем ему идентификатор экспорта font. Размер шрифта задаем относительно крупным (20—30 пунктов). Цвет символов должен быть белым. Выравнивание текста — по центру.

pole1.setTextFormat(new TextFormat("font", 20, 0xFFFFFF, null, null, null, null, null, "center"));

Теперь необходимо сформировать массив, в котором будут храниться индексы еще не отображенных символов. Так как изначально все символы невидимы, то в массив нужно занести индексы всех символов в строке, кроме пробелов, символов табуляции, знаков переноса строки и перевода каретки:

var char:String = null, n:Number = 0; for (var i = 0; i<text_p.length; i++) { char = text_p.length.charAt(i);

if (char!= " " && char!= "\r" && char!= "\t" && char!= "\n") {

// Пустой массив arr будет создаваться кодом инициализации arr[n++] = i;

}

После всех подготовительных операций запускаем основной алгоритм. Для этого необходимо активизировать функцию createSymbol(), создающую поле с «летящим» символом:

createSymb();

Ядром нашего проекта будет функция createSymb(), отвечающая за создание отдельных символов:

function.createSymb():Void {}

При запуске createSymb() в первую очередь мы должны создать точно такое же поле, как и формируемое функцией createMain(). Его позиция должна определяться случайным образом. Это даст эффект того, что символы в текст будут прилетать с разных сторон:

this.createTextField{"pole2", 1, -100 + 700*Math.random(), -100 +

600*Math.random(), 0, 0);

pole2.autoSize = pole2.embedFonts=true, pole2.selectable=false;

Поместить поле pole2 необходимо на большую глубину по сравнению с полем pole1. Иначе новый символ будет не пролетать над уже сформированным текстом, а скользить под ним.

Далее мы должны случайным образом определить, какой символ прилетит на этот раз, Для этого генерируем случайный индекс исходя из длины массива arr и читаем значение соответствующего элемента:

var index:Number = Math.round(arr.length*Math.random()); n = arr[index];

Уже отображенный символ повторно прилетать не должен. Чтобы это гарантировать, удаляем из массива arr элемент, исходя из которого будет визуализирован символ на данном вызове createSymb():

arr.splice(index, 1);

Далее необходимо сформировать строку для поля pole2. Для этого заменяем все символы с индексом, отличным от index, на пробелы. Так как мы используем моноширинный шрифт, нарушения форматирования от этого не произойдет. Очевидно, что помимо визуализируемого символа не следует заменять пробелы, знаки табуляции, символы перевода каретки и переноса строки (иначе либо будут проделаны лишние операции, либо изменится вид текста):

var pole2_text:String = text_p, char:String = null; for (var i = 0; i<pole2_text.length; i++} { char = pole2_text.charAt(i);

if (i!= n && char!= " " && char!= "\r" && char!= "\t" && char!= "\n") { pole2_text = pole2_text.slice(0, i)+" "+ pole2_text.slice(I +1);

}

}

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

pole2.text = pole2_text;

pole2.setTextFormat(new TextFormat("font", 20, 0, null, null, null, null, null, "center"));

Движение поля pole2 к полю pole1 мы реализуем очень просто. Для этого вычислим разности координат поля символа и статичного поля, а затем разделим полученные значения на число шагов, за которые поле должно достигнуть цели (их должно быть 20-30). Это даст нам величины необходимых шагов. Периодически отнимая от координат поля pole2 значения соответствующих шагов, мы получим эффект равномерного прямолинейного движения символа от начальной точки к положенной для него позиции в тексте.

Хранить величину шага для каждой координаты будут специальные переменные step_x и step_y:

var delta_x:Number = (pole2._x-pole1._x); var delta_y:Number = (pole2._y-pole1._y);

step_x = delta_x/20; // Символ достигнет цели за 20 шагов step_y = delta_y/20;

После того как поле с символом будет создано и все необходимые величины определены, запускаем алгоритм движения буквы. Для этого задаем цикл setInterval для функции charMover(). При вызове данной функции поле pole2 сместится на один шаг. Следовательно, чем выше частота работы цикла, тем быстрее «летают» символы. В нашем случае скорость движения букв будет определяться значением особой переменной speed_t:

loop = setInterval(charMover, 100-speed_t);

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

Третьим и последним этапом реализации нашего проекта будет создание функции charMover(), отвечающей за движение символа:

function charMover():Void {}

Перед тем как переместить символ еще на шаг, нужно проверить, не достигли он цели. Для этого необходимо вычислить расстояние между полями pole1 и pole2. Если оно окажется меньше некоторого минимума (3—5 пикселей), то поле символа должно быть удалено, а соответствующая буква в тексте поля pole1, наоборот, отображена. Также при этом необходимо прервать работу цикла setInterval.

var rast:Number = Math.sqrt((pole2._x-pole1._x)*(pole2._x-pole1._x)+

(pole2._y-pole1._y)*(pole2._y-pole1._y)); if (rast<3) {

pole2.removeTextField();

pole1.setTextFormat(n, new TextFormat(null, null, 0)); clearInterval(loop);

}

Вы можете спросить, почему нельзя проверять точное равенство координат полей pole1 и pole2. Действительно, если шаг равен 1/20 первоначального расстояния между ними, то через 20 вызовов функции charMover() координаты полей должны в точности совпасть. Однако на практике это происходит далеко не всегда. Причина этого довольно тонкая. Дело в том, что свойства _х и _у могут быть заданы с точностью только до 0,05 пикселя. При попытке присвоить им более точное значение оно будет округлено. При этом может возникнуть ошибка, которая станет накапливаться при проведении этой операции многократно. Именно ошибка округления обусловливает то, что через 20 шагов координаты полей pole1 и pole2 могут не совпасть, Конечно, разница будет самой минимальной: не более 1 пикселя, но этого будет достаточно, чтобы равенство не выполнилось. Чтобы учесть возможность возникновения и накопления ошибки округления при определении свойств _х и _у, мы будем считать, что текстовые поля совместились, если расстояние между их точками отсчета менее 3 пикселей.

Если условие равенства позиций текстовых полей выполнится, также необходимо проверить, имеются ли еще не отображенные символы. Если они есть, должна быть активизирована функция createChar(), которая создаст очередную летящую букву, — и так до тех пор, пока в массиве агг не останется ни одного элемента:

if (arr.length!= 0) { createSymb();

}

Если окажется, что поля еще не совместились, поле с символом должно быть на шаг передвинуто к полю с основным текстом:

pole2._x -= step_x; pole2._y -= step_y;

Чтобы движение символов было равномерным и не зависящим от частоты кадров в фильме, после смещения поля pole2 обновляем экран:

updateAfterEvent();

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

Для этого:

• Добавляем на рабочее поле большое поле ввода с поддержкой многострочного режима без автоматических переносов. Назовем его user_text. В данном поле пользователь будет задавать текст для эффекта,

• Создаем небольшое поле ввода и назовем его speed. Ограничиваем.количество допустимых для введения в него символов двумя. В этом поле будет определяться (числом от 0 до 99) скорость, с которой должны двигаться буквы.

• Изготавливаем кнопку и назовем ее start. Она будет запускать работу эффекта по событию

onPress. Необходимый для этого код абсолютно очевиден:

but.onPress = function():Void {

// Обнуляем переменные

n=null, text_p=user_text.text, arr=[], speed_t=speed.text;

// Останавливаем работу цикла setInterval clearInterval(loop);

createMain(100, 100); // Активизируем начало работы алгоритма };

Авторский вариант эффекта сохранен как random.fla папки «Текстовые эффекты» папки Проект

13.

Наши рекомендации