Анимационная пасхалка, или дань уважения студии при увольнении из нее

в 8:34, , рубрики: javascript, mo-js, Программирование, метки:

Анимационная пасхалка, или дань уважения студии при увольнении из нее - 1

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

Как то раз увидев завораживающую анимацию от создателя библиотеки mo-js для svg-эфектов, я загорелся и решенил сделать что-то подобное. Как раз мне на глаза попалась обновленная главная страничка нашей студии, недолго думая я собрался сделать анимацию для логотипа.
И раз эта статья рассказывает, как все это происходило, то к моему сожалению я не стану пересказывать весь мануал по данной библиотеки. Его вам придется прочитать самим, если конечно захотите сделать что-то подобное.

Ну а чтобы сразу вникнуть в статью:

Ссылка на демо просмотр только от 1204x400, кликнуть на колокольчик
Ссылка на GitHub

Первые начинания

Мы все ошибаемся, но не всегда понимаем, что идем по каменистой дорожке.

Изначально я хотел разбивать логотип на мелкие кусочки, сжимать их в 1 точку, и только потом как пазл собирать в единое целое со всевозможными эффектами (думаю это было бы красиво). Но быстро понял, что делаю очень быструю анимацию и естественно она будет без звука. Когда логотип создателя библиотеки, как я написал, был «Завораживающим».

Свое второе вдохновение я нашел в одной навязчивой мелодии (Clannad песенка булочек), думаю многие щас вздохнули в меланхоличном припадке. Да она просто умиляющая. И раз это колыбельная, то и тематика ей должна быть подстать!

Сюжет выдумывать особо и не пришлось. Колыбельная — «ночь, звезды, луна», замечательные ассоциации, большие варианты развития сюжета моей пасхалки.

Первым делом я нашел минус этой песенки и нарезал ее до 30 секунд, это максимум времени, сколько может выдержать любой гость, не закрыв страницы (думаю на большее меня бы и самого не хватило). Далее сделал метки эффектов, наиболее подходящим инструментом я выбрал Adobe Audition CC 2017 (честно говоря другого инструмента я и не искал, просто он мне попался на глаза первым).

Выглядело это так:

Анимационная пасхалка, или дань уважения студии при увольнении из нее - 2

Библиотека предоставляет вложенные временные шкалы объекта TimeLine(), я бы мог разделить все на отдельные блоки, и начинать их анимацию по отдельности. Но т.к. у меня была привязка к мелодии, я счел необходимым сделать все на 1 единой временной шкале, дабы не запутать себя самого с таймингом. Просто сделал большие блоки массивов с временем начала в виде предоставляемого библиотекой параметра задержки (delay).

Радужный дождь и взращивание букв

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

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

  1. FirstRainbow

    Анимация падения радуги мне далась довольно легко, я банально скопировал ее с примера в тутореал. Сделал массив с координатами и описал это в 1 методе

    rainbow(200, 500, 'A', 'str1')
    

    где параметры — это продолжительность, время задержки (в моем случае начала) анимации и последние 2 описывают координату.

    Эффект падения получился невзрачный:

    Скрытый текст

    Анимационная пасхалка, или дань уважения студии при увольнении из нее - 3

    Изменили на улучшенный:

    Скрытый текст

    Анимационная пасхалка, или дань уважения студии при увольнении из нее - 4

  2. Letters

    Описывает анимацию букв. Он стал первым по сложности для меня объектом. Концепция анимации заключалась в росте, будто растения. С буквами пришлось изрядно повозиться, думаю не каждый умеет работать в Illustrator (я в том числе). И как человек получивший в этом опыт, советую:

    • Всегда вычищайте код svg после редактора.
    • Переименовывайте классы в уникальные
    • Не забывайте удалять лишнии слои.

    Потратил 60% общего времени только на синхронизацию, сверку координат, отрисовку отдельных линий (которые нельзя сымитировать стандартными фигурами). Мне приходилось по 20 раз менять 1 параметр, ради достижения хорошего эффекта. Но мой внутренний перфекционист пошел на компромисс со временем и сдался.

    Код же прост для обзора:

                        ...that.plant_D(800, 10111),
                        ...that.plant_A(800, 10200),
                        ...that.plant_R(800, 10300),
                        ...that.plant_N(800, 10400),
                        ...that.plant_E(800, 10500),
                        ...that.plant_O(800, 10600),
    
                        ...that.plant_S(800, 12500),
                        ...that.plant_T(800, 13100),
                        ...that.plant_U(800, 13400),
                        ...that.plant_D2(800, 13700),
                        ...that.plant_I(800, 14000),
                        ...that.plant_O2(800, 14300)
    

    Вторым с чем я столкнулся — это с собственной невежественностью в чтении мануалов, мы все в той или иной степени этим страдаем. Банально упустил пункт о том, что собственные заготовки svg должны быть в разрешении 100x100 и первая буква «D» пошла наперекосяк. Ну и конечно, в таких случаях мы всегда делаем костыль, что и случилось. Просто поменял ей размер, после инициализации (а раз я использовал объект Burst, то и менять мне надо сразу всех потомков).

     for (let el of equal.el.children) {
                el.style['height'] = '180px';
            }
    

    В дальнейшем я задал вопрос на GitHub, на что меня ткнули в пунктик мануала)

    Добавление собственной svg происход след. образом:

    class elipceR2 extends mojs.CustomShape {
      getShape () { return '<path d="M0,120.5h13.9c39.6,0,59.6-30.6,59.1-61C73,29.8,52.8-0.2,13.3,0L0.1,0"/>'; }
      getLength () { return 200; } // optional
    }
    

    добавить и использовать (простота во всем):

    mojs.addShape( 'elipceR2', elipceR2'); 
    new mojs.Shape({ shape: 'elipceR2'});
    

    Либо использовать дефолтные svg-фигуры.

  3. MoonRise

    Описывает горизонт, горы, выход луны. С луной возился долго, надо было соблюдать стилистику и луна с детальным рельефом тут не подходила:

    Скрытый текст

    Анимационная пасхалка, или дань уважения студии при увольнении из нее - 5

    Но, я удачно нагуглил более лучший вариант:

    Скрытый текст

    Анимационная пасхалка, или дань уважения студии при увольнении из нее - 6

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

    Для приличия код:

                        that.moon(5000, 20000),
                        that.mountains(1500, 18500),
                        that.horizonLine(1600, 18500),
    

  4. Stars

    Второй по сложности объект. Стал основными источником синхронизации эффектов с музыкальной нарезкой «Семейства булочек».

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

    Где, параметры это время анимации, начало, и кол-во звезд.

                    that.curentStars = [
                        ...that.star(200, 17050, 5),
                        ...that.star(200, 17300, 15),
                        ...that.star(200, 17600, 25),
                        ...that.star(200, 17900, 30),
                        ...that.star(200, 18200, 35),
                    ];
    

    Как-то размыть svg средствами библиотеки мне не удалось, пришлось их просто увеличивать. Как всегда, длительность анимации, ее начало, массив и кол-во звезд, которые мы берем рандомно,

                    that.shineStars(10, 21200, that.curentStars, 3);
                    that.shineStars(10, 21600, that.curentStars, 3);
                    that.shineStars(10, 21900, that.curentStars, 3);
    
                    that.shineStars(10, 22100, that.curentStars, 4);
                    that.shineStars(10, 22400, that.curentStars, 4);
                    that.shineStars(10, 22700, that.curentStars, 4);
    
                    that.shineStars(10, 23300, that.curentStars, 10);
                    that.shineStars(10, 23900, that.curentStars, 15);
                    that.shineStars(10, 24500, that.curentStars, 12);
    
                    that.shineStars(10, 25150, that.curentStars, 9);
                    that.shineStars(10, 25700, that.curentStars, 6);
                    that.shineStars(10, 26300, that.curentStars, 7);
                    that.shineStars(10, 26600, that.curentStars, 4);
                    that.shineStars(10, 27200, that.curentStars, 8);
                    that.shineStars(10, 28170, that.curentStars, 3);
    

    попутно закидывая в свойство для падения this.curRimShineStar.

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

                    that.shootingStar(500, 29600, {y: 52, x: PARAMS.COORDINATES_X.str1.D});
                    that.shootingStar(500, 29800, {y: 52, x: PARAMS.COORDINATES_X.str1.A});
                    that.shootingStar(500, 30100, {y: 52, x: PARAMS.COORDINATES_X.str1.R[0]});
                    that.shootingStar(500, 30400, {y: 52, x: PARAMS.COORDINATES_X.str1.N[0]});
                    that.shootingStar(500, 30700, {y: 52, x: PARAMS.COORDINATES_X.str1.N[1]});
                    that.shootingStar(500, 31070, {y: 52, x: PARAMS.COORDINATES_X.str1.E});
                    that.shootingStar(500, 31370, {y: 52, x: PARAMS.COORDINATES_X.str1.O});
    

    Очень огорчило отсутствие некоторых подробностей по местоположению параметров в объектах, но все решилось банальным выводом его в консоль и чтением исходников (долго, но всегда гарантированный результат). Мне потребовалось узнать, когда объект завершит свою анимацию после всех манипуляций с ним и я не сразу понял что свойство Object.timeline._props.time его показывает, изначально я искал его в Object._o

    Где то я конечно схалтурил в коде, за что трудно себя простить. Но в целом анимация удалась замечательной.

  5. PointsTimer

    Чтобы гость мог покрутить ползунок и самому посмотреть, как это происходило, пришлось добавить и таймер, который завершал анимацию через пару секунд простоя. Тут моя фантазия уже закончилась и я просто добавил 3 уменьшающихся круга. По истечению которых, при помощи той же JQuery.animate() мы растворяем буквы в логотипе и удаляем все теги, что добавила библиотека.

К сожалению библиотека не может делать очень долгих анимаций, это можно заметить, есть дернуть ползунок очень быстро в сторону, вы увидите как все пойдет кусками, где то будут элементы, где то нет. Решить это можно думаю лишь принудительным манипулированием скорости ползунка.

Автор: Frimko

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js