company_banner

Делаем поздравительную открытку к 8 марта на HTML5 и EaselJS



    (картинка кликабельна и поздравительна)

    Общая идея: летающие бабочки на фоне красивой картинки и под весеннюю музыку. (Сразу признаюсь, у меня также была Silverlight-версия открытки, которую я делал год назад. Урок анимации бабочек в Silverlight.)

    Для отрисовки открытки, помимо стандартных средств CSS, будем использовать HTML5 Canvas и библиотеку анимации EaselJS.

    Анимация бабочек



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



    Шаг 1. Отрисовка крыльев

    Давайте для начала нарисуем просто пару крыльев. Для отрисовки каждого из крыльев нам понадобится класс Bitmap, объекты которого мы будем создавать на основе загруженных изображений. Чтобы собрать крылья воедино, нам понадобится класс Container. Для отрисовки сцены в EaselJS существует класс Stage.

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

    // loading left wing
    leftImg = new Image();
    leftImg.src = leftSrc;
    leftImg.onload = onWingImageLoaded;

    // loading right wing
    rightImg = new Image();
    rightImg.src = rightSrc;
    rightImg.onload = onWingImageLoaded;


    (В идеале, надо также вешать события на ошибку в загрузке и ее корректно обрабатывать.)

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

    // create a new container for butterfly wings
    var butterfly = new Container();
    // create wings
    var lWing = new Bitmap(leftImg);
    var rWing = new Bitmap(rightImg);

    // assemble a batterfly
    butterfly.addChild(lWing);
    butterfly.addChild(rWing);


    Внутри контейнера для каждого из крыльев нужно подвинуть точку регистрации, относительно которой они будут позиционироваться и далее вращаться:

    // change wings registration point inside container
    lWing.regX = lWing.image.width;
    lWing.regY = lWing.image.height / 2;
    rWing.regX = 0;
    rWing.regY = rWing.image.height / 2;


    Размещение бабочки
    Далее добавляем бабочку на сцену, размещаем в нужном месте и запускаем отрисовку сцены:

    // add butterfly to stage
    stage.addChild(butterfly);

    // change batterfly position
    butterfly.x = canvas.width * Math.random() | 0;
    butterfly.y = canvas.height * Math.random() | 0;
          
    // redraw stage
    stage.update();


    Общий код
    var leftSrc = "../images/Cypres 1.png";
    var rightSrc = "../images/Cypres 2.png";
    var leftImg;
    var rightImg;
    var loadedWings = 0;

    function init() {
      // create a new stage and point it at our canvas:
      canvas = document.getElementById("canvas");
      stage = new Stage(canvas);

      // loading left wing
      leftImg = new Image();
      leftImg.src = leftSrc;
      leftImg.onload = onWingImageLoaded;

      // loading right wing
      rightImg = new Image();
      rightImg.src = rightSrc;
      rightImg.onload = onWingImageLoaded;
    }

    function onWingImageLoaded(event) {
      loadedWings++;
      if (loadedWings == 2)
        onButterflyReady();
    }

    function onButterflyReady() {
      // create a new container for butterfly wings
      var butterfly = new Container();
      // create wings
      var lWing = new Bitmap(leftImg);
      var rWing = new Bitmap(rightImg);

      // assemble a batterfly
      butterfly.addChild(lWing);
      butterfly.addChild(rWing);

      // change wings registration point inside container
      lWing.regX = lWing.image.width;
      lWing.regY = lWing.image.height / 2;
      rWing.regX = 0;
      rWing.regY = rWing.image.height / 2;

          
      // add butterfly to stage
      stage.addChild(butterfly);

      // change batterfly position
      butterfly.x = canvas.width * Math.random() | 0;
      butterfly.y = canvas.height * Math.random() | 0;
          
      // redraw stage
      stage.update();
    }




    Готовый пример, можно найтит тут: пример 1.

    Шаг 2. Машем крыльями

    Таймер
    Запускать анимации или последовательно происходящие события можно различными способами. В EaselJS можно подписаться на таймер обновления сцены и на каждом шаге вносить изменения. (Это похоже на цикл обновления состояния игры, в котором отрисовка сцены происходит по таймеру.)

    Первым делом нужно подписаться на таймер (частоту срабатывания также можно устанавливать):

    Ticker.addListener(window);


    Далее нужно определить функцию tick в области видимости windows, внутри которой необходимо обновить состояние сцены и запустить отрисовку:

    // update scene on each tick
    function tick() {
      butterfly.move()
      stage.update();
    }


    Пошаговая анимация крыльев
    Чтобы описать анимацию бабочки, нужно разбить все взмахи крыльев и перемещения на отдельные шаги. Но прежде, давайте зададим еще один параметр, определяющий начальное состояние бабочки, – начальный масштаб (в принципе, его можно и не задавать, если вы не планируете делать бабочек разного размера, тогда scale = 1.0):

    // initial scale
    butterfly.scale = butterfly.lWing.scaleX
      = butterfly.lWing.scaleY = butterfly.rWing.scaleX
      = butterfly.rWing.scaleY = 0.5 + 0.2 * Math.random();


    Анимации крыльев хорошо делать через горизонтальное (по X) масшабирование, причем для периодичности можно применять тригонометрические функции.

    Давайте зададим количество шагов анимации:

    // animation steps
    butterfly.step = butterfly.steps = 40 + 60 * Math.random();


    Теперь осталось описать каждый взмах:

    butterfly.move = function () {
      var wingAngle = (butterfly.steps - butterfly.step) / butterfly.steps * Math.PI;
      butterfly.lWing.scaleX = butterfly.rWing.scaleX
        = butterfly.scale * (0.4 + 0.6 * Math.abs(Math.cos(wingAngle * 4)));

      butterfly.step--;
    }


    Общий код
    var leftSrc = "../images/Cypres 1.png";
    var rightSrc = "../images/Cypres 2.png";
    var leftImg;
    var rightImg;
    var loadedWings = 0;
    var butterfly;
    var butterflyLoaded = false;

    function init() {
      // create a new stage and point it at our canvas:
      canvas = document.getElementById("canvas");
      stage = new Stage(canvas);

      // loading left wing
      leftImg = new Image();
      leftImg.src = leftSrc;
      leftImg.onload = onWingImageLoaded;

      // loading right wing
      rightImg = new Image();
      rightImg.src = rightSrc;
      rightImg.onload = onWingImageLoaded;

      // subscribe to Ticker
      Ticker.addListener(window);
    }

    function onWingImageLoaded(event) {
      loadedWings++;
      if (loadedWings == 2)
        onButterflyReady();
    }

    function onButterflyReady() {
      // create a new container for butterfly wings
      butterfly = new Container();
      // create wings
      butterfly.lWing = new Bitmap(leftImg);
      butterfly.rWing = new Bitmap(rightImg);

      // assemble a batterfly
      butterfly.addChild(butterfly.lWing);
      butterfly.addChild(butterfly.rWing);

      // change wings registration point inside container
      butterfly.lWing.regX = butterfly.lWing.image.width;
      butterfly.lWing.regY = butterfly.lWing.image.height / 2;
      butterfly.rWing.regX = 0;
      butterfly.rWing.regY = butterfly.rWing.image.height / 2;

          
      // add butterfly to stage
      stage.addChild(butterfly);

      // change batterfly position
      butterfly.x = canvas.width * Math.random() | 0;
      butterfly.y = canvas.height * Math.random() | 0;

      // initial rotation
      butterfly.lWing.rotation = butterfly.rWing.rotation
        = butterfly.angle = 360 * Math.random() | 0;

      // initial scale
      butterfly.scale = butterfly.lWing.scaleX
        = butterfly.lWing.scaleY = butterfly.rWing.scaleX
        = butterfly.rWing.scaleY = 0.5 + 0.2 * Math.random();

      // animation steps
      butterfly.step = butterfly.steps = 40 + 60 * Math.random();

      // move butterfly
      butterfly.move = function () {
        var wingAngle = (butterfly.steps - butterfly.step) / butterfly.steps * Math.PI;
        butterfly.lWing.scaleX = butterfly.rWing.scaleX
        = butterfly.scale * (0.4 + 0.6 * Math.abs(Math.cos(wingAngle * 4)));

        butterfly.step--;
      }

      butterflyLoaded = true;

      // redraw stage
      stage.update();
    }    

    // update scene on each tick
    function tick() {
      if (butterflyLoaded && butterfly.step >= 0) {
        butterfly.move()
      }
      stage.update();
    }




    Готовый пример, можно найтит тут: пример 2.

    Шаг 3. Перемещаем бабочку

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

    // initial rotation
    butterfly.lWing.rotation = butterfly.rWing.rotation
      = butterfly.angle = 360 * Math.random() | 0;
    // movement direction
    butterfly.direction = 8 * Math.random() - 4.0;
    // movement speed
    butterfly.speed = 5 * Math.random() + 5;


    Внутри функции butterfly.move осталось добавить изменение угла повората:

    butterfly.angle += butterfly.direction + (5.0 * Math.random() - 2.5);
    butterfly.lWing.rotation = butterfly.rWing.rotation = butterfly.angle;


    и прирост перемещения:

    butterfly.x += (butterfly.speed * Math.sin((butterfly.angle) / 180.0 * Math.PI));
    butterfly.y -= (butterfly.speed * Math.cos((butterfly.angle) / 180.0 * Math.PI));


    Бабочка должна полететь.



    Готовый пример, можно найтит тут: пример 3.

    Шаг 4. Оформляем код

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

    (function (window) {

      Butterfly = function (leftImg, rightImg) {
        this.initialize();
        this.initButterfly(leftImg, rightImg);
      }

      var p = Butterfly.prototype = new Container();

      p.lWing = null;
      p.rWing = null;
      p.angle = 0;
      p.direction = 0;
      p.speed = 0;

      p.steps = 0;
      p.step = -1;

      p.initButterfly = function (leftImg, rightImg) {
        // create wings
        this.lWing = new Bitmap(leftImg);
        this.rWing = new Bitmap(rightImg);

        // change wings registration point inside container
        this.lWing.regX = this.lWing.image.width;
        this.lWing.regY = this.lWing.image.height / 2;
        this.rWing.regX = 0;
        this.rWing.regY = this.rWing.image.height / 2;

        // initial rotation
        this.lWing.rotation = this.rWing.rotation = this.angle = 360 * Math.random() | 0;
            
        // initial scale
        this.scale = this.lWing.scaleX = this.lWing.scaleY = this.rWing.scaleX = this.rWing.scaleY = 0.5 + 0.2 * Math.random();

        // assembling batterfly
        this.addChild(this.lWing);
        this.addChild(this.rWing);

        this.mouseEnabled = true;
        this.onMouseOver = function (e) { this.reset(); };
      };

      p.reset = function () {
        // animation steps
        this.step = this.steps = 40 + 60 * Math.random();
        // movement direction
        this.direction = 8 * Math.random() - 4.0;
        // movement speed
        this.speed = 5 * Math.random() + 5;
      };

      p.move = function () {
        // update rotation
        this.angle += this.direction + (5.0 * Math.random() - 2.5);
        this.lWing.rotation = this.rWing.rotation = this.angle;

        // update wings
        var wingAngle = (this.steps - this.step) / this.steps * Math.PI;
        this.lWing.scaleX = this.rWing.scaleX = this.scale * (0.4 + 0.6 * Math.abs(Math.cos(wingAngle * 4)));

        // update position
        this.x += (this.speed * Math.sin((this.angle) / 180.0 * Math.PI));
        this.y -= (this.speed * Math.cos((this.angle) / 180.0 * Math.PI));
        this.step--;
      };

      p.isActive = function () {
        return this.step >= 0;
      };

      window.Butterfly = Butterfly;
    } (window));


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

    var leftSrc = "../images/Cypres 1.png";
    var rightSrc = "../images/Cypres 2.png";
    var leftImg;
    var rightImg;
    var loadedWings = 0;
    var butterfly;
    var butterflyLoaded = false;

    function init() {
      // create a new stage and point it at our canvas:
      canvas = document.getElementById("canvas");
      stage = new Stage(canvas);
      stage.enableMouseOver(10);

      // loading left wing
      leftImg = new Image();
      leftImg.src = leftSrc;
      leftImg.onload = onWingImageLoaded;

      // loading right wing
      rightImg = new Image();
      rightImg.src = rightSrc;
      rightImg.onload = onWingImageLoaded;

      // subscribe to Ticker
      Ticker.addListener(window);
    }

    function onWingImageLoaded(event) {
      loadedWings++;
      if (loadedWings == 2)
        onButterflyImagesReady();
    }

    function onButterflyImagesReady() {
      // create a new container for butterfly wings
      var butterfly = new Butterfly(leftImg, rightImg);
          
      // add butterfly to stage
      stage.addChild(butterfly);

      // change batterfly position
      butterfly.x = canvas.width * Math.random() | 0;
      butterfly.y = canvas.height * Math.random() | 0;
          
      butterfly.reset();
      butterflyLoaded = true;

      // redraw stage
      stage.update();
    }    

    // update scene on each tick
    function tick() {
      if (butterflyLoaded && butterfly.step >= 0) {
        butterfly.move()
      }
      stage.update();
    }


    Готовый пример, можно найтит тут: пример 4.

    Много-много бабочек


    Теперь давайте добавим побольше бабочек. Нам понадобится массив адресов изображений крыльев:

    var bfimgsrc = [{ left: "../images/Didius 1.png", right: "../images/Didius 2.png" },
    { left: "../images/Amphitrion 1.png", right: "../images/Amphitrion 2.png" },
    { left: "../images/Catenarius 1.png", right: "../images/Catenarius 2.png" },
    { left: "../images/Cyanides 1.png", right: "../images/Cyanides 2.png" },
    { left: "../images/Cypres 1.png", right: "../images/Cypres 2.png" },
    { left: "../images/Diana 1.png", right: "../images/Diana 2.png" },
    { left: "../images/Hecuba 1.png", right: "../images/Hecuba 2.png" },
    { left: "../images/Peleides 1.png", right: "../images/Peleides 2.png" },
    { left: "../images/Polyphemus 1.png", right: "../images/Polyphemus 2.png" },
    { left: "../images/Sulkowski 1.png", right: "../images/Sulkowski 2.png"}];


    Создание бабочек вынесем в отдельную функцию loadButterflyis и будем запускать еще бабочек по клику мыши:

    function init() {
      // create a new stage and point it at our canvas:
      canvas = document.getElementById("canvas");
      stage = new Stage(canvas);
      stage.enableMouseOver(10);

      loadButterflyis(18);

      canvas.onclick = function () {
        loadButterflyis(5);
      };

      // subscribe to Ticker
      Ticker.addListener(window);
    }
        
    function loadButterflyis(count) {
      for (var k = 0; k < count; k++) {
        var i = Math.floor(bfimgsrc.length * Math.random());

        var bfimages = {
          left: new Image(),
          right: new Image(),
          loadedWings: 0,
          onready: onButterflyImagesReady
        };

        // loading left wing
        bfimages.left.src = bfimgsrc[i].left;
        bfimages.left.onload = onWingImageLoaded;
        bfimages.left.butterfly = bfimages;

        // loading right wing
        bfimages.right.src = bfimgsrc[i].right;
        bfimages.right.onload = onWingImageLoaded;
        bfimages.right.butterfly = bfimages;
      }
    }


    Обновленный tick:

    // update scene on each tick
    function tick() {
      for (var i = 0; i < bfs.length; i++) {
        var butterfly = bfs[i];
        if (butterfly.isActive()) {
          butterfly.move();
        }
        else if (Math.random() > 0.999) {
          butterfly.reset();
        }
      }
      stage.update();
    }




    Готовый пример, можно найтит тут:пример 5.


    Спецэффекты


    Дополнительные эффекты будут довольно простыми. Тематичный фон:



    И аудио на фоне:

    <audio src="audio/ArrivalForest.mp3" autoplay loop/>

    (Энтузиасты могул легко добавить поддержку других форматов аудио.)

    Финальная версия также доступна.

    Upd.


    Решаю проблему с хостингом, выложил временно на тестовом паркинге и отдельно: промежуточные примеры и финальная версия одним файлом — narod.ru/disk/6923480001/March8.zip.html

    Source code was highlighted with Source Code Highlighter.
    Microsoft
    201,77
    Microsoft — мировой лидер в области ПО и ИТ-услуг
    Поделиться публикацией

    Комментарии 47

      +3
      Здорово! Очень интересно!
        0
        круто, только примеры не работают :)
          0
          Они не грузятся или не работают? :) Я проверял в IE9 RC, Chrome 10.0.648.82 beta, Firefox 4b11 и Opera 11.01 на Win7. Какой у вас браузер?
            0
            HTTP Error 503. The service is unavailable.
              0
              Ой )) Видимо, умер — выложил все примеры и финальную версию одним файлом — см. ссылку в конце.
              0
              Забавно, что практически все проверки вы производили на бетаверсиях браузеров, которые по определению нестабильны и которымы пользуется пока минимальное кол-во людей :)

              Попробовал на последнем стабильном Firefox — работает :)
            0
            Чуть-чуть подправить цвета…
              +6
              Здорово, но чтобы подарок был оформленным, девушке надо дарить такую открытку, как фон рабочего стола на новеньком, только что купленном ей ноутбуке.
              • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  Тем, кто не может покупать девушкам ноутбуки, дарить подобные подарки не стоит? :)
                    0
                    Это будет как-то не солидно. Можете конечно подарить ноутбук ручной сборки)). Вот кстати сейчас вспоминается пост про самодельные сережки. Как раз по бабочке на сережку.
                  +5
                  Поздравительная открытка к 8 марта на HTML5 и EaselJS и блог компании Microsoft. Казалось бы, какая связь?
                    0
                    Вы против? :)
                      +1
                      Против чего? Я за правильную классификацию информации. Не нашел в заметке ничего, что бы связывало её с фимой Microsoft.
                        +3
                        Автор? :)
                      +4
                      Создатели EaselJS сотрудничают с MS, а в топике типа пиар)
                        0
                        Честно, у меня не было цели рекламировать именно EaselJS, но если бы и было — оно того стоит ;) Реально облегчает жизнь во многих сценариях.
                          +2
                          Ну, у меня есть LibCanvas, и, судя по мануалу EaselJS (та и не шибко изящным исходникам в топике[ничего личного, исходники сами по себе — неплохи, но фреймворк мог их значительно улучшить]) — они ооочень сильно не дотягивают.
                            0
                            Свое всегда кажется лучше и удобнее :) И согласен, есть еще куда двигаться, библиотека еще только развивается. Впрочем, на ее ранеей версии сделано www.pirateslovedaisies.com/ — думаю, это достаточный показать потенциала.
                              0
                              Я знаю про пиратов — вполне неплохо. Но пиратов можно было бы сделать и без фреймворка.

                              Я стараюсь судить объективно. Например, JQuery и MooTools круче, чем мой atomJS, с чем я даже не собираюсь спорить.

                              Но смотрите — кучу рутинных операций вам всё равно приходится пределывать вручную.

                              Прелоадинг картинок, анимацию, та даже получение случайной точки на холсте.

                              Да и идею с методом «tick» в глобальной области видимости, а также this.onMouseOver.

                              Обе штуки можно было бы исправить нормальной событийной моделью, как, например, сделано в JQuery:
                              this.onMouseOver = function (e) { this.reset(); };
                              // =>
                              this.bind('mouseover', function () {
                                  this.reset();
                              });
                              // а также
                              stage.bind('update' , function () {});
                              


                              На первый взгляд, конечно, разницы нету, но что будет, если надо будет прилепить два события? В dhtml раньше тоже так делали, но ведь отказались от этого со временем?

                              В общем, много вещей — непродуманно, а много — вообще не сделано. Ну да ладно, кому то оно нравится и это главное)
                                +2
                                Кстати, оно основано на фреймворке YUI?
                                  +1
                                  Спрашиваю вот почему — во всех заголовках мануала — есть упоминание, а в тексте — не нашёл:
                                    +1
                                    В документации используется YUIDocs вместо JSDocs, использовавшегося ранее.
                                    0
                                    EaselJS? Отсылок на YUI не видел. Насколько я знаю, авторы отталкивались от задачи воспроизвести на canvas и javascript привычные для них возможности flash и соответствующих классов на actionscript.
                          0
                          у меня все бабочки улетели и не хотят возвращаться! (ф5 помог)
                            0
                            Иногда они возвращаются ;) Можно кликать мышкой — будут появляться новые. В идеале, конечно, надо предусмотреть логику на возврат :)
                            0
                            В Chromium 11.0.691.0 (76865), Ubuntu 10.10 бабочки начитают лететь, только если навести на них курсор.
                              0
                              Так и должно быть. Иногда они начинают летать сами — в коде на это есть специальное условие.
                                0
                                Ага, пересмотрел листинг и понял, что так задумано.
                                Позвольте спросить, а почему именно так? Пусть себе сами летают с редкими остановками.
                                  0
                                  Чтобы они сразу все не разлетелись :) Исходники доступны, если интересно, можете подкрутить так, как вам больше нравится ;)
                                +1
                                Вы меня опередили, только хотел сказать что бабочки ленивы и пока не пнешь не летают :)
                                0
                                «на фоне красовой»
                                Прошу прощения, так и должно быть?
                                  0
                                  Спасибо )
                                  +1
                                  «Машем», а не «махаем»!
                                    0
                                    Спасибо)
                                    +1
                                    Я сначала всех бабочек прогнала, а потом накликала целую кучу что они бедняжки даже летали тяжело!
                                    Спасибо за поздравление! ^_^
                                      0
                                      Я, конечно, очень сильно извиняюсь, но совершенно унылые бабочки. Вы же видели как летают бабочки (живые). С такой частотой взмахов крыльями как у Вас, простите, но «не взлетит».
                                      По задору больше напоминает осенних мух, нежели бабочек.
                                        +5
                                        Если делать быстрее, то ничего не видно будет :) Воспроизвести реалистично движение крыльев бабочки — отдельная сложная задача. В частности, крылья не являются двумя жесткими плоскостями — это динамичные поверхности, которые синхронно движутся, создавая вокруг необходимые вихревые потоки. Буду рад посмотреть, если воспроизведете.
                                          0
                                          Ну это ведь казуалка. Не стоит требовать от неё симуляции.
                                          –1
                                          Боже мой, такой огромный код ради такой убогой анимации…

                                          Мы тоже на днях делали анимацию бабочки. Решили попробовать интеграцию Flash+JS.
                                          Получилось вот так. (нужно навести мышку на логотип).
                                            0
                                            Жалко, надпись в фоновом изображении. Так просто не поменяешь
                                              0
                                              Если нужно, могу выложить картинку без надписи.
                                              0
                                              Кто-нибудь может объяснить доступно, как берутся формулы для взмахов крыльев и откуда взяты константы в них? Как это было расчитано?
                                                +1
                                                butterfly.move = function () {
                                                var wingAngle = (butterfly.steps - butterfly.step) / butterfly.steps * Math.PI;
                                                butterfly.lWing.scaleX = butterfly.rWing.scaleX
                                                = butterfly.scale * (0.4 + 0.6 * Math.abs(Math.cos(wingAngle * 4)));

                                                butterfly.step--;
                                                }

                                                1. Взмахи крыльев делаются через изменение масштаба по оси X.
                                                2. Т.к. бабочки разного размера, в качестве отправной точки масштаба используется мастштаб бабочки butterfly.scale в качестве множителя. (Если размер не менялся, то butterfly.scale = 1).
                                                3. Дальше масштаб нужно менять периодически — здесь хорошо подходят тригонометрические функции.
                                                4. Полет бабочки состоит из {butterfly.steps} шагов, текущий шаг — {butterfly.step}. Из этих двух чисел получается пропорция в отрезке [0, 1.0].
                                                5. Для косинуса (беру cos из соображения удобства) нужно передать угол, пропорцию выше, нужно перевести в интервал [0, PI], значия cos будет, соответственно, 1->… -> 0 ->… -> -1. Так как отрицательные значения нам не нужны, берем модуль (abs).
                                                6. Дополнительный множитель 4 — определяет число взмахов за полет.
                                                7. Коэффициенты 0.4 и 0.6 в сумме должны давать 1 (полный масштаб), 0.4 — сколько нужно оставить, чтобы крылья не схлопнулись.

                                                Как-то так :)
                                                  0
                                                  Все понятно, кроме числа взмахов за полет. Почему оно умножается на угол?
                                                    0
                                                    Математика :) Умножение чисел из диапазона [0; PI] на число взмахов n приводит к диапазону [0; nPI], в котором значения cos n раз пройдут цикл 1->… -> 0 ->… -> -1.
                                                      0
                                                      Уже понял, сорри — чтобы крылья полностью разворачивались не 1 раз, а в данном случае при пропорции, равной 0, 0,25, 0,5, 0,75 и 1.

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

                                                Самое читаемое