Как анимировать SVG иконки с помощью библиотеки segment?

Как анимировать SVG иконки с помощью библиотеки segment?

От автора: в этом уроке мы рассмотрим, как анимировать иконку svg иконку меню при помощи Dribbble шота от Tamas Kojo, в котором используются SVG и Segment – JavaScript библиотека для рисования и анимирования элемента SVG path.

скачать исходникидемо

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

При помощи SVG и библиотеки Segment мы воссоздадим этот эффект. Сперва, мы распланируем нашу анимацию, познакомимся с библиотекой, а затем нарисуем анимацию для иконки.

Планирование

Для воссоздания данного эффекта я не вижу ничего лучше, чем SVG. А JS библиотека Segment (альтернатива DrawSVGPlugin от GSAP) в этом нам поможет.

Необходимо создать три элемента path, которые будут описывать анимацию каждой прямой иконки гамбургера. С помощью Segment можно как угодно анимировать svg path элементы. Для прорисовки пути анимации каждой прямой можно воспользоваться любым редактором векторной графики (Adobe Illustrator или Inkscape); для достижения большей точности мы будем рисовать данные пути самостоятельно (связующие линии, кривые и дуги). Помните, что наша анимация «эластична», т.е. нужно учесть длину пути каждой прямой. Но прежде, давайте познакомимся с бибилиотекой Segment.

Знакомство с Segment

Данная библиотека – основной инструмент, который мы будем использовать, а точнее, небольшой JS класс (без зависимостей) для рисования и анимирования SVG путей. Пользоваться библиотекой достаточно просто:

<!-- добавьте segment скрипт(меньше 2kb) -->
<script src="/dist/segment.min.js"></script>

<!—задаем path -->
<svg>
    <path id="my-path" ...>
</svg>

<script>
    // инициализируем новый класс segment с path
    var myPath = document.getElementById("my-path"),
        segment = new Segment(myPath);

    // рисуем путь в любое время
    // Syntax: .draw(begin, end[, duration, options])
    segment.draw("25%", "75% - 10", 1);

    /* пример со всеми возможными вариантами */
		
    // задаем функцию смягчения (t параметр в промежутке [0, 1])
    function cubicIn(t) {	
        return t * t * t;
    }

    // задаем колбек функцию
    function done() {
        alert("Done!");
    }

    // рисуем путь
    segment.draw(0, "100%", 1, {delay: 1, easing: cubicIn, callback: done});
</script>

Чтобы более подробно разобраться в принципе работы, поиграйтесь с demo и ознакомьтесь с документацией на GitHub. Также можно прочесть данную статью. Важно отметить, что в библиотеке Segment нет своих функций смягчения (кроме линейной по умолчанию). Дабы восполнить недостачу, мы задействуем библиотеку d3-ease.

Прорисовка путей

Анимация довольно быстрая, но если рассмотреть ее покадрово, можно нарисовать путь каждой прямой. В результате получим что-то похожее на это:

Код ниже рисует картинку выше шаг за шагом:

<svg width="100px" height="100px">
    <path d="M 30 40 L 70 40 C 90 40 90 75 60 85 A 40 40 0 0 1 20 20 L 80 80"></path>
    <path d="M 30 50 L 70 50"></path>
    <path d="M 70 60 L 30 60 C 10 60 10 20 40 15 A 40 38 0 1 1 20 80 L 80 20"></path>
</svg>

Для достижения желаемого эффекта, необходимо добавить CSS стили и идентификаторы к элементам path. ID нам пригодится для получения доступа к path через скрипт. Далее будем использовать следующую HTML разметку:

<!-- Wrapper -->
<div id="menu-icon-wrapper" class="menu-icon-wrapper">
    <!-- SVG с элементами path -->
    <svg width="100px" height="100px">
        <path id="pathA" d="M 30 40 L 70 40 C 90 40 90 75 60 85 A 40 40 0 0 1 20 20 L 80 80"/>
        <path id="pathB" d="M 30 50 L 70 50"/>
        <path id="pathC" d="M 70 60 L 30 60 C 10 60 10 20 40 15 A 40 38 0 1 1 20 80 L 80 20"/>
    </svg>
    <!-- триггер анимации -->
    <button id="menu-icon-trigger" class="menu-icon-trigger"></button>
</div>

И CSS стили:

// обертка задана с фиксированными шириной и высотой
// Заметьте, что свойство pointer-events установлено в 'none'. 
// нам не нужно, чтобы курсор изменялся на элементе анимации.
.menu-icon-wrapper{	
    position: relative;
    display: inline-block;
    width: 34px;
    height: 34px;
    pointer-events: none;
    transition: 0.1s;
}

// выполняем трансформацию второго демо
.menu-icon-wrapper.scaled{
    transform: scale(0.5);
}

// задаем позицию SVG элемента
.menu-icon-wrapper svg{
    position: absolute;
    top: -33px;
    left: -33px;
}

// задаем стили для path
.menu-icon-wrapper svg path{
    stroke: #fff;
    stroke-width: 6px;
    stroke-linecap: round;
    fill: transparent;
}

// устанавливаем pointer-events на 'auto', 
// позволяя запускать анимацию только по одному событию
.menu-icon-wrapper .menu-icon-trigger{
    position: relative;
    width: 100%;
    height: 100%;
    cursor: pointer;
    pointer-events: auto;
    background: none;
    border: none;
    margin: 0;
    padding: 0;
}

Анимация

SVG код готов. Теперь наша задача, для каждой секции анимации попытаться подобрать правильную функцию смягчения и добиться соответствующей синхронизации с GIF анимацией. Займемся анимацией верхней и нижней планкой иконки гамбургера. Для каждой планки необходимо задать сегмент со значениями begin и end. Так как у нас под рукой только GIF анимация, то процесс этот будет долгим и не обойдется без ошибок при подборе правильных значений.

var pathA = document.getElementById('pathA'),
    pathC = document.getElementById('pathC'),
    segmentA = new Segment(pathA, 8, 32),
    segmentC = new Segment(pathC, 8, 32);

После этого можно приступить к анимации, сохраняя длину планки (end — begin = 24) в процессе анимирования. Проанализировав последовательность анимации, можно понять, что первая функция смягчения линейная, заканчивающаяся гибким поворотом. Мы будем использовать функции, в качестве параметра в которые передается сегмент. Для верхней и нижней планки гамбургера функция будет одна и та же, так как анимация одинаковая, только направлена в другую сторону.

// линейная секция с колбеком к следующей секции
function inAC(s) { s.draw('80% - 24', '80%', 0.3, {delay: 0.1, callback: function(){ inAC2(s) }}); }

// Гибкая секция, применяется функция elastic-out 
function inAC2(s) { s.draw('100% - 54.5', '100% - 30.5', 0.6, {easing: ease.ease('elastic-out', 1, 0.3)}); }

// запуск анимации
inAC(segmentA); // верхняя планка
inAC(segmentC); // нижняя планка
То же самое необходимо повторить и для средней планки:
// инициализация
var pathB = document.getElementById('pathB'),
    segmentB = new Segment(pathB, 8, 32);

// немного расширяем планку
function inB(s) { s.draw(8 - 6, 32 + 6, 0.1, {callback: function(){ inB2(s) }}); }

// обратно уменьшаем с уаругим эффектом 
function inB2(s) { s.draw(8 + 12, 32 - 12, 0.3, {easing: ease.ease('bounce-out', 1, 0.3)}); }

// запуск анимации
inB(segmentB);

Для обратного эффекта, превращения крестика в гамбургер:

function outAC(s) { s.draw('90% - 24', '90%', 0.1, {easing: ease.ease('elastic-in', 1, 0.3), callback: function(){ outAC2(s) }}); }
function outAC2(s) { s.draw('20% - 24', '20%', 0.3, {callback: function(){ outAC3(s) }}); }
function outAC3(s) { s.draw(8, 32, 0.7, {easing: ease.ease('elastic-out', 1, 0.3)}); }

function outB(s) { s.draw(8, 32, 0.7, {delay: 0.1, easing: ease.ease('elastic-out', 2, 0.4)}); }

// запуск анимации
outAC(segmentA);
outB(segmentB);
outAC(segmentC);

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

var trigger = document.getElementById('menu-icon-trigger'),
    toCloseIcon = true;

trigger.onclick = function() {
    if (toCloseIcon) {
        inAC(segmentA);
        inB(segmentB);
        inAC(segmentC);
    } else {
        outAC(segmentA);
        outB(segmentB);
        outAC(segmentC);
    }
    toCloseIcon = !toCloseIcon;
};

Анимация закончена, но есть небольшая проблема. В разных браузерах, она выглядит немного по-разному. Длина элементов path вычисляется в браузерах по-разному, а значит, есть и небольшое различие (существенное). Особенно это заметно в Firefox и Chrome.

Решение данной проблемы достаточно простое. Мы увеличим наш SVG, тем самым увеличив размер элементов path, а потом уменьшим svg под необходимый размер. В нашем случае мы изменили размер SVG в 10 раз, в итоге получили следующий код:

<svg width="1000px" height="1000px">
    <path id="pathA" d="M 300 400 L 700 400 C 900 400 900 750 600 850 A 400 400 0 0 1 200 200 L 800 800"></path>
    <path id="pathB" d="M 300 500 L 700 500"></path>
    <path id="pathC" d="M 700 600 L 300 600 C 100 600 100 200 400 150 A 400 380 0 1 1 200 800 L 800 200"></path>
</svg>

А затем уменьшили размер с помощью CSS:

.menu-icon-wrapper svg {
    transform: scale(0.1);
    transform-origin: 0 0;
}

Обратите внимание на то, что необходимо также увеличить значения float в JS (умножить на 10), а также изменить значение stroke-width в CSS. Данный метод поможет снизить до минимума различия в браузерах, но если вас не смущают небольшие данные мелочи, то можно придерживаться первоначального варианта.

Сегодня мы узнали, как создавать эластичную анимацию с помощью Segment. Это только один из возможных способов. Теперь ваш черед, создайте необычную SVG анимацию :)

Надеемся, данный урок был полезен для вас!

Проект на GitHub

Автор: Luis Manuel

Источник: http://tympanus.net/

Редакция: Команда webformyself.

Фреймворк Bootstrap - верстаем адаптивно, просто, быстро!

Получите видеокурс по основам Bootstrap

Получить

Метки: ,

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree