Внимание!
Для работы с этой лекцией необходимы учебные
файлы, которые Вы можете загрузить
здесь.
В
прошлых версиях Flash для создания графики в режиме
run-time было не
обойтись без предопределенных символов и манипуляций
ими с помощью кода. Разумеется, вы могли взять
рисунок из Library и делать с ним интересные вещи,
но вы были сильно ограничены в возможности создавать
полезные, динамичные рисунки, не нарисовав их перед
этим вручную. Один из первых проектов, которые
создают все пользователи Flash - это программа для
рисования, однако быстро становится ясно, что на
площади 100х100 пикселей с помощью тонкой линии и
дубликатов фильмов можно нарисовать не слишком много
интересного.
Теперь
Flash MX Macromedia предоставляет возможность
создания графики в режиме
run-time, и эта возможность уникальна (да,
громко сказано, однако нигде больше создание
динамических рисунков не осуществляется именно так,
и именно этому посвящена вся данная лекция).
С
использованием рисования API (Application
Programming Interface) и новых методов фильмов
createEmptyMovieClip и
createTextField
пользователи Flash теперь могут программировать и
создавать все, что угодно, начиная с кнопок и форм и
заканчивая приложениями для рисования и целыми
интерфейсами, без предварительного ручного рисования.
Этому будут посвящены следующие несколько лекций.
Рисование линий с
помощью программирования
Перед тем, как рассматривать более сложные
приложения рисования API, проведем обзор некоторых
методов, изученных в лекции 3.
-
lineStyle
устанавливает визуальные параметры линии, такие
как высота, цвет и интенсивность.
-
moveTo
перемещает "карандаш" на новые координаты
рабочего стола без рисования линии.
- lineTo рисует
прямую линию от текущей позиции карандаша в
заданные координаты, а curveTo создает кривую
линию к координатам данной фиксированной точки
посредством искривления линии с помощью
изменения координат контрольной точки.
- Наконец,
beginFill и
endFill являются
выражениями для начала и окончания заливки
цветом фигур, а clear
удаляет заливку.
Осталась еще одна команда, которой мы пока не
касались -
beginGradientFill. Далее в этой лекции мы
рассмотрим ее.
Итак, выполним небольшое упражнение с
использованием рисования API для создания линий
различного размера и цвета.
Беспорядочные
линии
В этом
маленьком упражнении вы узнаете, как с помощью всего
лишь нескольких коротких строк кода можно создать
довольно впечатляющий эффект (файл
chaosLines.fla на
компакт-диске).
- Создайте
новый фильм и назовите слой по умолчанию именем
actions. Откройте
панель Actions и введите следующий код в кадр 1.
pos = {x:0, y:0};
stageWidth = Stage.width;
stageHeight = Stage.height;
Эти
три строки инициализируют переменные для нашего
эксперимента. Сначала мы создадим новый объект с
именем pos, содержащий параметры
x и
y, которые будут
нашими переменными координатами на протяжении
всего фильма. Затем указываем параметры нового
объекта Stage и
записываем их значения в переменные. Если не
сделать этого в начале, эти значения смогут
изменяться в течение эксперимента, и строки
будут рисоваться вне области рабочего места.
Учтите, что при объявлении новых массивов или
объектов я использую не конструктор
new, а быстрое
объявление. Поэтому новым пустым массивом или
пустым объектом в коде будут, соответственно:
myArray = [];
myObject {};
Все
работает точно так же, как приведенное ниже
объявление объектов и массивов.
myArray = new Array
();
myObject = new Object();
Наше объявление pos в шаге 1 может быть записано
так.
pos = new Object();
pos.x = 0;
pos.y = 0;
Но
более удобной для чтения и краткой оказалась
запись в одну строку, в особенности для объектов
и массивов с ограниченным числом индексов или
параметров. Кроме того, изменение имени объекта
(по крайней мере, в данном инстансе) требует
изменения одной строки, а не трех. Независимо от
формы, которую вы выберете, все сводится к стилю
программирования и вашим предпочтениям.
Я
также не объявляю переменные с помощью var, если
они не появляются в функциях, а делаю это лишь
для отведения для них места внутри функции. Во
Flash нет необходимости использовать
var для объявления
переменной. Разница в том, что при использовании
этого выражения в функции ограничивается
отводимое для переменной место. Лучше никогда не
вводить лишний код!
К
слову: дополнительный код необходим в другой
области, особенно если вы избрали сокращенное
программирование - это комментарии. Всегда
добавляйте в код комментарии во все места, где
может возникнуть хоть малейшая неясность.
Позднее вы поблагодарите за это самого себя. (В
коде этих лекций комментарии не включены, так
как к каждому упражнению есть сопроводительный
текст. Файлы FLA с комментариями могут быть
найдены на прилагаемом к книге компакт-диске.)
- Теперь
перейдем к рассмотрению функции рисования. Под
предыдущими тремя строками введите следующий
код.
placeLine = function
() {
var thickness = Math.ceil(Math.random()*4);
var lineColor = Math.floor(Math.random()*16777215);
this.lineStyle(thickness, lineColor, 80);
pos.x = Math.random()* stageWidth;
pos.у = Math.random()* stageHeight;
this.lineTo(pos.x, pos.y);
};
Какие действия он выполняет? Сначала здесь
устанавливается локальная переменная
thickness для
случайного числа между 1 и 4 включительно.
Math.random будет
возвращать число с плавающей точкой (в основном
- десятичные дроби) между 0 и 1, которое будет
умножаться на 4.
Math.cell является методом округления,
который будет округлять это десятичное число до
его ближайшего целого значения. Далее,
lineColor
присваивается случайный цвет из 16 миллионов
доступных. Math.floor
округляет это число до ближайшего целого
значения. Наконец, функция устанавливает степень
прозрачности линии на 80%. Все это передается в
виде параметров методу lineStyle.
Возможно, вы удивитесь, что можно отправлять
метод рисования в десятичном виде, в то время
как по правилам полагается отправлять
шестнадцатеричное число. Причиной этому является
то, что при вводе 0x перед шестнадцатеричным
значением в коде Flash автоматически переводит
это значение в его десятичный эквивалент.
Попробуйте ввести trace
(0xFF); в пустой фильм и вы увидите, что
Flash возвратит значение 255. В нашем коде выше
16777215 является десятичным эквивалентом числа
0xFFFFFF, что соответствует белому цвету.
Посредством выбора случайного целого числа между
0 и 16777215 мы можем присваивать случайный цвет
из имеющегося диапазона цветов.
Далее в функции placeLine
мы устанавливаем новые координаты посредством
поиска случайного числа в диапазоне чисел,
соответствующем разрешению экрана. Установив
новую позицию, рисуем линию. Имейте в виду, что
мы опять не используем метод moveTo, поэтому
каждый раз при вызове этой функции линия будет
прорисовываться от точки, в которой
заканчивается предыдущая линия. Карандаш
остается активным.
- Переходим к
последнему шагу. Введите:
this.onEnterFrame =
placeLine;
Очень удобно иметь возможность динамически
присваивать управляющие элементы! Это инстанс, в
котором сохраняются некоторые дополнительные
шаги, когда нам необходимо повторно выполнять
код. Во Flash 5 нужно было создавать пустой
фильм для запуска всего кода при событии
onEnterFrame, или
же устанавливать цикл кадра на главной точке
временной диаграммы. Теперь можно присваивать
управляющий элемент события
onEnterFrame самой
главной точке временной диаграммы! В каждом
кадре фильма будет вызываться
placeLine, что
обеспечит беспрепятственную работу цикла.
- Запустите
фильм и наслаждайтесь результатом рисования API
с использованием случайных значений! Возможно,
это не самый эффектный результат
программирования с участием случайных значений,
однако довольно удобно с помощью менее чем
десяти строк создать подобный эффект без
предварительного рисования.
Анимация рисунков
Беспорядочные линии выглядят довольно занимательно,
но они едва ли являются примером программированной
анимации. С помощью API можно реализовать несравнимо
более интересные эффекты. Так как числа являются
единственными параметрами для отправки в функции
рисования, довольно легко создавать анимированные
рисунки в фильмах, просто изменяя числа,
отправляемые в каждом кадре. Вы также можете легко
изменить частоты смены кадров для анимации, лишь
вызывая функцию рисования на установленных
интервалах (с использованием подходящего и
соответствующим образом названного setInterval).
Единственный "фокус" в анимации с помощью рисования
API состоит в получении значений для ваших функций.
Сначала создадим несложный инструмент, который
поможет нам выполнить эту задачу. Это будет
небольшой фильм для рисования линий на рабочем
столе. Каждый рисунок будет представлять собой буфер
перемещаемого изображения. После завершения создания
рисунков будет происходить считывание в окне Output
координат, необходимых для воссоздания рисунков с
помощью рисования API. (Изучите файл
API_animation.swf,
находящийся на компакт-диске.)
Генератор сцен
- Создайте
новый фильм с именем
celMaker.fla и добавьте следующий код в
первый кадр слоя по умолчанию.
startDraw = function
() {
shapes [shapeNum] = [];
this.moveTo(this._xmouse, this._ymouse);
this.onMouseMove = draw;
};
stopDraw = function () {
delete this.onMouseMove;
shapeNum++;
};
draw = function () {
this.lineTo(this._xmouse, this._ymouse);
shapes[shapeNum].push(Math.round(this._xmouse)+", "+
КMath.round (this._ymouse) );
};
Здесь мы создаем три небольших "черновых"
функции для поддержки рисования.
startDraw
перемещает карандаш на нашу текущую позицию мыши
и устанавливает функцию draw на выполнение при
каждом движении мыши.
shapes является массивом для хранения
позиций мыши.
stopDraw удаляет функцию рисования из элемента
управления onMouseMove
и увеличивает число фигур на 1 (перед каждой
новой фигурой нужна функция
moveTo).
draw является нашей текущей функцией рисования,
которая просто использует метод
lineTo для
рисования линии. Она также записывает позицию
мыши в массив shapes
таким образом, что мы впоследствии сможем
вставлять эти значения напрямую в код при
копировании и вставке из окна Output. Координаты
_x и
_y в (50, 100)
будут форматированы в виде строки типа 50, 100.
При трассировке кода в следующем шаге, мы сможем
вставлять это значение прямо в аргументы
lineTo.
- Добавьте
следующую функцию.
listVars = function ()
{
code = "shape"+layerNum+" = function() {"+newline;
code += " this.clear();"+newline;
code += " this.lineStyle(2, 0, 100);"+newline;
for (var i = 0; i<shapes. length; i++) {
code += "this.moveTo("+shapes[i] [0]+");"+newline;
for (var j = 1; j<shapes[i].length; j++) {
code += "
this.lineTo("+shapes[i][j]+");"+newline;
}
}
code += "}";
trace(code);
makeLayer();
};
listVars
впоследствии будет просто просматривать наши
массивы и выдавать нужный код. Я часто
устанавливаю такие методы трассировки для
автоматической генерации всего длинного кода.
Все, что в данном случае выполняют эти строки -
записывают координаты в строки, которые
отформатированы для копирования и вставки прямо
в панель Actions. Например, при трассировке
строки code в конце функции можно отправить в
окно Output следующее.
shape1 = function() {
this.clear();
this.lineStyle(2, 0, 100);
this.moveTo(50, 100);
this.lineTo(100, 200);
};
shapes.push("shape1") ;
После этого мы сможем вставить этот код в
область Script панели Actions, причем он уже
будет отформатирован соответствующим образом,
поэтому синтаксических ошибок не возникнет.
Довольно удобно.
- Добавьте эту
последнюю функцию, которая будет создавать
отдельные анимационные сцены посредством
затемнения всех предыдущих рисунков.
makeLayer = function
() {
delete layer.onMouseDown;
delete layer.onMouseUp;
layer = this.createEmptyMovieClip("layer"+layerNum, layerNum);
layerNum++;
layer.lineStyle(2, 0, 100);
layer.moveTo(150, 100);
layer.beginFill(0xFFFFFF, 50);
layer.lineTo(400, 100);
layer.lineTo(400, 300);
layer.lineTo(150, 300);
layer.lineTo(150, 100);
layer.endFill();
shapes = [];
shapeNum = 0;
layer.onMouseDown = startDraw;
layer.onMouseUp = stopDraw;
};
makeLayer
- это своеобразный мини-Photoshop. Каждый раз
при завершении анимационной сцены он будет
создавать новый слой поверх ваших предыдущих
сцен, чтобы можно было начать создание нового
рисунка. Каждая сцена имеет прозрачность 50%,
поэтому в итоге получается интересный эффект
"многослойности". Функция также заново
инициализирует массив
shapes (который будет содержать
координаты рисунков) и переменную
shapeNum, которая
будет содержать число фигур, изображенных на
определенном слое. Так как мы создаем новый
слой, это значение устанавливается на 0.
- Эти последние
несколько строк кода обеспечивают выполнение
всего упомянутого.
layerNum = 0;
_root.onKeyDown = listVars;
Key.addListener(_root);
makeLayer();
Здесь мы инициализируем исходные переменные и
устанавливаем главный фильм на прослушивание
события keyPress.
Метод addListener
является новой полезной командой, позволяющей
объектам принимать информацию о событиях, даже
если они не были изначально настроены на эту
возможность. Фильмы не "слышат" автоматически
события клавиш, такие как
keyDown и keyUp,
однако мы можем добавить эту функциональность
определенным инстансам с помощью
addListener. Здесь
мы настроили _root
на прослушивание всех событий, связанных с
объектом Key.
После этого последняя строка создает первый
слой, и можно приступать к работе.
- Запустите
фильм. Нарисуйте что-нибудь в анимационной сцене
(можете выходить за границы) и затем нажмите
любую клавишу для начала рисования новой сцены.
Ваш код работает так, как нужно, однако нам
необходимо настроить еще один фильм перед тем,
как мы сможем использовать их для генерирования
анимации.
- Сохраните
фильм и оставьте его открытым, так как мы к нему
сейчас вернемся.
Покадровая анимация
Это
небольшое упражнение, в котором мы будем
использовать рисунки API, генерируемые
celMaker.swf для
создания анимации, в данном случае - грубо
анимированного бегущего человечка. Мы будем
использовать рисование API для моделирования
обыкновенной покадровой анимации. Откройте
API_animation.swf (на
компакт-диске), чтобы увидеть результат работы кода.
- Откройте
новый фильм с именем API_animation.fla и введите
этот код в кадр 1 слоя по умолчанию:
shapes = [];
shapeNum = 0;
drawShapes = function () {
_root[shapes[shapeNum]]();
shapeNum = shapeNum+1>shapes.length-1 ? 0 : shapeNum+1;
};
setInterval(drawShapes, 100);
Это практически весь код, который нужно ввести в
наш новый фильм. Мы инициализируем две
переменные (и будем сохранять имена наших
функций в массиве, сразу после определения их
количества) и создаем функцию для смены
кодированных изображений.
drawShapes будет просто обрабатывать
циклически массив и переходить к следующему
изображению. Скорость, с которой будет
выполняться это действие, указывается выражением
setInterval в
следующей строке. Здесь я указал вхождение
функции через каждую десятую долу секунды.
Изменяя это число можно быстро изменять частоты
кадров вашего фильма.
Вторая строка в функции
drawShapes является примером
использования условного оператора. По большому
счету, это то же самое, что и выражение
if...else. Строка
начинается с переменной, в данном случае -
shapeNum. Мы будем
присваивать значение переменной
shapeNum в
зависимости от обстоятельств. Далее будет
проверяться условие. В нашем случае мы
проверяем, является ли
shapeNum+1 большим по значению, чем
полное число индексов в массиве
shapes. Если это
выражение принимает значение "истина", то все,
что стоит после знака "?", приписывается
переменной shapeNum
(в нашем примере - 0). Если условие возвращает
значение "ложь", тогда переменной
shapeNum (в
примере выше - shapeNum+1)
присваивается все, что стоит после двоеточия.
Вся строка тогда сводится к увеличению
shapeNum до тех
пор, пока она не достигнет конца массива shapes,
в случае чего будет осуществлен переход на
первый индекс.
- Вернитесь к
файлу celMaker.fla,
над которым вы работали в последнем упражнении,
и запустите фильм заново. Создайте несколько
кадров анимации, нажимая на любую клавишу перед
каждой новой сценой для создания новой
сцены/слоя. Мы будем анимировать эти кадры,
поэтому нарисуйте что-нибудь, что движется от
кадра к кадру. Я нарисовал в каждом кадре
бегущего человечка, который будет "бежать" при
циклическом проигрывании фильма.
Не
закрывайте окно Output при его появлении - оно
нам понадобится. При рисовании не следует
слишком долго рисовать карандашом, несмотря на
то, что движение будет минимально. Чем быстрее
вы рисуете, тем меньше кода впоследствии будет
использоваться.
Ах
уж это рисование мышью! Будь у нас время, мы бы
реализовали нечто более интересное, например,
традиционные кривые Безье и средства для
рисования фигур - окружностей и многоугольников.
- Завершив
создание анимационного цикла, нажмите любую
клавишу, чтобы вывести последнюю сцену. Окно
Output теперь будет содержать многие строки
кода, который вам нужно будет выделить и
скопировать. Все эти строки форматируются и
выводятся в функции
listVars. Убедитесь, что вы скопировали
весь код.
- Вернитесь в
главную точку временной шкалы
API_animation.fla
и вставьте содержимое буфера обмена под первой
строкой shapes = [];
кода в панели Actions. Осталось вставить имена
сцен в массив в нижней части кода. Наши сцены
названы именами shape1,
shape2 и т.д. в
коде, скопированном из окна Output. Обновите
массив соответствующим числом сцен. У меня их
было четыре, поэтому мой обновленный код
выглядит так.
shapes = [];
shape1 = function() {
this.clear();
this.lineStyle(2, 0, 100);
[Lots of additional function information]
this.lineTo(256, 269);
this.lineTo(255, 269);
this.lineTo(254, 269);
this.lineTo(253, 269);
}
shapes.push("shape4");
drawShapes = function() {
_root [shapes[shapeNum]] ();
shapeNum = shapeNum+1 > shapes.length-1 ? 0 : shapeNum+1;
}
setInterval(drawShapes, 100);
Если у вас больше сцен, добавьте нужное их
количество в массив. Убедитесь, что вы вводите в
массив лишь строки, а не сами функции.
- Запустите
фильм, чтобы посмотреть покадровую анимацию в
действии.
Программирование
промежуточных кадров
Мы
наблюдаем результат программирования покадровой
анимации с использованием рисования API (хотя в
нашем примере используется очень примитивный
рисунок). Так как насчет промежуточных кадров? При
работе исключительно с числами это проще простого.
В
следующем упражнении я уже написал за вас основную
часть, поэтому мы сможем сразу перейти к созданию
промежуточных сцен. Я уже демонстрировал один из
методов генерирования чисел в одном из упражнений и
здесь применил очень похожий процесс для генерации
чисел для кривых, с которыми мы будем работать.
Способы получения такой информации вы можете изучить
сами.
Превращения фрукта
В этом
упражнении мы создадим промежуточные сцены для
движения и цвета между двумя рисунками API. Откройте
apples_oranges.swf (на
компакт-диске) и щелкните на фрукте. Он будет
превращаться из яблока в апельсин при каждом щелчке
пользователя. Не впечатляет? Но, между прочим, этот
объект был создан полностью с помощью рисования API,
без рисунков на рабочем столе или в Library!
Для
начала создадим законченные рисунки.
- Создайте
новый фильм с именем
apple_oranges.fla. В этом фильме мы будем
работать только с кодом, поэтому понадобится
только панель Actions. Начните с добавления
следующего кода в первый кадр слоя по умолчанию.
this.createEmptyMovieClip("drawObj", 0);
apple = {};
apple.anchorPoints = [[245,155], [180,50],
[245,155], [247,70], [265,60], [262,150],
К[335,130], [354,220], [256,320], [163,263],
[161,158], [262,150]];
apple.controlPoints = [[250,50], [150,100],
[270,115], [255,70], [290,110], [290,109],
К[373,153], [314,358], [211,357], [128,196],
[200,118]];
apple.colors = [[0,0x009900], [2,0x663300],
[5,0x990000]];
orange = {};
orange.anchorPoints = [[232,152], [192,120],
[227,153], [307,103], [227,154],
К[210,154], [260,148], [350,150], [380,338],
[140,364], [133,175], [210,154]];
orange.controlPoints = [[212,58], [130,123],
[246,102], [276,154], [200,104],
К[230,124], [285,125], [450,204], [288,480],
[38,263], [178,140]];
orange.colors = [[0,0x333300], [2,0x335500],
[5,0xFF9933]];
shapes = [apple, orange];
shapeNum = 0;
Пример
9.1.
В
первой строке мы создаем фильм, который будет
содержать наши рисунки. Затем создаются два
новых объекта, названные "apple" и "orange",
т.е. яблоко и апельсин. Оба они имеют точки
контроля, фиксированные точки и присвоенные
цвета (все числа были сгенерированы другим SWF).
Для цветов мы сохраняем цвета и позиции индексов
фиксированных точек, на которых начинается цвет.
Затем мы располагаем эти два объекта в массиве с
именем shapes и устанавливаем текущую позицию
индекса на 0 для яблока с помощью
shapeNum.
Теперь мы создаем функцию, носящую
соответствующее название
drawShape и рисующую фигуры.
drawShape = function
(shape) {
drawObj.clear();
drawObj.lineStyle(2, 0, 100);
currentColor = 0;
drawObj.moveTo(shape.anchorPoints[0] [0], s
hape.anchorPoints[0] [1];
for (var i = 0; i<shape.anchorPoints.length-1; i++) {
if (shape.colors[currentColor] [0] == i) {
drawObj.endFill() ;
drawObj.beginFill(shape.colors[currentColor] [1]
, 100);
drawObj.moveTo(shape.anchorPoints[i] [0],
shape, anchor Points [i] [1] ) ;
currentColor++;
}
drawObj.curveTo(shape.controlPoints [i] [0],
shape.controlPoints [i] [1],
Кshape.anchorPoints [i+1] [0], shape.anchorPoints [i+1] [1]);
}
};
Большая часть этого кода совершенно ясна, если
вы разбираетесь в массивах, поэтому разберемся
досконально, как работают ссылки. Переменная
shape будет
содержать ссылку на один из наших объектов -
яблоко или апельсин. Следовательно,
shape.anchorPoints
будет ссылаться на параметр
anchorPoints
выделенной фигуры.
anchorPoints для каждого объекта содержит
двумерный массив. Каждая позиция индекса
anchorPoints
содержит координаты _x
и _y: значение
_x содержится в
индексе 0, а значение _y
содержится в индексе 1. Следовательно, если
shape содержит наш
объект apple, то
shape.anchorPoints[1] [1]
будет ссылаться на координату
_y второго индекса
массива anchorPoints
объекта apple,
которым является 50.
В
функции drawShape
мы сначала удаляем фильм, сбрасываем переменные
lineStyle и
currentColor и
переходим на нашу начальную позицию. Мы проходим
через весь массив
anchorPoints для нашего текущего объекта
и вызываем метод curveTo
для каждого из них. Выражение
if проверяет,
находимся ли мы на позиции индекса, где должен
начаться наш следующий цвет. Если это так, мы
заканчиваем предыдущую заливку и начинаем новую.
Я выяснил, что перемещение "карандаша" на
текущую позицию после вызова
beginFill
устранило некоторые ошибки заполнения, имевшиеся
при отсутствии этой команды.
- Теперь
введите последний фрагмент кода.
clickCatch = function
() {
shapeNum = shapeNum+1>1 ? 0 : shapeNum+1;
drawShape(shapes[shapeNum]);
};
drawShape(apple);
drawObj.onMouseDown = clickCatch;
При щелчке мышью будет вызываться функция
clickCatch. Она
увеличивает shapeNum
(мы используем здесь условный оператор на тот
случай, если нам понадобится добавить
дополнительные фигуры впоследствии) и вызывает
нашу функцию drawShape.
После этого рисуется начальная фигура, и
drawObj
настраивается на регистрацию щелчков мыши для
изменения рисунка. Запустите фильм, чтобы
отобразить яблоко, затем щелкните мышью, и
появится апельсин. Все это реализовано с помощью
вышеуказанного кода.
Мы
нарисовали фрукты с помощью рисования API.
Хорошо было бы анимировать это изменение.
Попробуем реализовать такую возможность.
- Добавьте эти
три строки в верхнюю часть вашего текущего кода,
сразу после вызова
createEmptyMovieClip.
tweenRate = 24;
currentPos = {};
currentPos.controlPoints = [];
currentPos.anchorPoints = [];
currentPos.colors = [];
Здесь мы просто инициализируем переменные.
tweenRate является
длиной нашей анимации, связанной с частотой
кадров. currentPos
будет содержать всю информацию о том, на какую
сцену в данный момент указывают наши
фиксированные точки, точки контроля и цвета.
- Еще одна
функция представлена на ваше утверждение.
Введите этот код сразу под функцией
drawShape.
convertToRGB =
function(c) {
var col = c.toString(16);
while (col.length < 6) {
col = "0" + col;
}
var r = parseInt(col.substr(0,2), 16);
var g = parseInt(col.substr(2,2), 16);
var b = parseInt(col.substr(4,2), 16);
return {r:r, g:g, b:b}
}
Функция convertToRGB
выполняет преобразование в цветовую систему RGB.
Она будет получать значение цвета и возвращать
значение для каждого из трех цветов - красного,
зеленого и синего. Эти значения понадобятся нам
для плавного изменения цветов объектов.
c - это
значение цвета, получаемое функцией. Мы
преобразовываем его в строку с помощью метода
toString, с
использованием шестнадцатеричной системы.
Выражение while
нужно для проверки того, что в строке содержится
шесть символов. Например, если мы отправляем
функции значение 255 (чистый синий), строкой,
возвращенной
c.toString(16) будет FF. Нам необходимо,
чтобы строка имела вид 0000FF для
шестнадцатеричного представления цвета, поэтому
в выражении while
мы добавляем необходимые нули.
Далее, мы извлекаем три подстроки из
c. r будет
содержать значение красного цвета, хранимое в
первых двух символах строки, g будет содержать
значение зеленого цвета в третьем и четвертом
символах строки, а b
будет содержать последние два символа,
представляющие собой значение для синего цвета.
parseInt преобразует строку в число, поэтому мы
отправляем функции наши подстроки, а также
основание системы исчисления (16). Эти конечные
значения возвращаются в отдельном объекте,
содержащем три параметра:
r, g и
b.
- Следующая
функция является местом инициализации
преобразования. Введите этот код сразу после
функции convertToRGB.
startShift = function
(shape) {
endShape = shape;
delete drawObj.onMouseDown;
tweenCount = 0;
increment = {};
increment.anchorPoints = [];
increment.controlPoints = [];
increment. colors = [];
for (var i = 0; i<shape.colors.length; i++) {
var newColor = convertToRGB(shape.colors[i] [1] );
var oldColor = convertToRGB(currentPos.colors[i] [1] ) ;
var r = (oldColor.r - newColor.r)/tweenRate;
var g = (oldColor.g - newColor.g)/tweenRate;
var b = (oldColor.b - newColor.b)/tweenRate;
increment.colors.push({r:r, g: g, b:b});
}
for (var i = 0; i < shape.anchorPoints.length; i++) {
var anchor1 = (shape.anchorPoints[i] [0] -
КcurrentPos.anchorPoints [i] [0])/tweenRate;
var anchor2 = (shape.anchorPoints[i] [1] -
КcurrentPos .anchorPoints [i] [1])/tweenRate;
var controll = (shape.controlPoints[i] [0] -
КcurrentPos.controlPoints[i] [0])/tweenRate;
var control2 = (shape.controlPoints[i] [1] -
КcurrentPos.controlPoints [i] [1])/tweenRate;
increment.anchorPoints.push([anchor1, anchor2]);
increment.controlPoints.push([control1, control2]);
}
drawObj.onEnterFrame = moveLines;
};
Пример
9.2.
Подготовительные действия завершены. Собственно
вся подготовка и заключается в этой функции.
startShift
отсылается при любой конечной фигуре, а это
значение хранится в
endShape. После этого удаляется
mouseDown, чтобы
пользователь не мог щелкать мышью во время
трансформации (не беспокойтесь, позднее мы
вернем все на свои места).
Далее: increment
необходим для хранения значений, которые
понадобятся нам для настройки текущей фигуры в
каждом кадре. Мы проходим через все цвета, точки
контроля и фиксированные точки
currentPos (сейчас
мы опишем currentPos),
находим разность между этими значениями и
конечными значениями, затем делим эту разность
на tweenRate. Это
даст нам значение increment, нужное для
настройки currentPos
в каждом кадре для преобразования одной фигуры в
другую. Эти значения помещаются в
соответствующие массивы объекта
increment.
Наконец, нужно настроить наш объект на вызов в
каждом кадре функции
moveLines. Разумеется, нам понадобится
написать эту функцию.
- Введите этот
код сразу после предыдущей функции.
moveLines = function
() {
for (var i = 0; i<endShape.anchorPoints.length; i++) {
currentPos.controlPoints[i] [0] +=
increment.controlPoints [i] [0];
currentPos.controlPoints[i] [1] +=
increment.controlPoints [i] [1] ;
currentPos.anchorPoints[i] [0] +=
increment.anchorPoints[i] [0];
currentPos.anchorPoints[i] [1] +=
increment.anchorPoints[i] [1];
}
for (var i = 0; i < endShape.colors.length; i++) {
var col = convertToRGB(currentPos.colors[i] [1]);
col.r = Math.round(col.r-increment.colors[i] .r);
col.g = Math.round(col.g-increment.colors[i] .g);
col.b = Math.round(col.b-increment.colors[i] .b);
currentPos.colors [i] [1] = col.r << 16 | col.g << 8 | col.b;
}
drawShape(currentPos);
tweenCount++;
if (tweenCount>tweenRate) {
delete drawObj.onEnterFrame;
drawobj.onMouseDown = clickCatch;
drawShape(endShape);
}
};
Эта функция непосредственно обеспечивает
трансформацию, хотя она и не настолько сложна,
чтобы обсуждать ее. Она просто настраивает числа
в currentPos в
каждом кадре c использованием соответствующих
значений increment.
После этого она вызывает
drawShape (функция рисования, написанная
нами в самом начале кода) и проверяет, является
ли данный кадр последним кадром трансформации.
Если это так, она сбрасывает все значения,
останавливает анимацию и подготавливается к
следующему щелчку мыши.
- Мы почти
закончили. Настроим нашу функцию
drawShape для
работы с новым объектом
currentPos. Обновите функцию
drawShape
следующими новыми строками (выделены жирным
шрифтом).
drawShape = function
(shape) {
drawObj.clear();
drawObj.lineStyle(2, 0, 100);
currentColor = 0;
drawObj.moveTo(shape.anchorPoints[0] [0] ,
shape.anchorPoints [0] [1]);
for (var i = 0; i<shape.anchorPoints.length-1; i++) {
if (shape.colors[currentColor] [0] == i) {
drawObj.endFill();
drawObj.beginFill(shape.colors[currentColor] [1],
100);
currentPos.colors[currentColor] =
К[i, shape.colors[currentColor][1]];
drawObj.moveTo(shape.anchorPoints[i] [0],
shape.anchorPoints[i] [1] ) ;
currentColor++;
}
drawObj.curveTo(shape.controlPoints[i] [0],
Кshape.controlPoints [i] [1], shape.anchorPoints [i+1] [0],
Кshape.anchorPoints[i+1] [1]);
currentPos.controlPoints[i] = [shape.controlPoints[i] [0],
Кshape.controlPoints[i] [1]];
currentPos.anchorPoints[i] = [shape.anchorPoints[i][0],
Кshape.anchorPoints [i] [1]];
}
currentPos.anchorPoints[i] = [shape.anchorPoints[i][0],
Кshape.anchorPoints[i][1]];
};
Эти дополнительные строки сохраняют номера
текущих позиций точек, чтобы можно было
использовать их в следующем кадре нашей функции
moveLines.
Проследите за превращением и вы увидите, как эти
значения влияют на фильм.
- Наконец,
измените функцию
clickCatch так, чтобы при щелчке
пользователем начиналась трансформация. Это
реализуется вызовом функции
startShift.
clickCatch = function
() {
shapeNum = shapeNum+1>1 ? 0 : shapeNum+1;
startShift(shapes[shapeNum]);
};
- Теперь
запустите фильм, чтобы увидеть превращение
одного фрукта в другой. Здорово!
Градиенты
До сих
пор мы анимировали прямые линии, кривые и заливку.
Теперь изучим последнюю составляющую рисования API -
градиенты. С помощью градиентов можно реализовать
достаточно сложные графические объекты, которые
невозможно было получить во Flash 5. На рисунке ниже
представлен один из таких реализованных с помощью
рисования API эффектов:
Для
создания градиентов существуют простой и сложный
способы. Синтаксис для обоих способов следующий.
mcInstance.beginGradientFill (fillType, colors,
alphas, ratios, matrix);
Эта
строка может показаться трудной даже для ввода.
Создадим двухцветный градиент, чтобы понять сам
принцип действия (упражнение
linear_gradients.fla на компакт-диске).
Линейные градиенты
- Создайте
новый фильм с именем
linear_gradients.fla. В кадре 1 слоя по
умолчанию введите следующий код для рисования
квадрата.
this.createEmptyMovieClip("square", 0);
square._x = 270;
square._y = 200;
square.moveTo(-100, -100);
square.lineStyle(2, 0x333333, 100);
square.lineTo(100, -100);
square.lineTo(100, 100);
square.lineTo(-100, 100);
square.lineTo(-100, -100);
Этот код довольно прост. Здесь создается фильм с
именем square,
который будет содержать рисунок; он
располагается по середине нашего фильма по
умолчанию (270, 200). После этого с помощью
lineTo рисуется
закрашенная фигура. Теперь разберемся с цветом!
- Перед кодом
выше введите следующее.
colors =
[0x0000FF, 0xF4620B];
alphas = [100, 100];
ratios = [0, 255];
matrix = {matrixType:"box",
x:-100, y:0, w:200, h:200, r:0};
Выглядит не очень просто. Однако, рассмотрев по
частям данный код, вы увидите, что он не
представляет особой сложности. Сначала
определяется массив цветов
colors. В данном
случае это синий и оранжевый цвета (если вы
хотите использовать другие цвета, просто
добавьте дополнительные индикаторы в эти
массивы). В следующей строке мы определяем
массив значений интенсивности для каждого цвета
в градиенте. Затем, с использованием массива
ratios, указываются точки в градиенте, в которых
каждый цвет принимает свое максимальное
значение. Эти значения являются
шестнадцатеричными процентными величинами от
ширины градиента. Мы указали, что синий цвет
будет иметь свое максимальное значение в начале
градиента (0% ширины градиента) и что оранжевый
цвет будет максимально чистым в конце градиента
(255, или 100% ширины градиента). Это делается
для того, чтобы без особых знаний математики
можно было вводить эти значения в виде
десятичных процентов, однако мы имеем дело с
шестнадцатеричными значениями. Такова жизнь!
Последняя строка инициализации нашей переменной
устанавливает matrix.
Это новый объект, содержащий шесть параметров.
Первый - matrixType
- будет всегда иметь значение
box. В дальнейшем
будет важно отсутствие
matrixType. Если этот параметр существует
(в случае с "простым" способом реализации
градиента), его значением всегда будет
box. Далее мы
указываем координаты x
и y начала нашего
градиента. Я расположил градиент таким образом,
что его стартовой позицией оказались координаты
(-100, 0) в фильме square,
что является левой стороной нашего нарисованного
квадрата, а w и
h - это,
соответственно, ширина и высота градиента. Так
как наш квадрат имеет размер 200х200 пикселей, я
установил разрешение градиента, равное размеру
квадрата.
Наконец, я установил поворот (r) градиента на
значение 0 радиан. Если вы укажете поворот,
имейте в виду, что это необходимо делать в
радианах. Поэтому, если вам нужно, чтобы
градиент имел наклон 45°, вам необходимо
провести преобразование градусов в радианы
(радианы = градусы*р/180).
Установив все эти переменные, добавим две
последние строки кода, которые будут заполнять
наш квадрат линейным градиентом.
>
- Введите в код
следующие две строки.
colors = [0x0000FF,
0xF4620B];
alphas = [100, 100];
ratios = [0, 255];
matrix = {matrixType:"box", x:-100, y:0,
w:200, h:200, r:0};
this.createEmptyMovieClip("square", 0);
square._x = 270;
square._y = 200;
square.moveTo(-100, -100);
square.lineStyle(2, 0x333333, 100);
square.beginGradientFill("linear", colors,
alphas, ratios, matrix);
square.lineTo(100, -100);
square.lineTo(100, 100);
square.lineTo(-100, 100);
square.lineTo(-100, -100);
square.endFill();
- Запустите
фильм.
- Это "простой"
вариант градиента. Попробуйте ввести другие
значения в переменные, чтобы посмотреть, как
изменения повлияют на результат. Ниже приведены
несколько примеров различных линейных
градиентов, которые вы можете получить.
Проделайте это перед тем, как рассматривать
более сложные типы градиентов - радиальные
градиенты (код этих градиентов содержится в виде
комментариев в файле
linear_gradients.fla на компакт-диске).
Радиальные градиенты
При
создании радиальных градиентов единственное, что
нужно изменить - тип градиента "линейный" на тип
"радиальный". Вот и все. Вам также понадобится
учесть, что параметры x
и y матрицы не
соответствуют центру радиального градиента. Поэтому,
чтобы расположить центр градиента в центре фигуры,
вам надо будет выполнить некоторые простые
математические действия. Мы создадим
трансформирующийся градиент, чтобы
продемонстрировать это (обратитесь к файлу
radial_gradients.fla
на компакт-диске). Это градиент перетаскиваемый, так
как он динамически создает светлое "пятно" вокруг
движущегося указателя мыши.
- Создайте
новый фильм с именем
radial_gradients.fla. Откройте панель
Actions и добавьте следующий код в кадр 1 слоя
по умолчанию.
this.createEmptyMovieClip("drawObj", 0);
drawObj._x = 270;
drawObj._y = 200;
drawObj.startSquare = {x:-100, y:-100};
drawObj.squareDim = {w:200, h:200};
Первые три строки создают и устанавливают
координаты фильма. Следующие две строки создают
два отдельных объекта в качестве параметров
нашего нового фильма
drawObj. Они содержат начальные позиции
x и
y нарисованного
квадрата, а также его ширину и высоту. Для
создания градиента нам понадобятся все четыре
значения.
- Добавьте эти
строки под предыдущим кодом.
spotlight = {};
spotlight.colors = [0xFFG0EC, 0x6F0061];
spotlight.alphas = [100, 100];
spotlight.ratios = [0, 255];
spotlight.matrix = {matrixType:"box",
x:drawObj.startSquare.x,
Кy:drawObj.startSquare.у,
w:drawObj.squareDim.w,
Кh:drawObj. squareDim. h,
Кr:0};
spotlight.setMatrix = function() {
this.matrix.x = _root._xmouse+drawObj.startSquare.x-drawObj._x;
this.matrix.у = _root._ymouse+drawObj.startSquare.y-drawObj._y;
};
Здесь мы создаем отдельный объект для хранения
параметров нашего градиента. Это не обязательно,
но помогает отделить типы градиентов друг от
друга при использовании нескольких заливок в
рисунке.
Установка параметров здесь совершенно ясна, так
как она не отличается от кода в предыдущем
примере с линейным градиентом (за исключением
того, что мы используем переменные для установки
значений). После этого добавляется метод,
который будет устанавливать центр нашего
градиента на указатель мыши. Мы берем позицию
мыши, вычитаем координаты фильма
drawObj и
прибавляем начальную позицию нарисованного
квадрата. Начальная позиция градиента сместится,
и его центр окажется в точности под указателем
мыши.
- Продолжим
создание градиента, добавив следующий код под
предшествующим кодом.
drawSquare = function
() {
var right = drawObj.squareDim.w/2;
var left = -right;
var bottom = drawObj.squareDim.h/2;
var top = -bottom;
drawObj.clear();
spotlight.setMatrix();
drawObj.moveTo(left, top);
drawObj.lineStyle(2, 0x333333, 100);
drawObj.beginGradientFill("radial", spotlight.colors,
Кspotlight.alphas, spotlight.ratios, spotlight.matrix);
drawObj.lineTo(right, top);
drawObj.lineTo(right, bottom);
drawObj.lineTo(left, bottom);
drawObj.endFill();
updateAfterEvent();
};
Здесь представлена наша функция, которая будет
перерисовывать квадрат в каждом кадре. Сначала
мы удаляем нарисованный квадрат (несмотря на то,
что рисование, казалось бы, происходит поверх
предыдущего рисунка, это делать необходимо).
Затем вызываем метод
setMatrix объекта
spotlight, созданный в предыдущем шаге.
Он будет изменять значения матрицы перед
перерисовыванием квадрата. Остаток кода - тот же
код рисования, который мы использовали ранее.
Единственным отличием является то, что значения
устанавливаются прежними переменными. Мы также
добавляем
updateAfterEvent для обновления экрана
каждый раз при движении мыши, и этот код
выполняется заново для предотвращения эффекта
мигания.
- Наконец,
добавим код, который будет перерисовывать
квадрат при каждом движении мыши. Введите эти
выражения в конце всего имеющегося кода.
drawSquare();
drawObj.onMouseMove = drawSquare;
Сначала вызывается функция
drawSquare в
начале фильма, затем мы присваиваем функцию
событию mouseMove
объекта drawObj.
Запустите фильм, чтобы проверить это в действии.
Имейте в виду, что это будет работать для
градиента, который заполняет всю ширину
квадрата. При изменении ширины (если ширина
исказит градиент, она не будет равна высоте) вам
также понадобится поработать с формулами
setMatrix.
Итак, один из примеров готов. Как насчет
создания чего-то такого, что может пригодиться в
будущих приложениях?
Динамическая цветовая
палитра
Здесь
мы создадим цветовую палитру градиента, которая
позволит создавать и изменять градиенты.
Поэкспериментируйте с готовой палитрой (gradient_palette.swf
на компакт-диске), чтобы иметь представление о том,
что мы будем создавать. Настройте параметры, изменяя
радиальный градиент на линейный, и попробуйте
добавлять, удалять и изменять цвета в области
цветов. Во время работы, учтите, что все, за
исключением компонента ComboBox в правом верхнем
углу, создано динамически с помощью рисования API.
А
теперь - кое-что интересное! Это упражнение довольно
большое, и здесь используются некоторые более
сложные концепции программирования, однако оно того
стоит. Если вы хотите сравнивать ваш файл с примером
во время работы, вы можете в любой момент обращаться
к файлу gradient_palette.fla
на компакт-диске. Итак, приступим.
- Создайте
новый фильм. Откройте панель Components, если
она не открыта (Ctrl + F7) и перетащите копию
компонента ComboBox
на рабочее место. Теперь удалите ее. Компонент и
все его ресурсы теперь сохранены в Library, как
нам и нужно. Мы будем добавлять
ComboBox на
рабочее место динамически в этом упражнении, и
это будет единственным элементом нашей палитры,
созданным заранее.
- Создайте
символ нового фильма (Ctrl+F8) с именем
colorStopSymbol.
Убедитесь, что при этом отображаются параметры
Advanced, и
выберите опцию Export for Actionscript. Flash
автоматически вставит такой же
Identifier, как и
имя символа. Нажмите OK, чтобы принять эти
параметры.
- Теперь
выйдите из режима Symbol Editing для возврата в
главную точку временной шкалы - нам не нужно
ничего добавлять в наш символ, так как это все
будет сделано с помощью ActionScript. Нам просто
нужен фильм в Library, который можно расширить.
- Переименуйте
слой по умолчанию в главной точке временной
шкалы, присвоив ему имя
actions. Откройте панель Actions и
начните с добавления следующего кода в кадр 1.
paletteWidth = 400;
paletteHeight = 300;
gp =
this.createEmptyMovieClip("gradientPicker",
0);
gp._x = (Stage.width-paletteWidth)/2;
gp._y = (Stage.height-paletteHeight)/2;
gp.lineStyle(5, 0, 100);
gp.beginFill (0x777777, 100);
gp.lineTo(paletteWidth, 0);
gp.lineTo(paletteWidth, paletteHeight);
gp.lineTo(0, paletteHeight);
gp.endFill();
Сначала мы устанавливаем высоту и ширину нашей
палитры цветов градиента, создаем фильм, который
будет содержать палитру, затем располагаем фильм
по центру рабочего места посредством принятия
параметров объекта Stage.
Затем используем рисование API для рисования
серого поля, которое будет фоном палитры. Все
это должно быть вам знакомо.
- Введите этот
код под предыдущими строками.
gradTypes = ["linear",
"radial"]; gradNum = 0;
sorter = function (a, b) {
return a[0]>b [ 0];
};
setGrad = function () {
colors = [];
alphas = [];
ratios = [];
stops = [];
for (var i = 0; i<well.colorStops.length; i++) {
stops.push([well.colorStops[i] ._x, well.colorStops[i]]);
}
stops.sort(sorter);
for (var i = 0; i<stops.length; i++) {
colors.push(stops[i] [1].stopColor);
alphas.push(100);
ratios.push(stops[i] [1].getValue()*255);
}
sample.drawGrad();
well.drawGrad();
};
Этот код может показаться немного странным, так
как мы ссылаемся здесь на объекты, которые еще
не были созданы. Потерпите, сейчас мы во всем
разберемся. Это начало фрагмента кода, в котором
происходит взаимодействие с оттенками цветов. Их
в нашем фильме будет три.
- Gradient
sample (sample) в левом верхнем углу для
предварительного просмотра результата работы
градиента.
- Gradient
well (well) - область в центре палитры -
будет содержать все цвета, с помощью которых
мы комбинируем и определяем наш градиент.
- Solid
color swatch (swatch) в правом нижнем углу
палитры, будет отображать цвет текущего
выбранного оттенка.
В
коде выше мы сначала устанавливаем два типа
градиента (линейный или радиальный) в виде строк
в массиве gradTypes.
Мы сделаем их доступными при установке градиента
для образца градиента.
dragNum просто содержит текущий индекс
массива gradTypes.
Функция sorter
будет сортировать наш двумерный массив. Мы к ней
еще вернемся.
Функция setGrad
выполняется для всех
colorStops (небольшие треугольники под
областью цвета на рисунке в начале этого
упражнения) и создает значения градиентов на
основании их цветов и позиций
_x. Она
инициализирует все массивы (colors, alphas и
ratios), предназначения каждого из которых вы
еще узнаете. Массив stops
будет содержать позиции
_x всех фильмов
colorStop, а также ссылки на сами фильмы.
Первый цикл for
затем выполняется для каждого
colorStop (ссылки
на каждый из них находятся в массиве
colorStops
цветовой области well,
который мы сейчас будем устанавливать) и
располагает позицию _x
объекта colorStop
и сам объект в этом двумерном массиве
stops. После
настройки массива мы сортируем его содержимое с
помощью функции sorter.
Этот массив необходимо сортировать, так как
нужно знать порядок
colorStops по их текущей позиции
_x. Так как
порядок позиций _x
не будет являться порядком создания (при каждом
щелчке пользователя могут добавляться
дополнительные colorStops,
и они, разумеется, могут быть перемещены в любое
место цветовой области), нам нужно сортировать
colorStops в
порядке возрастания с наименьшей позиции
_x к наибольшей.
Следовательно, мы можем получить их значения для
нашего градиента. Для этого мы воспользуемся
методом Array.sort
и функцией sorter.
Метод Array.sort
принимает метод сравнения, с помощью которого
осуществляется сортировка. Самый простой из них
(и, как оказалось, наиболее полезный) тот,
который будет сортировать числа в порядке
значения.
sorter = function(a,
b) {
return a > b
}
Этот псевдокод будет сортировать числа в порядке
возрастания (заменив символ "больше, чем" (>) на
символ "меньше, чем" (<), вы реализуете
сортировку в порядке убывания).
a и
b представляют
собой два смежных значения в нашем массиве.
Flash будет полностью проходить через нужный
массив и "перемешивать" все значения до тех пор,
пока они не будут отсортированы. Для сортировки
массива с несколькими измерениями нужно просто
указать в методе sorter,
по какому индексу необходимо осуществлять
сортировку. В нашем упражнении с палитрой мы
использовали первый индекс массива, который
является параметром _x
объекта colorStop.
Это обеспечивает успешную сортировку
colorStops в
порядке слева направо.
После сортировки
colorStops мы проходим через наш
отсортированный массив и вставляем значения в
массивы градиентов по порядку
colorStops. Каждый
colorStop имеет
параметр stopColor,
который мы помещаем в
colors. Каждый цвет будет находиться в
своей максимальной яркости (впоследствии вы
сможете это настроить), поэтому в массив
alphas просто
добавляем значение 100.
Наконец, мы вызываем метод
getValue массива
colorStops,
который будет возвращать процентное значение
того места, где объект
colorStop занимает всю цветовую область.
Мы умножаем это значение на 255 для получения
степени (запомните, степени варьируются от 0 до
255).
Заполнив массивы, обеспечим автоматическое
заполнение области просмотра и цветовой области.
- Создаем
методы, которые будут непосредственно заполнять
поля градиента цветами.
this.w и this.h
являются параметрами каждого из фильмов
sample,
swatch и
well (мы будем
присваивать эти значения в конце при
непосредственном создании фильмов), которые
содержат высоту и ширину фильмов.
drawGrad = function ()
{
this.clear();
this.lineStyle(2, 0, 100);
if (this == well) {
type = "linear";
} else {
type = gradTypes[gradNum];
}
this.beginGradientFill(type, colors, alphas, ratios, this .matrix);
this.moveTo(0, 0);
this.lineTo(this.w, 0);
this.lineTo(this.w, this.h);
this.lineTo(0, this.h);
this.endFill();
};
drawSolid = function () {
this.clear();
this.lineStyle(2, 0, 100);
this.beginFill(getSliderValues(), 100);
this.moveTo(0, 0);
this.lineTo(this.w, 0);
this.lineTo(this.w, this.h);
this.lineTo(0, this.h);
this.endFill();
};
drawGrad
и drawSolid
создают закрашенные квадраты, отличаясь друг от
друга типом заливки. drawGrad обрабатывает два
поля градиента (well
и sample), а
drawSolid
предназначен для поддержки
swatch. Здесь
необходимо обратить внимание на выражение
if в
drawGrad, которое
обеспечивает установку
gradientType для
sample (well
всегда является линейным), а также на функцию
getSliderValues в
drawSolid. Эта
функция, которую мы напишем, когда дойдем до
ползунков цветов, будет возвращать
шестнадцатеричное значение в соответствии с
текущей позицией ползунка цвета.
- Добавьте эти
строки для завершения секции цветовых образцов.
colorSample = function
() {
var w = sample.w*wSlider.getValue() +1;
var h = sample.h*hSlider.getValue()+1;
var x = (sample.w-w)/2;
var у = (sample.h-h)/2;
sample.matrix = {matrixType: "box", x:x, y:y, w:w, h:h,
Кr:rotSlider.getValue()*Math. PI};
sample.drawGrad();
};
colorSwatch = function () {
swatch.drawSolid();
well.selected.stopColor = getSliderValues();
setGrad();
};
changeGrad = function () {
gradNum = gradNum+1>1 ? 0 : gradNum+1;
sample.drawGrad();
};
Первая функция в этом фрагменте присваивает цвет
образцу градиента sample.
Ширина и высота градиента определяются в
зависимости от позиций
wSlider и hSlider
соответственно. Метод ползунка
getValue
возвращает процентное значение, которое затем
умножается на полную ширину и высоту
swatch (мы
прибавляем единицу к этим значениям, чтобы
избежать несоответствия результатов с
градиентом, если его ширина или высота меньше
единицы). Начальные точки x и y градиента
настраиваются в зависимости от этих значений
(таким образом, градиент остается
отцентрированным в образце и не смещается).
Затем мы можем заполнить нашу матрицу этими
значениями, вместе со значением поворота,
которым будет являться величина между 0 и р, и
вызвать метод drawGrad
объекта sample.
colorSwatch
заполняет цветом нашу цветовую область,
вызываемую каждый раз при перемещении ползунка
цвета. Сначала мы рисуем сам текущий образец с
помощью drawSolid.
После этого устанавливаем выбранный в данный
момент цвет colorStop
на значения наших ползунков цветов. Настраиваем
градиент соответствующим образом с помощью
setGrad, и после
этого заново раскрашиваем образцы градиента.
changeGrad
является самым простым методом. Он просто
переключает текущий тип градиента между
индексами 0 и 1, затем для соответствия
перерисовывает sam.
Этот метод вызывается каждый раз при щелчке на
поле ComboBox.
Мы
выполнили одну треть упражнения!
- Настало время
написать код colorStop,
который будет реализовывать маленькие
треугольные фильмы под цветовой областью (мы
будем использовать
ColorStop с заглавной буквой, когда речь
будет идти о Class,
и colorStop при
описании инстансов Class).
Для такого простого рисунка это достаточно
большое количество кода, однако с его помощью
реализуется не одна функциональность. Мы
создадим новый класс фильмов, который будет
поддерживать все исходные параметры и методы
фильма, а также параметры и методы
ColorStop. Введите
следующее под имеющимся кодом.
ColorStop = function
() {
this.init();
};
Colorstop.prototype = new MovieClip();
ColorStop.prototype.init = function() {
this._parent.stopDepth++;
this._parent.colorStops.push(this);
this.onPress = this.dragMe;
this.onRelease = this.onReleaseOutside = this.releaseMe;
this.selectMe();
setGrad ();
};
Это начало определения нашего класса, оно похоже
на определение любой другой функции. При вызове
этой функции, мы будем вызывать другую функцию с
именем init. init
является функцией (или методом), принадлежащей
только самому классу
ColorStop. Если вы посмотрите на метод
init, вы увидите,
что мы увеличиваем переменную глубины, чтобы
отвести место для следующего
colorStop и
вставляем текущий
colorStop (только что созданный) в наш
массив. После этого мы присваиваем функции
управляющим элементам событий
onPress,
onRelease и
onReleaseOutside
объекта colorStop.
Эти функции (dragMe
и releaseMe)
являются методами, которые мы сейчас создадим.
Мы
добавили этот метод init
для параметра prototype
нашего объекта ColorStop.
prototype является параметром объекта,
содержащим все остальные методы и параметры
этого объекта. Применяя метод init к параметру
prototype объекта
ColorStop, мы
обеспечиваем использование этого метода всеми
инстансами colorStop
без необходимости создания его дубликата.
Представьте себе, что нужно создавать дубликат
функции для каждого инстанса! С использованием
prototype этого делать не нужно, так как каждый
инстанс будет иметь ссылку на него в этом
параметре.
Ниже приведена последняя строка в вышеуказанном
коде, которую необходимо адресовать.
ColorStop.prototype =
new MovieClip ();
Она устанавливает наследование объекта
ColorStop.
Посредством установки параметра
prototype на новый
инстанс MovieClip Class
(именно с заглавной буквы),
ColorStop Class
унаследует все параметры и методы
MovieClip Class.
- Сразу под
предыдущим кодом введите следующее.
ColorStop.prototype.dragMe = function() {
this.startDrag(0, 0, this._y, this._parent._width-well._x, this._y);
this.selectMe();
this.onMouseMove = function() {
setGrad();
if (this._ymouse>25) {
this._parent.selected = null;
for (var i = 0; i<this._parent.colorStops.length;
i++){
if (this._parent.colorStops[i] ==
this) {
this._parent.colorStops.splice(i, 1);
this.removeMovieClip();
setGrad();
break;
}
}
}
};
};
ColorStop.prototype.releaseMe = function() {
stopDrag();
delete this.onMouseMove;
};
dragMe
содержит много вложенных уровней, однако эта
функция довольно проста. Она обеспечивает
горизонтальное перетаскивание
colorStop и
вызывает его метод
selectMe (который еще будет создан).
После этого устанавливается новая функция с
именем onMouseMove,
которая заново настраивает градиент в
зависимости от движения
colorStop. Наконец, она проверяет,
перетащил ли пользователь
colorStop вниз от области цвета. Если это
так, colorStop
удаляется из массива
colorStops и с рабочего места.
С
помощью функции releaseMe,
которая вызывается при возникновении событий
colorStop onRelease
и onReleaseOutside,
перетаскивание фильма прекращается, а также
удаляется функция
onMouseMove, созданная в вышеописанном
методе.
- Ниже
приведена довольно простая функция. Введите ее
код под уже имеющимся кодом.
ColorStop.prototype.drawMe = function(col) {
this.clear();
this.beginFill(col, 100);
this.lineTo(7, 15);
this.lineTo(-7, 15);
this.endFill();
};
Метод drawMe
рисует треугольник и заполняет его цветом,
указанным в вызове функции. Имейте в виду, что
для рисования треугольника нам необходимы только
начальная позиция ((0,0), так как это начальная
позиция по умолчанию), две стороны и
endFill,
автоматически закрывающая нашу фигуру, рисуя
последнюю сторону треугольника. Удобно.
- Введите эти
три метода для завершения класса
ColorStop Class.
ColorStop.prototype.selectMe = function() {
for (var i = 0; i<this._parent.colorStops.length; i++) {
this._parent.colorStops[i].deselectMe();
}
this.drawMe(0);
well.selected = this;
setSliderValues();
};
ColorStop.prototype.deselectMe = function()
{
this.drawMe(0xCCCCCC);
);
ColorStop.prototype.getValue = function() {
return this._x/this._parent._width;
};
Итак, selectMe
сначала выполняется для всех
colorStops и
снимает с них выделение (присваивает им серый
цвет, согласно следующему методу). После этого
функция перерисовывает вновь выделенный
colorStop с черной
заливкой, в выделенной переменной записывает
ссылку на саму себя и устанавливает ползунки
цвета для соответствия текущему цвету.
deselectMe просто
присваивает colorStop
светло-серый цвет. Наконец,
getValue будет
возвращать позицию текущего
colorStop в виде
процентного значения от ширины области цвета.
Например, если ширина области цвета равна 100, и
текущий colorStop
имеет значение _x,
равное 50, тогда getValue
возвратит значение .5 (50/100). Это процентное
значение будет использоваться при определении
степени этого цвета в градиенте.
- Чтобы
colorStop работал
в качестве расширения объекта
MovieClip, нам
нужно сделать некоторые дополнения. Введите
следующие строки кода.
Object.registerClass("colorStopSymbol",
ColorStop);
makeColorStop = function (x, col) {
well.attachMovie("colorStopSymbol", "h"+well.stopDepth,
Кwell.stopDepth,{_x:x, _y:well.h, stopColor:col});
};
Object.registerClass
позволяет нам ассоциировать символ, хранимый в
Library, с новым определением класса. Вспомните
о символе пустого фильма в Library, который мы
создали в начале этого проекта. Его
идентификатор Linkage
является первым параметром метода
registerClass.
Вторым параметром является имя самого класса.
Теперь, каждый раз при добавлении одного их этих
фильмов на рабочее место, он будет автоматически
становиться инстансом
ColorStop Class!
Функция makeColorStop
вызывается каждый раз при щелчке на кнопке цвета
нашей области (сейчас вы увидите этот процесс).
Необходима всего лишь одна строка кода, которая
добавляет наш пустой фильм на рабочее место и
присваивает ему начальные параметры. Все
остальное сделает определение нашего класса!
Разумеется, чтобы позволить
colorStop
наследовать параметры и методы объекта
MovieClip, нам
нужно установить параметр
prototype объекта
colorStop на значение нового фильма.
После этого мы регистрируем наш класс с именем
Object, присваивая
ему Linkage ID
нашего символа в Library (colorStopSymbol).
Мы
проделали половину работы.
- Следующая
часть программы работает с ползунками, которые,
опять-таки, создаются целиком с помощью
рисования API. Введите следующий код под всем
уже имеющимся кодом программы.
createSlider =
function (instance, label, depth, x, y,
height, width, func) {
this[instance] = gp.createEmptyMovieClip(instance+depth, depth);
var slider = this[instance];
slider._x = x;
slider._y = y;
slider.createTextField("label", 0, -20, 0, 10, 10);
slider.label.autoSize = "right";
slider.label.text = label;
slider.label.textColor = 0xFFFFFF;
slider.label.bold = 1;
slider.lineStyle(2, 0, 100);
slider.moveTo(0, height/2);
slider.lineTo(width, height/2);
var s = slider.createEmptyMovieClip("slider", 10);
s.lineStyle(1, 0, 100);
s.moveTo(-height/4, 0);
s.beginFill(OxCCCCCC, 100);
s.lineTo(height/4, 0);
s.lineTo(height/4, height);
s.lineTo(-height/4, height);
s.endFill();
s.onPress = function() {
this.startDrag(0, 0, 0, width, 0);
this .onMouseMove = func;
};
s.range = width;
var releaseMe = function () {
delete this.onMouseMove;
stopDrag();
};
s.onRelease = s.onReleaseOutside=releaseMe;
slider.getValue = function() {
return s._x/s.range;
};
slider.setValue = function(val) {
s._x = val/100 *s. range;
};
};
Пример
9.3.
Этот фрагмент довольно велик, однако вам должно
быть совершенно понятно, что в нем происходит.
Мы создаем новый фильм, в котором будет
содержаться наш ползунок. После этого создаем
текстовое поле для заголовка, располагаем и
форматируем его перед несколькими последующими
строками. Рисование API вызывается для рисования
горизонтальной линии.
Ползунок сам по себе является новым фильмом, так
как нам необходимо перетаскивать его и проверять
его параметр _x.
Первый блок кода API просто рисует
прямоугольник. Высота прямоугольника
определяется переменной
height. Его ширина будет равна половине
высоты. После этого устанавливается его событие
onPress для
горизонтального перетаскивания.
Функция, присваиваемая нами событию
onMouseMove,
отсылается в начальном вызове функции, что
позволяет иметь различные ползунки с отдельными
функциями. Говоря о методах, используемых в
момент отпускания кнопки мыши, нужно иметь в
виду два конечных метода, которые устанавливают
и получают значение ползунка (в остальном здесь
все то же самое).
getValue, как и метод
ColorStop.getValue,
созданный нами выше, возвращает позицию
_x ползунка в виде
процентного значения от полной ширины ползунка.
setValue просто
обращает это значение, располагая ползунок на
определенной позиции шкалы ползунка.
- Ниже
приведены последние функции для наших ползунков.
getSliderValues =
function () {
var r = redSlider.getValue() *255;
var g = greenSlider.getValue()*255;
var b = blueSlider.getValue()*255;
return r << 16 | g << 8 | b;
};
setSliderValues = function () {
var hex = well.selected.stopColor.toString(16);
while (hex.length<6) {
hex = "0"+hex;
}
var r = parselnt(hex.substr(0, 2), 16);
var g = parselnt(hex.substr(2, 2), 16);
var b = parselnt(hex.substr(4, 2), 16);
redSlider.setValue(r/2.55);
greenSlider.setValue(g/2.55);
blueSlider.setValue(b/2.55);
swatch.drawSolid();
};
Несмотря на то, что мы создали функции для
получения и установки отдельных значений
ползунка, три ползунка цвета, на самом деле,
работают вместе. Поэтому две эти функции созданы
специально для них. Первая из них -
getSliderValues,
получает значение каждого ползунка (процент),
умножает его на 255, и затем выполняет сдвиг
разряда для возврата к шестнадцатеричному
значению.
Сдвиг разряда, являясь довольно сложной
процедурой, передвигает наши значения на
корректные позиции для складывания по десятичной
системе с целью получения значений цвета.
Например, десятичное значение для желтого цвета
- 16776960, а его шестнадцатеричным эквивалентом
является FFFF00. Шестнадцатеричное значение
разбивается на три отдельных цветовых канала, и
в итоге получаем 255(FF), 255 и 0 для красного,
зеленого и синего цветов, соответственно. Как
вернуться от этих значений к десятичному
значению для всех трех каналов? Можно
использовать оператор побитного сдвига влево для
сдвига значений на их корректные позиции.
Оператор побитного сдвига влево, который гораздо
более полезен при работе с двоичной системой,
умножает число на указанную степень числа 2.
255<<16 умножает 255 на 2 в степени 16, что нам
и нужно для сдвига значения красного цвета для
нашего шестнадцатеричного числа. Если вы введете
эту операцию в калькулятор, то результатом будет
число 16711680. Далее мы выполняем то же
действие с зеленым значением, но умножаем его на
2 в степени 8 и получаем 65280. Нам не нужно
осуществлять сдвиг значения синего цвета.
Побитовый OR-оператор | складывает эти числа
(это упрощенное объяснение, однако оно подходит
в нашем случае). После сдвига нашими значениями
255, 255, 0 будут, соответственно, 16711680,
65280 и 0.
Вернемся к нашей программе.
setSliderValues
выполняет обратное действие
getSliderValues
при каждом выборе
colorStop (после чего образец цвета
отображает цвет colorStop).
Сначала эта функция выясняет значение цвета
выбранного colorStop,
затем оно преобразуется в шестнадцатеричное
значение, с добавлением дополнительных нулей до
строки размером в шесть символов. Затем она
отделяет каждый компонент цвета и устанавливает
нужным образом соответствующие ползунки. Мы
создавали точно такую же функцию в упражнении с
превращением фрукта.
Приближаемся к завершению программы!
- Следующий
фрагмент кода просто создает все необходимые
ползунки. Вспомним аргументы
createSlider:
(instanceName, labelText, depth, _x, _y, height,
width, functionToBePerformed). Введите
этот код под всеми предшествующими строками.
createSlider("rotSlider", "rotation",
200, 200, 40, 20, 175, colorSample);
createSlider("wSlider", "width",
201, 200, 70, 20, 175, colorSample);
wSlider.setValue(100);
createSlider("hSlider", "height",
202, 200, 100, 20, 175, colorSample);
hSlider.setValue(100);
createSlider("redSlider", "red",
203, 60, 200, 20, 200, colorSwatch);
createSlider("greenSlider", "green",
204, 60, 235, 20, 200, colorSwatch);
createSlider("blueSlider", "blue",
205, 60, 270, 20, 200, colorSwatch);
Здесь мы вызываем функцию
createSlider и отправляем необходимые
параметры. В идеале, эти значения менялись бы
посредством изменения начальных переменных
paletteWidth и
paletteHeight,
расположенных в верхней части кода, но это бы
требовало еще большего количества кода! Оставляю
это на ваше усмотрение - если хотите, можете
попробовать реализовать эту возможность.
- Осталось
всего два фрагмента кода. Надо создать рисунки
для всех наших функций. Введите следующий код.
well =
gp.createEmptyMovieClip ("well", 5);
well._x = 10;
well._y = 140;
well.w = 380;
well.h = 30;
well .colorStops = [];
well.stopDepth = 0;
makeColorStop(0, 0xFFFFFF);
makeColorStop(well.w, 0x000000);
stopMaker =
gp.createEmptyMovieClip("stopMaker", 0);
stopMaker._x = well._x;
stopMaker._y = well._y+well. h;
stopMaker.onPress = function() {
makeColorStop(this._xmouse, getSliderValues());
};
stopMaker.beginFill(0, 0);
stopMaker.lineTo(well.w, 0);
stopMaker.lineTo(well.w, well.h);
stopMaker.lineTo(0, well.h);
stopMaker.endFill();
well
будет областью градиента, и на ней мы
устанавливаем два начальных объекта
colorStops. stopMaker
- это невидимая кнопка, которая будет находиться
под well. Щелчок
на ней будет создавать
colorStops. Необходимо отделить эту
функциональность от самой области, так как
colorStops будут
иметь свои собственные события
onPress, и нам не
нужно, чтобы они игнорировались из-за этой
невидимой кнопки (по той же причине мы
устанавливаем глубину
stopMaker под глубиной
well).
Обратите внимание, что при возникновении события
onPress мы
вызываем нашу функцию
makeColorStop, которая добавляет инстанс
нашего собственного класса
Class на рабочий
стол. Обратившись к исходному тексту этой
функции, вы увидите, что она принимает два
параметра - позицию _x
и цвет. Позиция _x
определяется местоположением щелчков мыши,
однако значение цвета должно быть обработано.
Вместо прямой отправки значения цвета, мы
вызываем функцию
getSliderValues, которая будет возвращать
значение цвета по позициям наших трех ползунков.
- Это последняя
часть кода программы.
sample =
gp.createEmptyMovieClip("sample", 10);
sample._x = 10;
sample._y = 10;
sample.w = 120;
sample.h = 120;
swatch = gp.createEmptyMovieClip("swatch",
15);
swatch._x = 290;
swatch._y = 205;
swatch.w = 80;
swatch.h = 80;
gradType =
gp.attachMovie("FCoraboBoxSymbol",
"gradType", 20);
gradType._x = 250;
gradType._y = 10;
gradType.width = 130;
gradType.addItem("linear");
gradType.addItem("radial");
gradType.setChangeHandler("changeGrad",this);
sample.matrix = imatrixType:"box", x:0, y:0,
w:sample.w, h:sample.h, r:0};
well.matrix = {matrixType:"box", x:0, y:0,
wrwell.w, h:well.h, r:0};
sample.drawGrad = drawGrad;
well.drawGrad = drawGrad;
swatch.drawSolid = drawSolid;
swatch.drawSolid();
setGrad();
Здесь, наконец, появляются фильмы, на которые мы
ссылались на протяжении всего кода!
sample и
swatch
предназначены для совместной работы с
well, в результате
которой мы получаем воистину впечатляющий
результат. gradType
- это наше поле ComboBox
(Macromedia выполнила эту работу за вас!), в
которое мы добавляем наши строковые значения
градиента. Функция
changeGrad выполняется каждый раз при
щелчке мышью на ComboBox.
После установки исходных матриц для градиентов,
присваиваем им их методы
drawGrad и вызываем методы рисования для
всех образцов (setGrad
вызывает как
sample.drawGrad, так и
well.drawGrad()).
- Вот и все!
Запустите ваш фильм и проверьте работу палитры.
Мы получили полнофункциональную палитру с
практически пустой библиотекой Library.
Согласитесь, рисование API - это очень удобно!
Матрица преобразования
Помните, я говорил о том, что есть простой и более
сложный методы поддержки градиентов? В "сложном"
способе используются все те же параметры, что и при
работе с beginGradientFill,
однако существуют различия в формировании матрицы.
Если
вы обратитесь к панели Reference (Window >
Reference) и посмотрите на
beginGradientFill, вы увидите, что вторым
типом матрицы является матрица преобразования
размером 3х3.
Немного поговорим об этом, так как здесь вам должно
быть далеко не все понятно. Это будет не урок
линейной алгебры, а упражнение, выполнив которое вы
будете иметь преставление о том, что вам нужно знать
для использования этой матрицы. Разумеется, мы могли
бы поговорить о единичных матрицах, перестановках и
т.д., но сейчас это не обязательно.
Матрица -
это прямоугольный массив чисел. Проше говоря - ящик
с числами. Это специальная структура ящика, которая
позволяет математическим путем выполнять
разнообразные действия с числами. Но как же этот
ящик с числами связан с параметрами градиента?
Если
вы просто добавляете числа, вы можете не обращать
внимания на третий столбец. (Конечно, если вы только
добавляете числа и оставляете градиент статическим,
нет никаких оснований для отказа от использования
первого типа матрицы. Матрица второго типа
становится нужной в том случае, если нам необходимо
изменять форму и анимировать градиент, так как
структура 3х3 позволяет преобразовывать матрицу с
помощью операций, а следовательно и градиент.)
Остальные шесть параметров относятся к сдвигу,
размеру и повороту градиента.
Для
преобразования существующей матрицы мы просто
умножаем ее на матрицу, содержащую преобразование.
Если вы никогда не видели пример умножения матриц,
посмотрите на рисунок ниже.
С
помощью этого процесса, вы можете создать класс
матриц, в котором бы автоматически выполнялась эта
операция (она состоит из элементарных операций
сложения и умножения), и сохранять необходимые
матрицы преобразования, которые будут изменять
градиенты.
К
счастью кто-то облегчил нам жизнь, обеспечив нас
возможностью работать с преобразованиями матриц
подобным образом. Если у вас под рукой есть компакт
диск Flash MX, зайдите в папку Goodies\ Macromedia\
Other Samples и найдите в ней файл
transformmatrix.as.
(Вы также можете скопировать его с компакт-диска
этой книги.) Этот файл ActionScript содержит класс
матриц для поддержки преобразований матрицы
градиента. Откройте этот файл в текстовом редакторе
и внимательно прочтите весь код.
Нам в
помощь предоставлены методы "упрощения процесса
построения матриц для перехода к функциям рисования
Flash MX". Внимательно просмотрев код, вы увидите,
что класс содержит матрицы изменения размеров,
смещения и поворота, а также метод с именем concat,
который осуществляет умножение матриц, как показано
на рисунке выше.
Матрицы преобразования для нашего градиента выглядят
следующим образом.
- Смещение
- Поворот
- Изменение
размера
Посредством умножения этих матриц на нашу исходную
матрицу, мы можем легко преобразовывать градиенты
для создания некоторых впечатляющих эффектов.
Рассмотрим небольшой пример с использованием этих
методов.
Преобразование градиента 1:
блюз восхода
- Создайте и
сохраните новый фильм. Скопируйте
transformmatrix.as
в ту же папку, в которой находится созданный
фильм.
- Откройте
панель Actions, выделите кадр 1 слоя по
умолчанию и введите следующий код.
#include "transformmatrix.as"
this.createEmptyMovieClip("grad" , 1);
colors = [0xDEFFFF, 0x000033];
alphas = [100, 100];
ratios = [0, 255];
matrix = new TransformMatrix();
matrix.rotate (-90);
matrix.scale (400, 550);
matrix.translate(0, 800);
drawGrad = function () {
matrix.translate(0, -5);
this.clear();
this.beginGradientFill("linear", colors, alphas, ratios, matrix);
this.moveTo(0, 0);
this.lineTo(550, 0);
this.lineTo(550, 400);
this.lineTo(C, 400);
this.endFill();
};
grad.onEnterFrame = drawGrad;
Смысл этого кода вам уже знаком. Единственные
новые строки - это преобразование матрицы.
Во-первых, мы создаем новый инстанс объекта
матрицы, как в файле
transformmatrix.as. Поворачиваем нашу
новую матрицу на -90 градусов (объект сам
осуществляет перевод в радианы) и увеличиваем
его до размера 400x550. Выполнение этого
изменения размера на нашей исходной пустой
матрице изменит ее размер для соответствия
высоте и ширине нашего рабочего места по
умолчанию.
Несмотря на то, что в данном примере это может
ввести в заблуждение, помните, что при
выполнении операции изменения размера вы
умножаете имеющийся размер на ваши новые
значения, поэтому, при умножении на 2 будет
удвоен текущий размер.
Затем мы смещаем наш градиент вниз на 800
пикселей, что позволит нам постепенно смещать
градиент вверх в каждом кадре, и это будет
похоже на восход солнца.
- Запустите
фильм. Откиньтесь на спинку кресла и
расслабьтесь, наблюдая за тем, как темно-синяя
ночь постепенно сменится днем (со скоростью 12
fps это изменение происходит плавно и медленно).
Этот пример был небольшой и эффектный. Может
быть еще интереснее.
Преобразование градиента 2:
поворот
- Откройте
новый фильм (убедитесь, что он находится в одной
папке с файлом
transformmatrix.as). Откройте панель
Actions и введите следующий код в кадр 1 главной
точки временной шкалы.
#include
"transformmatrix.as"
colors = [0XE7D2EC, 0xE7D2EC, 0xE7D2EC];
alphas = [0, 100, 0];
ratios = [128, 192, 255];
gradType = ["radial", "linear"];
gradNum = 0;
drawGrad = function () {
this.matrix.rotate(this.rot);
this.clear();
this.beginGradientFill(gradType[gradNum], colors, alphas, ratios,
this.matrix);
this.moveTo(-275, -200);
this.lineTo(275, -200);
this.lineTo(275, 200);
this.lineToi-275, 200);
this.endFill();
};
for (i = 1; i<5; i++) {
var grad = _root.createEmptyMovieClip("grad"+i, i);
grad._x = 275;
grad._y = 200;
grad.matrix = new TransformMatrix();
grad.matrix.scale(200, 400);
grad.onEnter Frame = drawGrad;
grad.rot = (i%2>0) ? 5 : -5;
}
grad1.matrix.rotate(90);
grad2.matrix.rotate(90);
this.onMouseDown = function() {
gradNum = gradNum+1>1 ? 0 : gradNum+1;
};
Пример
9.4.
Обратите внимание, что в этом коде мы перемещаем
все четыре фильма в центр нашего рабочего места.
Это делается потому, что метод rotate
поворачивает точку регистрации нашего фильма, и
нужно было получить этот эффект в центре
рабочего места, а не в левом верхнем углу. Я
также добавил управляющий элемент
onMouseDown для
переключения между линейным и радиальным
градиентом. Предположите, какой эффект получится
в результате работы этого кода, прежде чем вы
запустите этот фильм, чтобы выяснить, насколько
хорошо вы поняли принципы создания градиентов с
помощью рисования API.
Если вы незнакомы с оператором модуля (%),
запомните: он возвращает остаток от деления двух
операндов. Например, 5%2 вернет 1 (делим 5 на 2
и получаем остаток 1). Это быстрый способ
определения четности числа. Любое четное число
делится на 2 без остатка, а при делении
нечетного числа будет остаток 1. Проверяя
четность, можно поворачивать фильмы в различных
направлениях.
- Сделайте
черным фон вашего фильма, и затем запустите его.
Не забудьте щелкнуть мышью для переключения
между следующими двумя состояниями.
Преобразование градиента 3:
затемнение градиентов
- Откройте
новый фильм и, как обычно, убедитесь, что он
сохранен в той же папке, что и файл
transformmatrix.as.
Введите следующий код в кадр 1 главной точки
временной шкалы.
#include
"transformmatrix.as"
gradDepth = 1;
grads = [];
makeGrad = function () {
var grad = this.createEmptyMovieClip("grad"+gradDepth, gradDepth);
grad._x = 275;
grad._y = 200;
grad._alpha = 10;
grad.matrix = new Transf ormMatrix ();
grad.matrix.scale(5, 10);
grads.push(grad);
gradDepth++;
};
colors = [0x000000, 0xFFFFFF, 0x000000,
0xFFFFFF, 0x000000];
alphas = [0, 100, 0, 100, 0];
ratios = [0, 64, 128, 192, 255];
gradType = ["linear", "radial"];
gradNum = 0;
drawGrads = function () {
for (var i = 0; i<grads. length; i++) {
var grad = grads[i];
grad._alpha += 2;
grad.matrix.scale(1.1, 1.1);
grad.matrix.rotate (5); grad.clear();
grad.beginGradientFill(gradType[gradNum], colors, alphas,
ratios,grad.matrix);
grad.moveTo(-275, -200);
grad.lineTo(275, -200);
grad.lineTo(275, 200);
grad.lineTo(-275, 200);
grad.endFill();
if (grad.matrix.b>2000) {
grad.removeMovieClip();
grads.shift();
}
}
};
this.onEnterFrame = drawGrads;
this.onMouseDown = function() {
gradNum = gradNum+1>1 ? 0 : gradNum+1;
};
setInterval(this, "makeGrad", 1000);
Пример
9.5.
В
этом коде мы установили функцию для создания
градиентов. Если вы перейдете в конец кода, вы
увидите, что здесь использован setInterval для
вызова этой функции каждые 1000 миллисекунд.
Каждый градиент будет изначально небольшим и
затемненным, затем, при каждом вызове
drawGrads, он
будет увеличиваться и становиться ярче. По
достижении максимального размера он будет
удален.
- Теперь
сделайте фон вашего фильма черным и запустите
его. Не забудьте попробовать переключение
линейных/радиальных градиентов щелчком мыши!
Необходимо иметь в виду, что градиенты занимают
довольно много процессорного времени во Flash, в
особенности те, которые заполняют рабочее место
полностью (или полностью анимированные). Это
необходимо учесть при изучении создания
градиентов и поиске новых способов их анимации.
Данные упражнения являются лишь надводной частью
айсберга в отношении матриц. С их помощью можно
реализовать преобразование одного объекта в
другой, а также изменение размеров для
соответствия пропорциям другого объекта. Можно
также применить промежуточные кадры между
градиентами с использованием их значений
преобразования. При регулярной практике вы
сможете применять довольно сложную математику,
экспериментируя и создавая фантастические
эффекты.
|