voliuf.narod.ru

главная

друзья

помощь сайту

Flash MX Studio

Бесплатные учебники по темам

Партнерская программа

1.Представление сайта

2.Форматирование текста

3.Рисование API

4.Компоненты

5.Движемся дальше

6.Интерфейсы AсtionScript

7.Обнаружение коллизий

8.Математика и физика Flash

9.Анимация и интерактивность Drawing API

10.Реализация 3D-графики с помощью рисования API

11.Изучение SphereCage

12.Сайты с мультимедийным содержимым

13.Видеоданные

14.Аудио

15.Интеграция динамических данных

16.Динамический Flash: PHP

17.XML

18.Flash, ColdFusion и Remoting


 


Flash MX Studio
3.Рисование API
  
 
Внимание! Для работы с этой лекцией необходимы учебные файлы, которые Вы можете загрузить  здесь.

В этой лекции мы будем рассматривать прорисовку API - новую возможность Flash MX, позволяющую создавать новое графическое содержимое "на лету". Мы начнем с описания основных принципов и методов работы, а затем рассмотрим, как можно расширить возможности рисования API, например, написав дополнительные методы для рисования квадратов.

 

Прежде всего, мы рассмотрим следующие случаи применения API-рисования.

 
  • Рисование прямых и кривых линий.
  • Рисование кривых линий через определенную точку.
  • Заливка области сплошным цветом или градиентом.
  • Использование общих объектов для создания альбома, в котором можно локально сохранить изображения.
 

Рисование прямой линии

Для рисования прямой линии с помощью API-рисования необходимо знать следующее.

 
  1. У каждого фильма есть текущая позиция рисования, которая может быть установлена с помощью функции moveTo. Каждый фильм инициализируется в позиции рисования по умолчанию (0,0).
  2. Каждый фильм имеет параметр lineStyle, который определяет внешний вид всех линий, имеющихся в фильме. Если не настраивать параметр lineStyle, ни одна нарисованная нами линия не будет отображаться. (Иногда это бывает полезным, как мы убедимся далее при добавлении заливки.)

    Итак, чтобы нарисовать прямую линию, откройте панель Actions. Введите следующий код в кадр 1 вашего нового фильма и проверьте его работу.

     
    this.lineStyle (3, 0x000000);
    this.lineTo (200, 100);

     


     

     

    Это действие нарисует линию из левого верхнего угла рабочего места до позиции (200, 100). С помощью функции lineStyle мы настраиваем линию на ширину в три пикселя и черный цвет. Вы также можете добавить необязательный третий аргумент для установки интенсивности линии. По умолчанию это значение равно 100. Если не указать цвет линии, она будет нарисована черным цветом.

     

    Если линия должна начинаться из какого-либо другого места, нужно использовать функцию moveTo:

     
    this.lineStyle (3, 0x000000);
    this.moveTo (200, 0);
    this.lineTo (200, 100);

     


     

     

    moveTo перемещает точку начала рисования без прорисовки линии, а lineTo рисует линию и перемещает точку рисования.

     
    this.lineStyle (3, 0x000000);
    this.moveTo (200, 0);
    this.lineTo (200, 100);
    this.lineTo (400, 100);

     


     

     

    Это все, что нужно для рисования прямой линии. Функцию lineTo мы позже будем использовать для определения края заполненной области.

     
 

Рисование кривой линии

Кривая линия рисуется аналогичным образом, однако здесь уже требуется указывать два набора точек.

 
this.curveTo (controlX, controlY, anchorX, anchorY);

Линия будет нарисована из текущей точки рисования в точку anchorX, anchorY со значением controlX и controlY, определяющим изгиб линии. Чтобы разобраться, каким образом значения controlX и controlY влияют на кривизну, нужно представить себе третью точку как своего рода магнит, "сгибающий" линию по направлению к себе. Попробуйте выполнить следующий код.

 
this.lineStyle (3, 0x000000);
this.moveTo (50, 50);
this.curveTo (100, 0, 250, 250);

 


 

 

Затем сравните его с результатом работы этой программы.

 
this.lineStyle (3, 0x000000);
this.moveTo (50, 50);
this.curveTo (200, 300, 250, 250);

 


 

 

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

 
  1. Откройте новый файл Flash и нарисуйте простую фигуру, например, окружность. Затем преобразуйте ее в фильм с центральной точкой закрепления и назовите его point.
  2. Выберите фильм в библиотеке Library и щелкните на белом значке меню. В меню выберите команду Linkage:, чтобы открыть диалоговое окно Linkage Properties. Отметьте опцию Export for ActionScript и в поле Identifier введите point.

     


     

     
  3. Удалите ваш инстанс point с рабочего места и добавьте в главный слой следующий код.
    _quality = "LOW";
    this.attachMovie("point", "start_mc", 1);
    this.attachMovie("point", "end_mc", 2);
    this.attachMovie("point", "control_mc", 3);

    Вы создали три точки на рабочем месте с именами start_mc, end_mc и control_mc. Теперь нам нужно настроить каждую точку таким образом, чтобы их можно было перетаскивать в любое место и перерисовывать кривую согласно их новым позициям.

     
  4. Нам нужна функция onPress для начала перетаскивания, функция onRelease для завершения перетаскивания и функция onMouseMove для выполнения перетаскивания и перерисовки кривой линии. Добавьте следующий ActionScript под только что введенным кодом.
    function dragOn() {
    // sets the mousemove handler of the movieclip to 'dragMe'
      this.onMouseMove = dragMe;
    }
    function dragOff() {
    // clears the mousemove handler of the movieclip.
      delete this.onMouseMove;
    }
    function dragMe() {
    // moves the movieclip to the position of the mouse
      this._x = _root._xmouse;
      this._y = _root._ymouse;
    // calls the redraw function in the parent clip ie the _root
      this._parent.reDraw();
    }
  5. Есть два варианта присвоения этих функций управляющим элементам наших фильмов. Вот самый очевидный выбор.
    start_mc.onPress = dragOn;
    end_mc.onPress = dragOn;
    control_mc.onPress = dragOn;

    Однако существует способ, который в данном случае подойдет нам больше. Во Flash MX метод attachMovie имеет полезное свойство, которое позволяет нам передавать объект при добавлении нового фильма. При добавлении фильма все параметры этого объекта копируются в новый фильм.

     
  6. Создадим объект, содержащий всю информацию для onPress, onRelease и onMouseMove, которую мы будем передавать в функцию attachMovie, после чего все присвоения будут производиться без нашего участия. Мы передаем объект в attachMovie в виде четвертого аргумента. Ниже жирным цветом выделен добавленный код.
    _quality = "LOW";
    // set up an init object
    initObj = {onPress:dragOn, onReiease:dragOff, onReleaseOutside:dragOff,
                   useHandCursor:false};
    // pass the init object when we call attachMovie
    this.attachMovie("point" , "start_mc", 1, initObj);
    this.attachMovie("point", "end_mc", 2, initObj);
    this.attachMovie("point", "control_mc", 3, initObj);

    Выполнив этот код, вы сможете перетаскивать ваши фильмы.

     

     


     

     
  7. Теперь надо определить функцию для рисования кривой. Прежде всего, нужно вызвать this.clear () для удаления уже имеющихся в текущем месте линий или заполнений. Далее нужно установить lineStyle на черный цвет и нарисовать кривую из точки start_mc в точку end_mc через точку control_mc, т.е. контрольную точку кривой. Наконец, мы нарисуем красную линию с интенсивностью 30 из точки end_mc в точку control_mc, и затем из control_mc в start_mc.
    function redraw() {
      this.clear();
      this.lineStyle(2, 0x000000);
      this.moveTo(start_mc._x, start_mc._y);
      this.curveTo(control_mc._x, control_mc._y, end_mc._x, end_mc._y);
      this.lineStyle(2, 0xFF5555, 30);
      this.lineTo(control_mc._x, control_mc._y);
      this.lineTo(start_mc._x, start_mc._y);
    }
  8. Сохраните ваш фильм в файле Curve with anchor.fla и запустите его.

     


     

     
  9. Теперь вы можете наблюдать эффект влияния контрольной точки на траекторию кривой. Мы вернемся к точным характеристикам кривой после знакомсвтва с заливками и способами провести кривую через определенную точку.
 

Добавление заливки

Нарисовав набор линий, создающий путь, мы тем самым создадим заливку. Основным способом создания заливки является вызов функции beginFill и последующее рисование линий. Имейте в виду, что lineStyle может быть не указан. Тогда заливка будет прорисована без рамки. Ниже приведен ActionScript для заливки квадрата.

 
this.moveTo(100, 100);
this.beginFill(0xFF0000);
this.lineTo(200,  100);
this.lineTo(200,  200);
this.lineTo(100,  200);
this.lineTo(100,  100);
this.endFill();

 


 

 

Мы можем применить заливку, добавив вызов lineStyle:

 
this.moveTo(100, 100);
this.lineStyle(2, 0x000000);
this.beginFill(0xFF0000);
this.lineTo(200, 100);
this.lineTo(200, 200);
this.lineTo(100, 200);
this.lineTo(100, 100);
this.endFill();

 


 

 

В словаре ActionScript такая возможность не оговорена, но вы можете обнулить все установки lineStyle, один раз вызвав lineStyle без параметров. Итак, мы можем залить половину рамки.

 
this.moveTo(100, 100);
this.lineStyle(2, 0x000000);
this.beginFill(0xFF0000);
this.lineTo(200, 100);
this.lineTo(200, 200);
this.lineStyle();
this.lineTo(100, 200);
this.lineTo(100, 100);
this.endFill();

 


 

 

Таким образом, функции lineTo возвращают точки начала рисования на их исходные позиции. Здесь endFill в любом случае закрывает путь и проводит прямую линию обратно в начальную точку. Можно убрать строку кода, и будет по-прежнему выводиться красный квадрат.

 
this.moveTo(100, 100);
this.beginFill(0xFF0000);
this.lineTo(200, 100);
this.lineTo(200, 200);
this.lineTo(100, 200);
this.endFill();

Применив заливку к curveTo, можно рисовать квадрат со сглаженными углами, как показано ниже:

 
this.moveTo(100, 100);
this.beginFill(0xFF0000);
this.curveTo(150, 50, 200, 100);
this.curveTo(250, 150, 200, 200);
this.curveTo(150, 250, 100, 200);
this.curveTo(50, 150, 100, 100);
this.endFill();

 


 

 

Градиентная заливка

Прежде чем рассматривать градиентную заливку, мы вкратце рассмотрим один из способов использования функции beginGradientFill. Функция beginGradientFill должна иметь ряд параметров: необходимо передавать ей массивы, содержащие различные используемые цвета, интенсивности цветов, степени цветов, а также массив, содержащий значения для координат X и Y, наряду с шириной и высотой градиента. Вот как выглядит функция beginGradientFill в словаре ActionScript:

 
myMovieClip.beginGradientFill(fillType, colors, alphas, ratios, matrix);

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

 

При более простом способе передаваемый нами матричный объект содержит ширину, высоту, начальные координаты X и Y и значение поворота. В нем также содержится конечный параметр matrixType, который должен быть всегда установлен на значение box. При определении ширины и высоты градиента указывается область, к которой применяется градиент. Например, если мы определим линейный градиент с X=100, Y=100, шириной, равной 100 и высотой 100, градиент будет применен к области с 100 до 200 по оси X. Левая и правая области рядом с градиентом будут отображаться просто сплошным цветом.

 

Если вы посмотрите на следующий код, позиция X градиента равна 120, а его ширина равна 60. Рисуемая фигура отображается с позиции 100 по 200 по оси X, при этом со 100 по 120 имеет место сплошной розовый цвет, а с позиции 180 по 200 - сплошной оранжевый.

 
this.moveTo(100, 100);
colors = [0xFFE4E1, 0xFF6600];
alphas = [100, 100];
ratios = [23, 255];
matrix = {matrixType:"box", x:120, y:30, w:60, h:10, r:0};
beginGradientFill("linear", colors, alphas, ratios, matrix);
this.curveTo(150, 50, 200, 100);
this.curveTo(250, 150, 200, 200);
this.curveTo(150, 250, 100, 200);
this.curveTo(50, 150, 100, 100);
this.endFill() ;

Чтобы лучше разобраться, как градиент заполняет область, можно поэкспериментировать с различными значениями и посмотреть, как они влияют на результат. Например, в строке matrix в ActionScript выше приравняйте r к Math.PI. Вы увидите, что градиент развернется на 180 градусов (pi радиан).

 

 


 

 

Также интересно поменять местами X и Y. Здесь мы переместили радиальное заполнение на край нашей фигуры.

 
this.moveTo(100, 100);
colors = [0xFFE4E1, 0xff6600];
alphas = [100, 100];
ratios - [23, 255];
matrix = {matrixType:"box", x:100, y:30, w:100, h:100, r:1};
beginGradientFill("radial", colors, alphas, ratios, matrix);
this.curveTo(150, 50, 200, 100);
this.curveTo(250, 150, 200, 200);
this.curveTo(150, 250, 100, 200);
this.curveTo(50, 150, 100, 100);
this.endFill();

 


 

 

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

 

Функция drawSquare

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

 

При создании таких функций первое, что нужно сделать - это выделить все параметры, которые нам могут понадобиться. Нам нужны координаты X и Y, ширина, высота, цвет заливки или ее интенсивность, цвет линий, ширина и интенсивность.

 
MovieClip.prototype.drawSquare = function(x, y, w, h, fillCol,
                fillAlpha, lineCol, lineWeight, lineAlpha) {

};

Порядок расположения параметров в функции очень важен. Например, нам нужно просто отобразить квадрат без линии вокруг его края. Вместо создания отдельной функции для рисования квадрата без рамки мы можем передать все параметры нового стиля в конце определения нашей функции и пропускать их при вызове функции. В этом случае значения lineCol, lineWeight и lineAlpha будут отображаться как "undefined", и рамка не будет прорисовываться.

 

Внутри функции мы, во-первых, должны установить lineStyle и цвет заливки, а также переместить точку рисования на позицию (X, Y), которую мы указали.

 
MovieClip.prototype.drawSquare = function(x, y, w, h, fillCol,
 fillAlpha, lineCol, lineWeight, lineAlpha) {
  this.moveTo(x, y);
  // check if a line colour has been specified
  if (lineCol) {
    this.lineStyle(lineWeight, lineCol, lineAlpha);
  } else {
    this.lineStyle();
  }
  this.beginFill(fillCol, fillAlpha);
};

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

 

Другим моментом, на котором основывается наше решение, является то, передает ли пользователь аргумент lineAlpha (будет ли lineAlpha по умолчанию нулем или нет). Мы, конечно, мы могли бы оградиться от этого и изменить функцию по умолчанию на 100, если lineCol передается, а lineAlpha - нет. Осталось проверить, определена ли lineAlpha, и предпринять соответствующие действия.

 
MovieClip.prototype.drawSquare = function(x, y, w, h, fillCol,
КfillAlpha, lineCol, lineWeight, lineAlpha) {
  this.moveTo(x, y) ;
  if (lineCol != undefined){
    if (lineAlpha == undefined) {
      lineAlpha = 100;
    }
    this.lineStyle(lineWeight, lineCol, lineAlpha);
  } else {
    this.lineStyle ();
  }
  this.beginFill(fillCol, fillAlpha);
};

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

 
  • Левая верхняя: (X, Y)
  • Правая верхняя: (X + ширина, Y)
  • Правая нижняя: (X + ширина, Y + высота)
  • Левая нижняя: (X, Y + высота)
 

Теперь нам просто нужно нарисовать линию для каждой из этих точек по очереди и затем завершить заполнение.

 
MovieClip.prototype.drawSquare = function(x, y, w, h, fillCol,
КfillAlpha, lineCol, lineWeight, lineAlpha) {
  this.moveTo(x, y);
  if (lineCol != undefined) {
    if (lineAlpha == undefined) {
      lineAlpha = 100;
    }
    this.lineStyle(lineWeight, lineCol, lineAlpha);
  } else {
    this.lineStyle();
  }
  this.beginFill(fillCol, fillAlpha);
  // top right
  this.lineTo(x+w, y);
  // bottom right
  this.lineTo(x+w, y+h);
  // bottom left
  this.lineTo(x, y+h);
  // top left.
  this.lineTo(x, y);
  // end the fill
  this.endFill();
};

Наконец, нам нужно вызвать

 
_root.drawSquare (100, 100, 200, 100, 0xFF0000, 100, 0xdddddd, 20, 50) ;

Вы заметите, что рамка, созданная с помощью lineTo, выровнена по центру края квадрата, и если бы у нашей рамки была толщина в 20 пикселей, квадрат был бы на десять пикселей шире с каждой стороны. Углы рамки закруглены, что может быть излишним. Чтобы избежать этого, можно изменить метод так, чтобы сначала прорисовывался квадрат для рамки, а затем уже квадрат поверх него для заливки.

 

В конечном счете, настройка метода определяется тем, как вы собираетесь его использовать. Например, вы можете указывать значения для левого, правого, нижнего и верхнего краев вместо X, Y, ширины и высоты (если бы нужно было использовать его со значениями, возвращаемыми функцией Movieclip.getBounds). Это несложно.

 
MovieClip.prototype.drawSquare = function(left, right, top, bottom,
КfillCol, fillAlpha, lineCol, lineWeight, lineAlpha) {
  this.moveTo(left, top);
  if (lineCol != undefined) {
    if (lineAlpha == undefined) {
      lineAlpha = 100;
    }
    this.lineStyle(lineWeight, lineCol, lineAlpha);
  } else {
    this.lineStyle();
  }
  this.beginFill(fillCol, fillAlpha);
  this.lineTo(right, top);
  // top right
  this.lineTo(right, bottom);
  // bottom right
  this.lineTo(left, bottom);
  // bottom left
  this.lineTo(left, top);
  // top left
  this.endFill();
};

И далее вызов.

 
// a square with left edge at 100, right edge at 200, top at
// 200 and bottom at 300:
this.drawSquare(100,200,200,300,0xff0000,100)

Можно применять другие цвета, интенсивность заполнения и т.д. - все зависит от того, как вы планируете использовать данный метод. Здесь нет правильного или неправильного пути, вы просто конструируете его под ваши нужды.

 

Функция рисования окружности

Теперь мы рассмотрим метод, который позволяет рисовать окружности с помощью API- рисования. Он более "хитрый". Многие методы рисования окружностей зависят от использования curveTo для формирования круга посредством рисования дуг. Этот же метод основан на принципе закругления концов линии. Если нарисовать линию, имеющую ширину в 100 пикселей, она может послужить основой для круга радиусом 100; функция Дэна рисует 0,15 пикселей от текущей точки рисования, что формирует прекрасный круг.

 

Параметры, передаваемые функции, являются координатами X и Y центра круга, а cWidth - ширина окружности.

 
function drawCircle(x, y, radius, cWidth) {
  me = _root.createEmptyMovieClip("circle"+cnt,++cnt);
  me.lineStyle(radius, 0x000000, 100);
  me.moveTo(x, y);
  me.lineTo(x, y+.15);
  me.lineStyle(radius-cWidth, 0xffffff, 100);
  me.moveTo(x, y);
  me.lineTo(x, y+.15);
}
drawCircle(100, 100, 100, 20);

 


 

 

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

 

Преобразуем эту функцию так, чтобы она работала как наши предыдущие функции, и затем добавим возможность указания цветов:

 
MovieClip.prototype.drawCircle = function (x, y, radius, cWidth) {
  this.lineStyle(radius, 0x000000, 100);
  this.moveTo(x, y);
  this.lineTo(x, y+.15);
  this.lineStyle(radius-cWidth, 0xffffff, 100);
  this.moveTo(x, y);
  this.lineTo(x, y+.15);
};
drawCircle(100, 100, 100, 20);

Теперь добавим цвета:

 
MovieClip.prototype.drawCircle = function(x, y, radius,
КcWidth, innerCol, outerCol) {
  this.lineStyle(radius, outerCol, 100);
  this.moveTo(x, y);
  this.lineTo(x, y+.15);
  this.lineStyle(radius-cWidth, innerCol, 100);
  this.moveTo(x, y);
  this.lineTo(x, y+.15);
};
drawCircle(100, 100, 100, 10, 0xff0000, 0x000000);

 


 

 

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

 

Рисование кривой через указываемую точку

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

 

Функция curveTo во Flash создает то, что называется квадратным уравнением для кривой Безье. Раньше кривые линии рисовали, проставляя серию точек на кривой, а затем воспризводя линии фильмов между этими точками, что достигалось с помощью уравнения кривой Безье.

 

С помощью этого уравнения указывается конечная точка, точка контроля и начальная точка. Переменной в этом уравнении является t, которая изменяет значения от 0 до 1 от начала кривой и до ее конца. Мы можем вставлять различные значения t для выяснения координат любой отдельной точки на кривой.

 

Квадрат Безье

Таблица 3.1.
x Положение по оси X, через которое проходит кривая за в t
x0 Начальная точка кривой по оси X
x1 Контрольная точка прохождения кривой по оси X
X2 Конечная точка кривой по оси X
y Положение по оси Y, через которое проходит кривая в t
Y0 Начальная точка кривой по оси Y
Y1 Контрольная точка прохождения кривой по оси Y
Y2 Конечная точка кривой по оси Y
t Время
 

Приведем наше уравнение.

 
x = x0*t*t + x1*2*t*(1-t) + x2*(1-t)*(1-t)
y = y0*t*t + y1*2*t*(1-t) + y2*(1-t)*(1-t)

 


 

 

Это уравнение полезно для поиска отдельной точки, через которую проходит кривая, в любой точке t, от 0 до 1, т.е. от начала и до конца кривой. Мы указываем точку начала, точку контроля, конечную точку и t для перехода к позиции (X, Y) (в которой кривая проходит через эту точку).

 

Нам нужно изменить функцию таким образом, чтобы можно было указывать значения для точки начала (x0,y0), конца (x2,y2) и отдельной точки кривой (X, Y) и перейти в нужную точку контроля (x1,y1). Другими словами, нужно переработать функцию так, чтобы она выглядела примерно следующим образом.

 
x1= умноженное на x0, x2, x и t
y1= умноженное на y0, y2, y и t

Это разумное упрощение. Мы рассмотрим его для уравнения для X и затем продублируем наши результаты для Y, так как уравнения идентичны. Прежде всего, нам нужно получить значения x1 и y1 в левой части уравнения. Это мы сделаем взаимным уничтожением с обеих сторон:

 
x - x1*2*t*(1-t) = x0*t*t + x1*2*t*(1-t) + x2*(1-t)*(1-t) - x1*2*t*(1-t)

Значения плюс и минус x1*2*t*(1-t) в правой части взаимно компенсируют друг друга:

 
x - x1*2*t*(1-t) = x0*t*t + x1*2*t*(1-t) + x2*(1-t)*(1-t) х1*2*t(1-t)
x - x1*2*t*(1-t) = x0*t*t + x2*(1-t)*(1-t)

Теперь вычтем x из обеих сторон:

 
x - x1*2*t*(1-t) - x = x0*t*t + x2*(1-t)*(1-t) - x
x -  x1*2*t*(1-t) -x = x0*t*t + x2*(1-t)*(1-t) - x
-x1*2*t*(1-t) = x0*t*t + X2*(1-t)*(1-t) - x
-1*x1*2*t*(1-t) = x0*t*t + x2*(1-t)*(1-t) - x

Умножим -1 на 2 в левой части уравнения:

 
x1* - 2*t*(1-t) = x0*t*t + x2*(1-t)*(1-t) - x

Далее, делим обе части уравнения на (-2*t*(1-t) ):

 
x1*(-2*t*(1-t)) / (-2*t*(1-t)) =
=(x0*t*t + x2*(1-t)*(1-t) - x) / (-2*t*(1-t))

С левой стороны имеем (-2*t*(1-t)) / (-2*t*(1-t)), что дает 1, поэтому можно произвести сокращение:

 
x1 (- 2 * t * (1 - t)) / ( - 2 * t * (1 - t) ) =
=(x0*t*t + x2*(1-t)*(1-t) - x) / (-2*t*(1-t))
x1 = (x0*t*t + x2*(1-t)*(1-t)-x) / (-2*t*(1-t))

Мы получили уравнение для нахождения x1, а уравнением для y1 будет следующее:

 
y1 = (y0*t*t  + y2*(1-t)*(1-t) - y) / (-2*t*(1-t))

Теперь можно использовать полученные результаты для создания функции, которая будет передавать значения x, y, x0, y0, x2, y2 и t для рисования кривой из (x0,y0) в (x2,y2), проходящей через точку (x,y) в точке на прямой, указываемой t.

 

Истинная роль t станет вам понятнее, когда вы начнете вставлять числа, которые будут влиять на форму кривой. Переименуем значения на a, b и c в нашей функции, для простоты. Мы будем вызывать функцию aToBThroughC чтобы выяснить значение каждой из величин. Приведем уравнение с добавленными a, b и c:

 
controlX = (ax*t*t + bx*(1-t)*(1-t) - cx) / (-2*t*(1-t))
controlY = (ay*t*t + by*(1-t)*(1-t) - cy) / (-2*t*(1-t))

Теперь перейдем к самой функции. Для простоты предположим, что мы настроили lineStyle.

 
MovieClip.prototype.aToBThroughC =
 function (ax, ay, bx, by, ex, cy,t) {
  var controlX = (ax*t*t+bx*(1-t)*(1-t)-cx)/(-2*t*(1-t));
  var controlY = (ay*t*t+by*(1-t)*(1-t)-cy)/(-2*t*(1-t));
  this.moveTo(ax, ay);
  // for security
  this.curveTo(controlx, controly, bx, by);
};

Для проверки работы введите следующий код.

 
this.lineStyle (27, 0xff0000);
this.aToBThroughC (100, 100, 250, 100, 200, 350, 0.5);

Сохраните ваш файл под именем AtoBthroughC.fla и запустите его. Мы создали сильно изогнутую кривую

 

 


 

 

Чтобы показать корректность работы нашей функции, мы откроем созданный нами ранее файл curve with anchor.fla, и изменим его так, чтобы третья, перетаскиваемая, точка была точкой, через которую проходит кривая, а не точкой контроля. Измененный код выделен жирным шрифтом:

 
_quality = "LOW";
MovieClip. prototype. aToBThroughC = function (ax, ay, bx, by, ex, cy, t) {
  var controlX = (ax*t*t+bx*(1-t)*(1-t)-cx)/(-2*t*(1-t));
  var controlY = (ay*t*t+by*(1-t)*(1-t)-cy)/(-2*t*(1-t));
  this.moveTo(ax, ay);
  // for security
  this.curveTo(controlx, controly, bx, by);
};
initObj = {onPress:dragOn, onRelease:dragOff,onReleaseOutside:dragOff,
КuseHandCursor: false};
this.attachMovie("point", "start_mc", 1, initObj);
this.attachMovie("point", "end_mc" , 2, initObj);
this.attachMovie("point", "control_mc", 3, initObj);
function dragOn() {
  this.onMouseMove = dragMe;
}
function dragOff() {
  delete this.onMouseMove;
}
function dragMe() {
  this._x = _root._xmouse;
  this._y = _root ._ymouse;
  this._parent.reDraw();
}
function reDraw() {
  this.clear();
  this.lineStyle(2, 0xFF5555, 100);
  this.aToBThroughC(start_mc._x, start_mc._y, end_mc._x, end_mc._y,
  Кcontrol_mc._x, control_mc._y, 0.5);
}

Пример 3.1.

Вы увидите, что при перетаскивании точек кривая теперь всегда проходит через третью точку.

 

 


 

 

Задав другое значение t, вы увидите, что кривая изменила форму.

 
this.aToBThroughC(start_mc._x, start_mc._y, end_mc._x, end_mc._y,
Кcontrol_mc._x, control_mc._y, 0.1);
}

Переменная t может иметь любое значение между 0 и 1, поэтому здесь может быть огромное количество кривых, которые будут проходить через нашу точку c - по одной для каждого значения между 0 и 1.

 

Создание простого приложения

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

 
  • Обеспечение пользователю возможности рисования с помощью мыши.
  • Изучение работы общих объектов для хранения информации.
  • Комбинирование первого и второго для возможности сохранения рисунков и открытия их в дальнейшем.
 

Вы не найдете ничего нового в этом приложении, однако оно поможет разобраться вам в методологии и поможет изучить работу общих объектов. Это новая мощная возможность Flash MX.

 

Рисование

Простое приложение для рисования состоит из тридцати строк кода. Вот принцип его работы:

 
  • При щелчке пользователем текущая точка рисования перемещается на позицию мыши. Записываются начальная и текущая точка движения мыши. Далее рисуется линия из точки в первом кадре в текущую точку.
  • Когда пользователь отпускает кнопку мыши, линия завершается, прекращается запись позиции мыши и рисование.
 

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

 
function drawOn() {
  this.lineStyled (0x000000);
  this.moveTo(this._xmouse, this._ymouse);
  this.onMouseMove = addPoint;
}
function addPoint() {
  this.lineTo(this._xmouse, this._ymouse);
}
function drawOff() {
  delete this.onMouseMove;
}
this.onMouseDown = drawOn;
this.onMouseUp = drawOff;

С помощью onMouseDown устанавливается lineStyle и точка рисования перемещается туда, где находится мышь, а также onMouseMove устанавливается в addPoint.

 

В тот момент, когда пользователь двигает мышь, рисуется линия к новой позиции указателя мыши. Когда пользователь отпускает кнопку мыши, управляющий элемент onMouseMove удаляется.

 

Введите код в панель Actions нового фильма, сохраните его в файле doDrawing.fla и запустите его.

 

 


 

 

Мы получили простой интерфейс рисования, состоящий всего лишь из тридцати строк.

 

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

 
function drawOn() {
  this.lineStyle(1, 0x000000);
  this.moveTo(this._xmouse, this._ymouse);
  // add the new point to the array
  tempArr.push((x:_root._xmouse, у:_root._ymouse});
  this.onMouseMove = addPoint;
}
function addPoint() {
  // add the new point to the array
  tempArr.push ({x:_root._xmouse, у:_root._ymouse});
  this.lineTo(_root._xmouse, _root._ymouse);
}
function drawOff() {
  // the pen is lifted so we add the value "break" to the array
  tempArr.push("break");
  delete this.onMouseMove;
}
tempArr = [];
this.onMouseDown = drawOn;
this.onMouseUp = drawOff;

Мы создали массив tempArr для хранения всех координат в объектах с параметрами X и Y.

 

Сохраните ваш фильм в файле doAndStoreDrawing.fla. Если вы запустите фильм и выберете команду меню Debug > List Variables, вы увидите все координаты нарисованных вами точек в окне Output.

 

 


 

 

Перед тем, как рассматривать восстановление рисунка из этих координат, изучим использование общих объектов для хранения переменных.

 

Общие объекты

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

 

Грубо говоря, общие объекты являются своего рода cookies для Flash. Они позволяют нам локально хранить любые данные наших Flash-фильмов, и работают в любых браузерах, равно как и локальный Flash Player.

 

Итак, общие объекты могут быть использованы для хранения данных, представляющих собой рисунок, созданный в приложении при помощи рисования API. Общие объекты могут также использоваться в онлайн-формах для запоминания предыдущих записей пользователей и автозавершения их имен и адресов, и т.д., так можно сделать, например, отдельную страницу на сайте интернет-магазина, основанную на предыдущих покупках пользователя.

 

Использование общего объекта

Мы не будем разбираться в сложных деталях использования общих объектов, мы изучим создание общего объекта и обеспечение его работы. Если вы хотите узнать больше об общих объектах и их применении, вам стоит обратиться к сайту по этой ссылке: www.macromedia.com/support/flash/action_scripts/local_shared_object.

 

Мы начнем с очень простого приложения.

 
  1. Создайте фильм, добавьте на рабочее место статическое текстовое поле и введите в него текст visit number.
  2. Теперь добавьте динамическое текстовое поле и назовите его инстанс именем visited.

     



     

     

    Мы используем общий объект для хранения числа просмотров страницы. Это элементарное приложение, но оно содержит все необходимое для продолжения работы с приложением рисования.

     
  3. Теперь создайте новый слой script для ввода кода в наше элементарное приложение.
  4. Прежде всего, мы получаем sharedObject с помощью SharedObjec.getLocal. Это действие вернет объект, если он уже был создан, а если он еще не создавался, он будет создан. Введите следующий код на панели Actions:
    mySharedObj = SharedObject.getLocal ("visits");

    Теперь мы имеем копию общего объекта в переменной mySharedObj. mySharedObj - это объект, которому вы можете присваивать параметры, как любому другому объекту. Это могут быть переменные, массивы и т.д. Переданный параметр "visits" является именем общего объекта, что позволяет иметь несколько общих объектов для каждого проекта.

     
  5. Теперь мы будем увеличивать переменную visitCount и вставлять ее значение в созданное нами текстовое поле. Добавьте следующие две строки ActionScript.
    mySharedObj = SharedObject.getLocal("visits");
    //we increment a variable "visitCount" inside "data" where
    //all the data resides in a shared object
    mySharedObj.data.visitCount++;
    visited.text = mySharedObj.data.visitCount;
  6. Наконец, мы обновляем объект sharedObject, синхронизируя его, если нужно, с использованием метода flush, который возвращает все из mySharedObj обратно в текущий общий объект. Добавьте последнюю строку сценария в панель Actions.
    mySharedObj = SharedObject.getLocal("visits");
    mySharedObj.data.visitCount++;
    visited.text = mySharedObj.data.visitCount;
    mySharedObj.flush();
  7. Сохраните ваш фильм в файле sharedObj basics.fla. Если вы запустите фильм несколько раз, вы увидите, что число будет увеличиваться (если вы не игнорировали запрос безопасности, который мы будем рассматривать и обрабатывать в реальных условиях).
 

 


 

 

Сохранение данных рисунка

На примере рассмотренного приложения становится ясно, что сохранение рисунков в приложении заключается просто в сохранении массива данных в общем объекте.

 

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

 

Приведем структуру того, что мы хотим сделать:

 

Главная страница:

 

Возможности:

 

Панель, содержащая список рисунков из sharedObject с полосой прокрутки.

 

Опции:

 

Выбрать имеющийся рисунок - перейти к странице перерисовки.

 

Создать новый рисунок - перейти к странице создания.

 

Страница перерисовки:

 

Возможности:

 

Перерисовка прежнего рисунка.

 

Опции:

 

Вернуться на главную страницу.

 

Страница создания:

 

Возможности:

 

Рисование мышью.

 

Опции:

 

Сохранить текущий рисунок - сохранить рисунок в общем объекте - перейти на главную страницу.

 

Вернуться - возврат на главную страницу.

 

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

 
Таблица 3.2.
Имя функции Цель
init Установка некоторых основных управляющих элементов кнопок и вызов drawPanel.
drawPanel Получение данных из объекта sharedObject и отключение кнопки возврата и сохранения. Вызов listDrawings и initScrollers.
listDrawings Добавление текстового поля в меню.
initScrollers Установка полос прокрутки для текстового поля.
initDraw Выключение меню, включение кнопки возврата и сохранения и установка управляющих элементов событий onMouseDown и onMouseUp для реализации рисования. Настройка lineStyle и создание временного массива для хранения точек.
drawOn Начало рисования линии, установка onEnterFrame и addPoint.
addPoint Рисование линии и добавление текущей точки в массив.
drawOff Выключение addPoint.
save Добавление текущего массива точек в sharedObject и вызов backToMain.
backToMain Удаление всех событий кадра и мыши на _root. Вызов drawPanel.
redraw Отключение меню и включение кнопки возврата. Установка onEnterFrame.
drawNextPoint Рисование следующей точки в текущем рисунке.
 

Полнофункциональное приложение рисования

Перейдем к работе над полнофункциональным приложением рисования и сохранения рисунков.

 
  1. Прежде всего займемся основой будущего приложения. Создайте два символа кнопок, которые будут использоваться для сохранения наших рисунков и для возврата к временно сохраненным рисункам. Вы сами теперь можете создать кнопки. На наших кнопках мы просто написали "save" и "return", причем настроили текстовое поле на использование системных шрифтов. Эти кнопки расположены в правом нижнем углу главного рабочего места.

     


     

     
  2. Инстанс кнопки Save назовите saverButton с помощью Property inspector, а инстанс кнопки Return - backButton.
  3. Создайте символ фильма и назовите его listingsPanel. В фильме создайте новый слой с именем textfield и добавьте динамическое текстовое поле на этот слой. С помощью Property inspector назовите текстовое поле lista.

     


     

     
  4. Создайте новый слой с именем background и нарисуйте прямоугольник (светло-серого цвета, #F5F5F5) значительно больший динамического текстового поля, которое вы только что создали. Под ним я нарисовал другой прямоугольник, который намного темнее первого (#E8E8E8), чтобы выделить его. Затем перетащите слой background под слой textfield.

     


     

     
  5. Все еще находясь в фильме listingsPanel, создайте слой с именем new под слоем textField. Внутри слоя new создайте символ кнопки и присвойте ему имя инстанса newButton с помощью Property inspector. Как видите, мы просто добавили необходимый текст в нашу кнопку и поместили его внизу динамического текстового поля.

     


     

     
  6. Создайте два новых слоя с именами upscroll и downscroll. Создайте символ кнопок прокрутки вверх и вниз. Я просто добавил текстовые поля с символами "больше" и "меньше". Разместите символ прокрутки вверх на слое upscroll и с помощью Property inspector дайте ему имя инстанса upScroll. Расположите второй символ кнопки на слое downscroll и назовите его инстанс именем downScroll. Здесь вполне понятно, какие действия выполняют эти кнопки. Мы расположили наши кнопки под текстовым полем слева от кнопки new.

     


     

     

    Можно было бы использовать для этого программный компонент прокрутки, но компоненты мы будем рассматривать в следующей лекции и пока не будем забегать вперед. Создание фильма почти завершено, осталось только добавить в него заголовок. Мы создали новый слой с именем title и добавили статическое текстовое поле со словами "List of drawings".

     

     


     

     
 
  1. Мы закончили создание фильма. Теперь перетащите его на рабочее место и назовите инстанс listingsPanel.

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

     
    function drawPanel() {
      // turn off the back button and the save button
      backButton._visible = false;
      saverButton._visible = false;
      // make the menu panel visible
      this.listingsPanel._visible = true;
      // retrieve the shared object
      myPictureObj = SharedObject.getLocal("pictures");
      // list any drawings
      listDrawings(myPictureObj.data.arrays);
      // check if we need to turn on the scrollers
      initScrollers();
    }

    Здесь мы инициализировали общий объект и сохранили его в myPictureObj, а также настроили отображение некоторых элементов рабочего места. Далее мы вызовем listDrawings для просмотра myPictureObj и выяснения количества находящихся там рисунков. Мы запишем наши массивы точек в массив с именем arrays объекта sharedObject и затем передадим этот массив функции listDrawings. Внутри массива arrays будет находиться один массив для каждого рисунка. Вместо создания кнопки для каждого рисунка мы используем функцию asfunction, позволяющую представлять фрагмент текста HTML в виде ссылки на функцию ActionScript. Вот как это делается.

     
    asfunction:functionName, parameter;
  2. Мы будем использовать эту функцию для вызова _root.redraw и передачи номера записи массива.
    function listDrawings(pictureArray) {
      listingsPanel.lista.htmlText = "";
      // loop through the picture array
      for (var i = 0; i<pictureArray.length; i++) {
      // for each entry in the picture array add another line
      // to the text in the textfield
      var nexta = "<A HREF='asfunction:_root.redraw,"+i+"'>"+"
       Drawing Number "+(i+1)+"</A><b/>";
      listingsPanel.lista.htmlText += nexta;
      }
    }
  3. Теперь создадим вторую функцию, которая будет вызываться drawPanel. Она просто настроит отображение кнопок прокрутки в зависимости от того, где мы находимся - вверху или внизу текстового поля, а также от того, может ли поле вместить все содержащиеся в нем строки текста. С помощью with мы перейдем внутрь фильма listingsPanel. Параметр visible инстанса кнопки downScroll устанавливаем на значение on, если значение прокрутки меньше, чем максимально возможное значение прокрутки. Этот же параметр устанавливается на значение on, если текстовое поле было полностью прокручено.
    function initScrollers() {
      with (this.listingsPanel) {
        downScroll._visible = lista.scroll<lista.maxscroll;
        upScroll._visible = lista.scroll>l;
      }
    }

    Наш список будет выглядеть примерно так.

     

     


     

     
  4. Теперь добавим уже знакомые вам функции для рисования. Сначала добавим initDraw - функцию, которая будет вызываться при щелчке пользователя на кнопке new:
    function initDraw() {
      // turn on the back and save buttons
      backButton._visible = true;
      saverButton._visible = true;
      // turn off the menu
      _root.listingsPanel._visible = false;
      // create an array for the points
      tempArr = [];
      _root.lineStyle(1, 0x000000);
      // set up out mouse event handlers
      _root.onMouseDown = drawOn;
      _root.onMouseUp = drawOff;
    }

    Все это вам уже знакомо: сначала настраивается отображение элементов рабочего места так, чтобы были видны кнопки back и save, затем настраивается lineStyle и создается массив для наших точек.

     
    function drawOn() {
      this.moveTo(this._xmouse, this._ymouse);
      tempArr.push({x:this._xmouse, y:this._ymouse});
      this.onMouseMove = addPoint;
    }
    function addPoint() {
      tempArr.push((x:this._xmouse, y:this._ymouse});
      this.lineTo(this._xmouse, this._ymouse);
    }
    function drawOff() {
      tempArr.push("break");
      delete this.onMouseMove;
    }
  5. Затем создадим функцию, позволяющую сохранять рисунок. Она будет вызываться при нажатии кнопки save и будет сохранять tempArr в общем объекте. Перед сохранением массива мы удаляем последний объект, так как объект точки записывается в массив, когда пользователь нажимает кнопку для ее сохранения (когда происходит onMouseDown). Если не делать этого, можно столкнуться с проблемами потерянной строки в конце рисунка.
    function save() {
      // remove final element
      tempArr.pop();
      if (!myPictureObj.data.arrays) {
      // if our array for pictures has not yet been created,
      // create it
      myPictureObj.data.arrays = [];
      }
      // put the current picture array into the array
      // containing all pictures
      myPictureObj.data.arrays.push(tempArr);
      // update the shared object
      myPictureObj.flush();
      // return to the menu screen
      _root.backToMain();
    }

    Обратите внимание, что здесь сначала проверяется существование массива arrays, и если массив не существует, то он создается. Затем tempArr помещается в конец этого массива и вызывается flush для сохранения его в объекте. После этого вызывается backToMain для удаления всех управляющих элементов событий, а также весь рисунок в фильме, перед возвращением к началу.

     
    function backToMain() {
      delete _root.onEnterFrame;
      delete _root.onMouseMove;
      delete _root.onMouseDown;
      delete _root.onMouseUp;
      _root.clear();
      _root.drawPanel();
    }
  6. Теперь приступим к созданию последней части - функции перерисовки. Здесь нам нужны две функции: redraw для начала перерисовки и drawNextPoint для покадрового продолжения. Перерисовка осуществляется посредством прохода через массив точек и рисования линии от предыдущей точки к текущей в каждом кадре. С помощью break осуществляется переход к точке без рисования линии.
    function redraw(num) {
      backButton._visible = true;
      this.listingsPanel._visible = false;
      // set count to zero, used to keep track of current point
      this.count = 0;
      // the array which we are redrawing
      this.currentArr = myPictureObj.data.arrays[num];
      this.lineStyled (0x000000);
      this.moveTo( this, currentArr [0] .x, this .currentArr [0] .y);
      // every frame we draw the next point
      this.onEnterFrame = drawNextPoint;
    }
  7. Это начальная функция, получающая массив нужного рисунка и перемещающая точку рисования на первую позицию в массиве. Она сбрасывает переменную count, которая будет использоваться для учета точки выхода из массива, и затем настраивает функцию enterFrame на drawNextPoint.
    function drawNextPoint() {
      this.count++;
      // nextEntry is the next point object in the array
      var nextEntry = this.currentArr[this.count];
      if (nextEntry != "break") {
        this.lineTo(nextEntry.x, nextEntry.y);
      } else {
        // if the nextEntry is "break", we increment count
        // and move to the next point.
        this.count++;
        var nextEntry = this.currentArr[this.count];
        this.moveTo(nextEntry.x, nextEntry.y);
      }
      if (this.count>this.currentArr.length) {
        // we have drawn all available points
        delete this.onEnterFrame;
      }
    }

    Таким образом, с помощью drawNextPoint мы увеличиваем значение нашего счетчика и устанавливаем переменную nextEntry для указания на следующую запись в массиве. Мы проверяем, равно ли это значение "break" и, если это так, увеличиваем count и переходим к следующей точке в массиве с помощью moveTo. Если оно не равно "break", рисуем линию к новой точке. Наконец, проверяем, достигнут ли конец массива; если да, удаляем функцию onEnterFrame и завершаем выполнение действия.

     
  8. Последняя - это функция инициализации, которая будет просто устанавливать управляющие элементы для кнопок, настраивать textField в listingsPanel на HTML-текст и рисовать панель в первый раз.
    function init() {
      this.saverButton.onPress = this.save;
      this.backButton.onPress = this.backToMain;
      with (this.listingsPanel) {
        newButton.onRelease = this.initDraw;
        // enable html text in the textfield
        lista.html = true;
        downScroll.onPress = function() {
          // scroll the textfield down
          lista.scroll++;
          // check if the scrollers should still appear
          _root.initScrollers();
        };
        upScroll.onPress = function() {
          lista.scroll--;
          _root.initScrollers();
        };
      }
      drawPanel();
    }
  9. Нам также нужен вызов этой функции для инициализации приложения
    init ()

Весь код программы находится в файле drawingapp.fla.

 

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

 

источник: http://www.INTUIT.ru 


 

13 центов(0,13$) за клик, выплаты через WebMoney каждый вторник +10% с рефералов

Мы выкупаем 100% трафа! $12 за 1000 хостов (РФ), и до $4 за 1000 хостов (зарубежный траф) + 10% с дохода Ваших рефералов!
 Выплаты через
WebMoney

~80-100$ за1000 хостов 2.5$ за 1 смс.
реф. процент - 10 %Выплаты происходят раз в неделю, в четверг на
WebMoney
 
 

 

____________________________

Посмотреть порно видео в онлайне »

_______________________________

 

   
   
Сайт управляется системой uCoz