Программное рисование во Flash MX
Предыдущие версии Флэш (вплоть до Флэш 5) не имели функций программного рисования. В чем-то такое решение было оправдано: ведь Флэш предназначен для рисования в design-time, то есть вручную - до запуска анимации. И тем не менее возникали задачи (часто не такие уж сложные), которые требовали именно программного рисования. Это и эффекты, для которых потребовалось бы слишком много ручной работы (например, градиент, изменяющийся в каждом следующем кадре строго определенным образом, или набор из огромного количества определенным образом расположенных тонких линий). И, тем более, - изображения, внешний вид которых сильно зависит от действий пользователя и заранее совершенно неизвестен (например, графики функций). Флэш МХ, наконец-то, предоставляет нам все необходимые средства для решения этих задач. И начнем мы изучение этих средств с самого простого - прямых и кривых различного вида.
Прямые и кривые
Итак, внутрь любого клипа мы можем добавить программно нарисованные прямые или кривые. Заметьте, что рисование происходит именно в конкретном клипе (и будет сдвигаться вместе с клипом или перекрываться вышележащими клипами). Приятное свойство Флэша - это антиалиасинг проводимых линий. Больше никаких "лесенок"!
Методы программного рисования прямых во Флэше мало чем отличаются от таковых в других языках программирования. Поэтому начнем сразу с примера. Вот как выглядит код, рисующий равносторонний треугольник.
//создаем MovieClip, в котором хотим нарисовать треугольник _root.createEmptyMovieClip("triang_mc", 1); thickness = 1; //толщина линии lineColor = 0xff0000; //цвет линии alpha = 100; //прозрачность линии //далее, передаем эти аргументы в метод, определяющий стиль линии triang_mc.lineStyle(thickness, lineColor, alpha); //далее, определяем координаты вершин Ax = 20; Ay = 100; Bx = 120; By = 100; Cx = 70; Cy = 100*(1-Math.sqrt(3)/2); //и, наконец, собственно рисуем треугольник triang_mc.moveTo(Ax, Ay); //ставим курсор в вершину A //соединяем вершины triang_mc.lineTo(Bx, By); triang_mc.lineTo(Cx, Cy); triang_mc.lineTo(Ax, Ay);10.1.
Обратите внимание, что "холстом" для программно нарисованных объектов является MovieClip (в данном случае triang_mc ). Если в клипе есть объекты, вставленные в design-time, то они будут отображаться поверх нарисованных через ActionScript. Последние будут перемещаться и поворачиваться, сжиматься и растягиваться, менять прозрачность и цвет вместе с клипом, а также влиять на _width и _height.
На _width и _height хотелось бы остановиться подробнее. В редакторе FlashMX с помощью панелей Properties или Info мы можем достаточно точно установить размеры объектов. И в праве ожидать, что те же значения мы получим, спрашивая у объекта значения _width и _height в ActionScript. К сожалению, это не всегда так. Например, создайте клип, содержащий одну горизонтальную линию длиной 200 пикселей, толщиной 10. В панели Properties или Info вы увидите, что ширина этого клипа ровно 200.0 пикселей, а высота - 0.0 (рис. 10.2 а). В размерах, которые вы задаете в редакторе, толщина линий не учитывается. То же самое можете проделать программно (рис. 10.2 б). А теперь давайте посмотрим, как это выглядит в run-time (рис. 10.2 в). Вы видите разницу между двумя черными линиями, одна из которых нарисована в design-time, другая - через ActionScript? Я тоже не вижу. А вот flash-player видит. При измерении свойств _width и _height учитывается конечная толщина линий. При этом для объектов, вставленных в design-time и для нарисованных в ActionScript, учитывается по-разному (см. окно Output на рис. 10.2 б). Во втором случае к "номинальным" размерам прибавляется с обеих сторон толщина линии, в первом - половина толщины линии. Так что это необходимо учитывать, если нужно знать точное соответствие размеров объекта в design-time и в run-time (для чего это может понадобиться - см., например, гл. 11). Однако, никто не может поручиться, что абсолютно все плееры ведут себя в этом отношении одинаково (а особенно - что так же будут вести себя новые версии плеера). Гораздо более надежно в этих случаях пользоваться формами, или, что то же самое, заливками ( shapes ). Об этом читайте ниже.
а) Клип, содержащий линию и его свойства в design-time ;
б) код, создающий новый клип и рисующий в нем линию, а также выводящий в Output ширину и высоту этого клипа, и из пункта а)1Объекты из пунктов а и б на первый взгляд могут показаться частями одного скриншота. Но на самом деле это монтаж, от редактора Flash MX такого добиться невозможно: если на сцене выделен какой-то объект, то в панели Actions показывается код, относящийся к этому объекту. Однако на рисунке в панели Actions отображен код, относящийся к кадру.
в) отображение клипа из пункта а) и результат выполнения кода из пункта б) в run-time и окно Output.
В примере 10.1 использовались методы lineStyle, moveTo и lineTo. Если добавить к ним еще метод clear (который, во-первых, стирает все программно нарисованное, а также сбрасывает настройки, заданные методом lineStyle ), то этого будет уже достаточно, чтобы рисовать довольно сложные объекты. Такие, как проекция гиперболоида вращения (пример 10.2), или бегающая ломаная (пример 10.3), парабола (пример 10.4) или спираль (пример 10.5).
linesNum = 150; lineRot = 0.35; xCenter = 275; HalfWidth = 250; MinY = 0; MaxY = 400; for (var i = 0; i < linesNum; i++){ x1 = xCenter + HalfWidth*Math.sin(2*Math.PI * i / linesNum); x2 = xCenter + HalfWidth*Math.sin(2*Math.PI * (i + lineRot*linesNum) / linesNum ); hyper_mc.moveTo(x1, MinY); hyper_mc.lineTo(x2, MaxY); } pointsNum = 6; avSpeed = 20; globalWidth = 550; globalHeight = 400; _root.createEmptyMovieClip("lines_mc", 1); points = new Array(); for (var i = 0; i < pointsNum; i++){ points.push({x: Math.random()*globalWidth, y: Math.random()*globalHeight, vx: 2*avSpeed*(Math.random() - 0.5), vy: 2*avSpeed*(Math.random() - 0.5)}); }10.2.
lines_mc.onEnterFrame = function(){ for (var i = 0; i < points.length;i++){ points[i].x += points[i].vx; points[i].y += points[i].vy; xbound = null; if (points[i].x < 0) xbound = 0; if (points[i].x > globalWidth) xbound = globalWidth; if (xbound != null){ points[i].x = xbound; points[i].vx = -points[i].vx } ybound = null; if (points[i].y < 0) ybound = 0; if (points[i].y > globalHeight) ybound = globalHeight; if (ybound != null){ points[i].y = ybound; points[i].vy = -points[i].vy } } this.clear(); this.lineStyle(1, 0xff0000, 100); this.moveTo(points[i-1].x, points[i-1].y); for (var i=0; i< points.length; i++) this.lineTo(points[i].x, points[i].y); }10.3.
_root.createEmptyMovieClip("curve_mc", 2); curve_mc.lineStyle(1, 0x00cc00, 100); var xControl = 50; var yControl = 100; var xTarget = 70; var yTarget = 30; for (var tau=0; tau <=1; tau+=0.01) curve_mc.lineTo(2*xControl*tau*(1-tau) + xTarget*tau*tau, 2*yControl*tau*(1-tau) + yTarget*tau*tau);10.4.
_root.createEmptyMovieClip("spir_mc", 1); spir_mc._x = 275; spir_mc._y = 200; spir_mc.lineStyle(2, 0xcc00ff, 100); for (var t=0; t<500; t++){ spir_mc.lineTo(0.25*t*Math.cos(0.1*t), 0.25*t*Math.sin(0.1*t)); } spir_mc.moveTo(0, 0); for (var t=0; t<500; t++){ spir_mc.lineTo(-0.25*t*Math.cos(0.1*t), -0.25*t*Math.sin(0.1*t)); }10.5.