В прошлых версиях Flash для создания графики в режиме run-time было не обойтись без предопределенных символов и манипуляций ими с помощью кода. Разумеется, вы могли взять рисунок из Library и делать с ним интересные вещи, но вы были сильно ограничены в возможности создавать полезные, динамичные рисунки, не нарисовав их перед этим вручную. Один из первых проектов, которые создают все пользователи Flash - это программа для рисования, однако быстро становится ясно, что на площади 100х100 пикселей с помощью тонкой линии и дубликатов фильмов можно нарисовать не слишком много интересного.
Теперь Flash MX Macromedia предоставляет возможность создания графики в режиме run-time, и эта возможность уникальна (да, громко сказано, однако нигде больше создание динамических рисунков не осуществляется именно так, и именно этому посвящена вся данная лекция).
С использованием рисования API (Application Programming Interface) и новых методов фильмов createEmptyMovieClip и createTextField пользователи Flash теперь могут программировать и создавать все, что угодно, начиная с кнопок и форм и заканчивая приложениями для рисования и целыми интерфейсами, без предварительного ручного рисования. Этому будут посвящены следующие несколько лекций.
Перед тем, как рассматривать более сложные приложения рисования API, проведем обзор некоторых методов, изученных в лекции 3.
lineStyle устанавливает визуальные параметры линии, такие как высота, цвет и интенсивность.moveTo перемещает "карандаш" на новые координаты рабочего стола без рисования линии.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 состоит в получении значений для ваших функций. Сначала создадим несложный инструмент, который поможет нам выполнить эту задачу. Это будет небольшой фильм для рисования линий на рабочем столе. Каждый рисунок будет представлять собой буфер перемещаемого изображения. После завершения создания рисунков будет происходить считывание в окне 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 (на компакт-диске), чтобы увидеть результат работы кода.

shapes = [];
shapeNum = 0;
drawShapes = function () {
_root[shapes[shapeNum]]();
shapeNum = shapeNum+1>shapes.length-1 ? 0 : shapeNum+1;
};
setInterval(drawShapes, 100);
Это практически весь код, который нужно ввести в наш новый фильм. Мы инициализируем две переменные (и будем сохранять имена наших функций в массиве, сразу после определения их количества) и создаем функцию для смены кодированных изображений. drawShapes будет просто обрабатывать циклически массив и переходить к следующему изображению. Скорость, с которой будет выполняться это действие, указывается выражением в следующей строке. Здесь я указал вхождение функции через каждую десятую долу секунды. Изменяя это число можно быстро изменять частоты кадров вашего фильма.
Вторая строка в функции drawShapes является примером использования условного оператора. По большому счету, это то же самое, что и выражение if...else. Строка начинается с переменной, в данном случае - shapeNum. Мы будем присваивать значение переменной shapeNum в зависимости от обстоятельств. Далее будет проверяться условие. В нашем случае мы проверяем, является ли shapeNum+1 большим по значению, чем полное число индексов в массиве shapes. Если это выражение принимает значение "истина", то все, что стоит после знака "?", приписывается переменной shapeNum (в нашем примере - 0). Если условие возвращает значение "ложь", тогда переменной shapeNum (в примере выше - shapeNum+1 ) присваивается все, что стоит после двоеточия. Вся строка тогда сводится к увеличению shapeNum до тех пор, пока она не достигнет конца массива shapes, в случае чего будет осуществлен переход на первый индекс.
Не закрывайте окно Output при его появлении - оно нам понадобится. При рисовании не следует слишком долго рисовать карандашом, несмотря на то, что движение будет минимально. Чем быстрее вы рисуете, тем меньше кода впоследствии будет использоваться.
Ах уж это рисование мышью! Будь у нас время, мы бы реализовали нечто более интересное, например, традиционные кривые Безье и средства для рисования фигур - окружностей и многоугольников.

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;
В первой строке мы создаем фильм, который будет содержать наши рисунки. Затем создаются два новых объекта, названные "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;
};
Подготовительные действия завершены. Собственно вся подготовка и заключается в этой функции. 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();

При создании радиальных градиентов единственное, что нужно изменить - тип градиента "линейный" на тип "радиальный". Вот и все. Вам также понадобится учесть, что параметры 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 объекта , созданный в предыдущем шаге. Он будет изменять значения матрицы перед перерисовыванием квадрата. Остаток кода - тот же код рисования, который мы использовали ранее. Единственным отличием является то, что значения устанавливаются прежними переменными. Мы также добавляем updateAfterEvent для обновления экрана каждый раз при движении мыши, и этот код выполняется заново для предотвращения эффекта мигания.
drawSquare(); drawObj.onMouseMove = drawSquare;
Сначала вызывается функция drawSquare в начале фильма, затем мы присваиваем функцию событию mouseMove объекта drawObj. Запустите фильм, чтобы проверить это в действии.

Имейте в виду, что это будет работать для градиента, который заполняет всю ширину квадрата. При изменении ширины (если ширина исказит градиент, она не будет равна высоте) вам также понадобится поработать с формулами setMatrix.
Итак, один из примеров готов. Как насчет создания чего-то такого, что может пригодиться в будущих приложениях?
Здесь мы создадим цветовую палитру градиента, которая позволит создавать и изменять градиенты. Поэкспериментируйте с готовой палитрой ( gradient_palette.swf на компакт-диске), чтобы иметь представление о том, что мы будем создавать. Настройте параметры, изменяя радиальный градиент на линейный, и попробуйте добавлять, удалять и изменять цвета в области цветов. Во время работы, учтите, что все, за исключением компонента ComboBox в правом верхнем углу, создано динамически с помощью рисования API.

А теперь - кое-что интересное! Это упражнение довольно большое, и здесь используются некоторые более сложные концепции программирования, однако оно того стоит. Если вы хотите сравнивать ваш файл с примером во время работы, вы можете в любой момент обращаться к файлу gradient_palette.fla на компакт-диске. Итак, приступим.
ComboBox на рабочее место. Теперь удалите ее. Компонент и все его ресурсы теперь сохранены в Library, как нам и нужно. Мы будем добавлять ComboBox на рабочее место динамически в этом упражнении, и это будет единственным элементом нашей палитры, созданным заранее.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();
};
Этот код может показаться немного странным, так как мы ссылаемся здесь на объекты, которые еще не были созданы. Потерпите, сейчас мы во всем разберемся. Это начало фрагмента кода, в котором происходит взаимодействие с оттенками цветов. Их в нашем фильме будет три.
В коде выше мы сначала устанавливаем два типа градиента (линейный или радиальный) в виде строк в массиве gradTypes. Мы сделаем их доступными при установке градиента для образца градиента. dragNum просто содержит текущий индекс массива gradTypes.
Функция будет сортировать наш двумерный массив. Мы к ней еще вернемся.
Функция setGrad выполняется для всех colorStops (небольшие треугольники под областью цвета на рисунке в начале этого упражнения) и создает значения градиентов на основании их цветов и позиций _x. Она инициализирует все массивы (colors, alphas и ratios), предназначения каждого из которых вы еще узнаете. Массив stops будет содержать позиции _x всех фильмов colorStop, а также ссылки на сами фильмы.
Первый цикл for затем выполняется для каждого colorStop (ссылки на каждый из них находятся в массиве colorStops цветовой области well, который мы сейчас будем устанавливать) и располагает позицию _x объекта colorStop и сам объект в этом двумерном массиве stops. После настройки массива мы сортируем его содержимое с помощью функции . Этот массив необходимо сортировать, так как нужно знать порядок colorStops по их текущей позиции _x. Так как порядок позиций _x не будет являться порядком создания (при каждом щелчке пользователя могут добавляться дополнительные colorStops, и они, разумеется, могут быть перемещены в любое место цветовой области), нам нужно сортировать colorStops в порядке возрастания с наименьшей позиции _x к наибольшей. Следовательно, мы можем получить их значения для нашего градиента. Для этого мы воспользуемся методом Array.sort и функцией . Метод Array.sort принимает метод сравнения, с помощью которого осуществляется сортировка. Самый простой из них (и, как оказалось, наиболее полезный) тот, который будет сортировать числа в порядке значения.
sorter = function(a, b) {
return a > b
}
Этот псевдокод будет сортировать числа в порядке возрастания (заменив символ "больше, чем" (>) на символ "меньше, чем" (<), вы реализуете сортировку в порядке убывания). a и b представляют собой два смежных значения в нашем массиве. Flash будет полностью проходить через нужный массив и "перемешивать" все значения до тех пор, пока они не будут отсортированы. Для сортировки массива с несколькими измерениями нужно просто указать в методе , по какому индексу необходимо осуществлять сортировку. В нашем упражнении с палитрой мы использовали первый индекс массива, который является параметром _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).
Мы проделали половину работы.
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;
};
};
Этот фрагмент довольно велик, однако вам должно быть совершенно понятно, что в нем происходит. Мы создаем новый фильм, в котором будет содержаться наш ползунок. После этого создаем текстовое поле для заголовка, располагаем и форматируем его перед несколькими последующими строками. Рисование 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() ).

Помните, я говорил о том, что есть простой и более сложный методы поддержки градиентов? В "сложном" способе используются все те же параметры, что и при работе с beginGradientFill, однако существуют различия в формировании матрицы.
Если вы обратитесь к панели Reference (Window > Reference) и посмотрите на beginGradientFill, вы увидите, что вторым типом матрицы является матрица преобразования размером 3х3.

Немного поговорим об этом, так как здесь вам должно быть далеко не все понятно. Это будет не урок линейной алгебры, а упражнение, выполнив которое вы будете иметь преставление о том, что вам нужно знать для использования этой матрицы. Разумеется, мы могли бы поговорить о единичных матрицах, перестановках и т.д., но сейчас это не обязательно.
Матрица - это
Если вы просто добавляете числа, вы можете не обращать внимания на третий столбец. (Конечно, если вы только добавляете числа и оставляете градиент статическим, нет никаких оснований для отказа от использования первого типа матрицы. Матрица второго типа становится нужной в том случае, если нам необходимо изменять форму и анимировать градиент, так как структура 3х3 позволяет преобразовывать матрицу с помощью операций, а следовательно и градиент.) Остальные шесть параметров относятся к сдвигу, размеру и повороту градиента.
Для преобразования существующей матрицы мы просто умножаем ее на матрицу, содержащую преобразование. Если вы никогда не видели пример умножения матриц, посмотрите на рисунок ниже.

С помощью этого процесса, вы можете создать класс матриц, в котором бы автоматически выполнялась эта операция (она состоит из элементарных операций сложения и умножения), и сохранять необходимые матрицы преобразования, которые будут изменять градиенты.
К счастью кто-то облегчил нам жизнь, обеспечив нас возможностью работать с преобразованиями матриц подобным образом. Если у вас под рукой есть компакт диск Flash MX, зайдите в папку Goodies\ Macromedia\ Other Samples и найдите в ней файл transformmatrix.as. (Вы также можете скопировать его с компакт-диска этой книги.) Этот файл ActionScript содержит класс матриц для поддержки преобразований матрицы градиента. Откройте этот файл в текстовом редакторе и внимательно прочтите весь код.
Нам в помощь предоставлены методы "упрощения процесса построения матриц для перехода к функциям рисования Flash MX". Внимательно просмотрев код, вы увидите, что класс содержит матрицы изменения размеров, смещения и поворота, а также метод с именем concat, который осуществляет умножение матриц, как показано на рисунке выше.
Матрицы преобразования для нашего градиента выглядят следующим образом.


Посредством умножения этих матриц на нашу исходную матрицу, мы можем легко преобразовывать градиенты для создания некоторых впечатляющих эффектов.
Рассмотрим небольшой пример с использованием этих методов.
transformmatrix.as в ту же папку, в которой находится созданный фильм.#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 пикселей, что позволит нам постепенно смещать градиент вверх в каждом кадре, и это будет похоже на восход солнца.
Этот пример был небольшой и эффектный. Может быть еще интереснее.
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;
};
Обратите внимание, что в этом коде мы перемещаем все четыре фильма в центр нашего рабочего места. Это делается потому, что метод rotate поворачивает точку регистрации нашего фильма, и нужно было получить этот эффект в центре рабочего места, а не в левом верхнем углу. Я также добавил управляющий элемент onMouseDown для переключения между линейным и радиальным градиентом. Предположите, какой эффект получится в результате работы этого кода, прежде чем вы запустите этот фильм, чтобы выяснить, насколько хорошо вы поняли принципы создания градиентов с помощью рисования API.
Если вы незнакомы с оператором модуля (%), запомните: он возвращает остаток от деления двух операндов. Например, 5%2 вернет 1 (делим 5 на 2 и получаем остаток 1). Это быстрый способ определения четности числа. Любое четное число делится на 2 без остатка, а при делении нечетного числа будет остаток 1. Проверяя четность, можно поворачивать фильмы в различных направлениях.

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);
В этом коде мы установили функцию для создания градиентов. Если вы перейдете в конец кода, вы увидите, что здесь использован drawGrads, он будет увеличиваться и становиться ярче. По достижении максимального размера он будет удален.

Необходимо иметь в виду, что градиенты занимают довольно много процессорного времени во Flash, в особенности те, которые заполняют рабочее место полностью (или полностью анимированные). Это необходимо учесть при изучении создания градиентов и поиске новых способов их анимации. Данные упражнения являются лишь надводной частью айсберга в отношении матриц. С их помощью можно реализовать преобразование одного объекта в другой, а также изменение размеров для соответствия пропорциям другого объекта. Можно также применить промежуточные кадры между градиентами с использованием их значений преобразования. При регулярной практике вы сможете применять довольно сложную математику, экспериментируя и создавая фантастические эффекты.
В прошлых версиях Flash для создания графики в режиме run-time было не обойтись без предопределенных символов и манипуляций ими с помощью кода. Разумеется, вы могли взять рисунок из Library и делать с ним интересные вещи, но вы были сильно ограничены в возможности создавать полезные, динамичные рисунки, не нарисовав их перед этим вручную. Один из первых проектов, которые создают все пользователи Flash - это программа для рисования, однако быстро становится ясно, что на площади 100х100 пикселей с помощью тонкой линии и дубликатов фильмов можно нарисовать не слишком много интересного.
Теперь Flash MX Macromedia предоставляет возможность создания графики в режиме run-time, и эта возможность уникальна (да, громко сказано, однако нигде больше создание динамических рисунков не осуществляется именно так, и именно этому посвящена вся данная лекция).
С использованием рисования API (Application Programming Interface) и новых методов фильмов createEmptyMovieClip и createTextField пользователи Flash теперь могут программировать и создавать все, что угодно, начиная с кнопок и форм и заканчивая приложениями для рисования и целыми интерфейсами, без предварительного ручного рисования. Этому будут посвящены следующие несколько лекций.
Перед тем, как рассматривать более сложные приложения рисования API, проведем обзор некоторых методов, изученных в лекции 3.
lineStyle устанавливает визуальные параметры линии, такие как высота, цвет и интенсивность.moveTo перемещает "карандаш" на новые координаты рабочего стола без рисования линии.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 состоит в получении значений для ваших функций. Сначала создадим несложный инструмент, который поможет нам выполнить эту задачу. Это будет небольшой фильм для рисования линий на рабочем столе. Каждый рисунок будет представлять собой буфер перемещаемого изображения. После завершения создания рисунков будет происходить считывание в окне 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 (на компакт-диске), чтобы увидеть результат работы кода.

shapes = [];
shapeNum = 0;
drawShapes = function () {
_root[shapes[shapeNum]]();
shapeNum = shapeNum+1>shapes.length-1 ? 0 : shapeNum+1;
};
setInterval(drawShapes, 100);
Это практически весь код, который нужно ввести в наш новый фильм. Мы инициализируем две переменные (и будем сохранять имена наших функций в массиве, сразу после определения их количества) и создаем функцию для смены кодированных изображений. drawShapes будет просто обрабатывать циклически массив и переходить к следующему изображению. Скорость, с которой будет выполняться это действие, указывается выражением в следующей строке. Здесь я указал вхождение функции через каждую десятую долу секунды. Изменяя это число можно быстро изменять частоты кадров вашего фильма.
Вторая строка в функции drawShapes является примером использования условного оператора. По большому счету, это то же самое, что и выражение if...else. Строка начинается с переменной, в данном случае - shapeNum. Мы будем присваивать значение переменной shapeNum в зависимости от обстоятельств. Далее будет проверяться условие. В нашем случае мы проверяем, является ли shapeNum+1 большим по значению, чем полное число индексов в массиве shapes. Если это выражение принимает значение "истина", то все, что стоит после знака "?", приписывается переменной shapeNum (в нашем примере - 0). Если условие возвращает значение "ложь", тогда переменной shapeNum (в примере выше - shapeNum+1 ) присваивается все, что стоит после двоеточия. Вся строка тогда сводится к увеличению shapeNum до тех пор, пока она не достигнет конца массива shapes, в случае чего будет осуществлен переход на первый индекс.
Не закрывайте окно Output при его появлении - оно нам понадобится. При рисовании не следует слишком долго рисовать карандашом, несмотря на то, что движение будет минимально. Чем быстрее вы рисуете, тем меньше кода впоследствии будет использоваться.
Ах уж это рисование мышью! Будь у нас время, мы бы реализовали нечто более интересное, например, традиционные кривые Безье и средства для рисования фигур - окружностей и многоугольников.

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;
В первой строке мы создаем фильм, который будет содержать наши рисунки. Затем создаются два новых объекта, названные "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;
};
Подготовительные действия завершены. Собственно вся подготовка и заключается в этой функции. 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();

При создании радиальных градиентов единственное, что нужно изменить - тип градиента "линейный" на тип "радиальный". Вот и все. Вам также понадобится учесть, что параметры 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 объекта , созданный в предыдущем шаге. Он будет изменять значения матрицы перед перерисовыванием квадрата. Остаток кода - тот же код рисования, который мы использовали ранее. Единственным отличием является то, что значения устанавливаются прежними переменными. Мы также добавляем updateAfterEvent для обновления экрана каждый раз при движении мыши, и этот код выполняется заново для предотвращения эффекта мигания.
drawSquare(); drawObj.onMouseMove = drawSquare;
Сначала вызывается функция drawSquare в начале фильма, затем мы присваиваем функцию событию mouseMove объекта drawObj. Запустите фильм, чтобы проверить это в действии.

Имейте в виду, что это будет работать для градиента, который заполняет всю ширину квадрата. При изменении ширины (если ширина исказит градиент, она не будет равна высоте) вам также понадобится поработать с формулами setMatrix.
Итак, один из примеров готов. Как насчет создания чего-то такого, что может пригодиться в будущих приложениях?
Здесь мы создадим цветовую палитру градиента, которая позволит создавать и изменять градиенты. Поэкспериментируйте с готовой палитрой ( gradient_palette.swf на компакт-диске), чтобы иметь представление о том, что мы будем создавать. Настройте параметры, изменяя радиальный градиент на линейный, и попробуйте добавлять, удалять и изменять цвета в области цветов. Во время работы, учтите, что все, за исключением компонента ComboBox в правом верхнем углу, создано динамически с помощью рисования API.

А теперь - кое-что интересное! Это упражнение довольно большое, и здесь используются некоторые более сложные концепции программирования, однако оно того стоит. Если вы хотите сравнивать ваш файл с примером во время работы, вы можете в любой момент обращаться к файлу gradient_palette.fla на компакт-диске. Итак, приступим.
ComboBox на рабочее место. Теперь удалите ее. Компонент и все его ресурсы теперь сохранены в Library, как нам и нужно. Мы будем добавлять ComboBox на рабочее место динамически в этом упражнении, и это будет единственным элементом нашей палитры, созданным заранее.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();
};
Этот код может показаться немного странным, так как мы ссылаемся здесь на объекты, которые еще не были созданы. Потерпите, сейчас мы во всем разберемся. Это начало фрагмента кода, в котором происходит взаимодействие с оттенками цветов. Их в нашем фильме будет три.
В коде выше мы сначала устанавливаем два типа градиента (линейный или радиальный) в виде строк в массиве gradTypes. Мы сделаем их доступными при установке градиента для образца градиента. dragNum просто содержит текущий индекс массива gradTypes.
Функция будет сортировать наш двумерный массив. Мы к ней еще вернемся.
Функция setGrad выполняется для всех colorStops (небольшие треугольники под областью цвета на рисунке в начале этого упражнения) и создает значения градиентов на основании их цветов и позиций _x. Она инициализирует все массивы (colors, alphas и ratios), предназначения каждого из которых вы еще узнаете. Массив stops будет содержать позиции _x всех фильмов colorStop, а также ссылки на сами фильмы.
Первый цикл for затем выполняется для каждого colorStop (ссылки на каждый из них находятся в массиве colorStops цветовой области well, который мы сейчас будем устанавливать) и располагает позицию _x объекта colorStop и сам объект в этом двумерном массиве stops. После настройки массива мы сортируем его содержимое с помощью функции . Этот массив необходимо сортировать, так как нужно знать порядок colorStops по их текущей позиции _x. Так как порядок позиций _x не будет являться порядком создания (при каждом щелчке пользователя могут добавляться дополнительные colorStops, и они, разумеется, могут быть перемещены в любое место цветовой области), нам нужно сортировать colorStops в порядке возрастания с наименьшей позиции _x к наибольшей. Следовательно, мы можем получить их значения для нашего градиента. Для этого мы воспользуемся методом Array.sort и функцией . Метод Array.sort принимает метод сравнения, с помощью которого осуществляется сортировка. Самый простой из них (и, как оказалось, наиболее полезный) тот, который будет сортировать числа в порядке значения.
sorter = function(a, b) {
return a > b
}
Этот псевдокод будет сортировать числа в порядке возрастания (заменив символ "больше, чем" (>) на символ "меньше, чем" (<), вы реализуете сортировку в порядке убывания). a и b представляют собой два смежных значения в нашем массиве. Flash будет полностью проходить через нужный массив и "перемешивать" все значения до тех пор, пока они не будут отсортированы. Для сортировки массива с несколькими измерениями нужно просто указать в методе , по какому индексу необходимо осуществлять сортировку. В нашем упражнении с палитрой мы использовали первый индекс массива, который является параметром _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).
Мы проделали половину работы.
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;
};
};
Этот фрагмент довольно велик, однако вам должно быть совершенно понятно, что в нем происходит. Мы создаем новый фильм, в котором будет содержаться наш ползунок. После этого создаем текстовое поле для заголовка, располагаем и форматируем его перед несколькими последующими строками. Рисование 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() ).

Помните, я говорил о том, что есть простой и более сложный методы поддержки градиентов? В "сложном" способе используются все те же параметры, что и при работе с beginGradientFill, однако существуют различия в формировании матрицы.
Если вы обратитесь к панели Reference (Window > Reference) и посмотрите на beginGradientFill, вы увидите, что вторым типом матрицы является матрица преобразования размером 3х3.

Немного поговорим об этом, так как здесь вам должно быть далеко не все понятно. Это будет не урок линейной алгебры, а упражнение, выполнив которое вы будете иметь преставление о том, что вам нужно знать для использования этой матрицы. Разумеется, мы могли бы поговорить о единичных матрицах, перестановках и т.д., но сейчас это не обязательно.
Матрица - это
Если вы просто добавляете числа, вы можете не обращать внимания на третий столбец. (Конечно, если вы только добавляете числа и оставляете градиент статическим, нет никаких оснований для отказа от использования первого типа матрицы. Матрица второго типа становится нужной в том случае, если нам необходимо изменять форму и анимировать градиент, так как структура 3х3 позволяет преобразовывать матрицу с помощью операций, а следовательно и градиент.) Остальные шесть параметров относятся к сдвигу, размеру и повороту градиента.
Для преобразования существующей матрицы мы просто умножаем ее на матрицу, содержащую преобразование. Если вы никогда не видели пример умножения матриц, посмотрите на рисунок ниже.

С помощью этого процесса, вы можете создать класс матриц, в котором бы автоматически выполнялась эта операция (она состоит из элементарных операций сложения и умножения), и сохранять необходимые матрицы преобразования, которые будут изменять градиенты.
К счастью кто-то облегчил нам жизнь, обеспечив нас возможностью работать с преобразованиями матриц подобным образом. Если у вас под рукой есть компакт диск Flash MX, зайдите в папку Goodies\ Macromedia\ Other Samples и найдите в ней файл transformmatrix.as. (Вы также можете скопировать его с компакт-диска этой книги.) Этот файл ActionScript содержит класс матриц для поддержки преобразований матрицы градиента. Откройте этот файл в текстовом редакторе и внимательно прочтите весь код.
Нам в помощь предоставлены методы "упрощения процесса построения матриц для перехода к функциям рисования Flash MX". Внимательно просмотрев код, вы увидите, что класс содержит матрицы изменения размеров, смещения и поворота, а также метод с именем concat, который осуществляет умножение матриц, как показано на рисунке выше.
Матрицы преобразования для нашего градиента выглядят следующим образом.


Посредством умножения этих матриц на нашу исходную матрицу, мы можем легко преобразовывать градиенты для создания некоторых впечатляющих эффектов.
Рассмотрим небольшой пример с использованием этих методов.
transformmatrix.as в ту же папку, в которой находится созданный фильм.#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 пикселей, что позволит нам постепенно смещать градиент вверх в каждом кадре, и это будет похоже на восход солнца.
Этот пример был небольшой и эффектный. Может быть еще интереснее.
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;
};
Обратите внимание, что в этом коде мы перемещаем все четыре фильма в центр нашего рабочего места. Это делается потому, что метод rotate поворачивает точку регистрации нашего фильма, и нужно было получить этот эффект в центре рабочего места, а не в левом верхнем углу. Я также добавил управляющий элемент onMouseDown для переключения между линейным и радиальным градиентом. Предположите, какой эффект получится в результате работы этого кода, прежде чем вы запустите этот фильм, чтобы выяснить, насколько хорошо вы поняли принципы создания градиентов с помощью рисования API.
Если вы незнакомы с оператором модуля (%), запомните: он возвращает остаток от деления двух операндов. Например, 5%2 вернет 1 (делим 5 на 2 и получаем остаток 1). Это быстрый способ определения четности числа. Любое четное число делится на 2 без остатка, а при делении нечетного числа будет остаток 1. Проверяя четность, можно поворачивать фильмы в различных направлениях.

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);
В этом коде мы установили функцию для создания градиентов. Если вы перейдете в конец кода, вы увидите, что здесь использован drawGrads, он будет увеличиваться и становиться ярче. По достижении максимального размера он будет удален.

Необходимо иметь в виду, что градиенты занимают довольно много процессорного времени во Flash, в особенности те, которые заполняют рабочее место полностью (или полностью анимированные). Это необходимо учесть при изучении создания градиентов и поиске новых способов их анимации. Данные упражнения являются лишь надводной частью айсберга в отношении матриц. С их помощью можно реализовать преобразование одного объекта в другой, а также изменение размеров для соответствия пропорциям другого объекта. Можно также применить промежуточные кадры между градиентами с использованием их значений преобразования. При регулярной практике вы сможете применять довольно сложную математику, экспериментируя и создавая фантастические эффекты.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.