Плавная анимация интерфейса (easing)

от автора

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



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

Анимационные слайдеры

Анимационные слайдеры похожи на дорожки на микшерском пульте. По сути слайдер — это число от 0.0 до 1.0, где 0.0 — это начало анимации, а 1.0 — ее конец. Именно это число подставляется в математическую формулу. Зачем вообще нужны эти слайдеры? Нужны, потому что обычно в интерфейсе присутствует много элементов, каждому нужна анимация и их движение должно начинаться и заканчиваться одновременно. А вот тип анимации и координаты каждого элемента на экране могут быть разными.

Типы анимаций

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

Функция анимации слайдера:

#define MAXSLIDES 5 //максимальное количество слайдеров #define LOOP_ANIM 1 //анимация по кругу #define FORWBACK_ANIM 2 //анимация вперед-назад  static float slide[MAXSLIDES]={0.0f}; //анимационные слайдеры, число от 0.0 до 1.0 static bool slideback[MAXSLIDES]={false}; //флаг означающий, что анимация проигрывается назад static bool loopback[MAXSLIDES]={false}; //флаг для анимаций типа вперед-назад, означает что анимация проигрывается назад  static void ANIM(int n, float speed=1.0f, bool forw=true, char loop=0){     //n - какой слайдер двигаем     //speed - с какой скоростью     //forw - двигаем вперед или назад     //loop - тип анимации слайдера      if(loopback[n])forw=!forw; //если тип анимации вперед-назад и сейчас движемся назад то меняем начальное направление     slideback[n]=!forw; //запоминаем куда проигрываем анимацию     slide[n]+=fpsf*(forw?speed:-speed); //двигаем слайдер вперед или назад с заданной скоростью, где fpsf = 1.0/FPS          switch(loop){         case LOOP_ANIM: //анимация по кругу             if(slide[n]>1.0f)slide[n]-=1.0f;             else if(slide[n]<0.0f)slide[n]+=1.0f;             break;         case FORWBACK_ANIM: //анимация вперед-назад             if(slide[n]>1.0f){                 slide[n]=2.0f-slide[n];                 loopback[n]=!loopback[n];             }else if(slide[n]<0.0f){                 slide[n]=-slide[n];                 loopback[n]=!loopback[n];             }             break;         default: //обычная анимация до 0.0 или 1.0             if(slide[n]>1.0f)slide[n]=1.0f;             else if(slide[n]<0.0f)slide[n]=0.0f;             break;     }      } 

Вывод элементов интерфейса

//типы анимаций #define LINEAR 0 #define FADEIN 1 #define FADEOUT 2 #define BALL 3 #define SIN 4 #define INCENTER 5 #define FADEBOTH 6 #define FADEBOTH2 7 #define STEPS 8 #define LAMP 9 #define JUMPIN 10 #define JUMPOUT 11  static float A(int n, int type, float s, float e, int typeout=0, float e2=-1000.0f){     //где n - какой слайдер использовать     //type - тип анимации     //s и e - начальные и конечные значения, между которыми произойдет переход     //typeout - можно задать другой тип для обратной анимации (необязательное)     //e2 - конечное значение для обратной анимации (необязательное)          if(n<0){         //если номер слайдера отрицательный то переворачиваем анимацию         float tmp=s;         s=e;         e=tmp;         n=-n;         if(!slideback[n]){             if(typeout)type=typeout;             if(e2!=-1000.0f)e=e2;         }     }else{         //если анимация движется назад и указан другой типа для обратной анимации, используем его         if(slideback[n]){             if(typeout)type=typeout;             if(e2!=-1000.0f)s=e2;         }     }          float x=slide[n];          //различные формулы для анимаций     switch(type){         case FADEOUT:             x=x*x;             break;         case FADEIN:             x=1.0f-pow(x-1.0f, 2);             break;         case FADEBOTH:             x=pow(sinf(x*M_PI*0.5f), 2);             break;         case FADEBOTH2:             x=pow(sinf(x*M_PI*0.5f), 2);             x=pow(sinf(x*M_PI*0.5f), 2);             break;         case INCENTER:             x=tanf(x*2.0f-1.0f)/3.0f+0.5f;             break;         case BALL:{             float d=sinf((x-1.0f/12.0f)*M_PI*6.0f)*0.65f+0.65f;             x=d+sinf(x*M_PI*0.5f)*(1.0f-d);         }break;         case STEPS:             x=(int)(x*7.0f)/7.0f;             break;         case LAMP:             x=sinf(pow(expf(1.25f-x), 2)*4.0f);             if(x>0.0f)x=1.0f;             else x=0.0f;             break;         case JUMPOUT:             x=x*x*8.0f/3.0f-x*5.0f/3.0f;             break;         case JUMPIN:             x=x-1.0f;             x=-x*x*8.0f/3.0f-x*5.0f/3.0f+1.0f;             break;     }          //возвращаем значение между начальным и конечным в зависимости от типа анимации и текущего положения слайдера     return s+(e-s)*x; } 

Как все это использовать?

static void mainLoop(){     //в главном цикле двигаем все слайдеры     ANIM(1, 1.0f, showPage); //проигрываем слайдер №1 вперед если страницу надо показать, и назад если страница спрятана     ANIM(2, 2.0f, true, LOOP_ANIM); //проигрываем слайдер №2 в два раза быстрее, анимация будет крутиться по кругу     //выводим элементы интерфейса     glColor4f(1.0f, 1.0f, 1.0f, A(1, FADEIN, 0.0f, 1.0f));     DrawSomeImage(width/2, A(1, BALL, -150, height/2, FADEIN, height+200));     DrawOtherImage(A(2, INCENTER, -150, width+150), 100); } 

DrawSomeImage(width/2, A(1, BALL, -100, height/2, FADEIN, height+100));
Тут выводится некая картинка, допустим это логотип. По оси Х она выводится посередине экрана, а У у нас анимированый. Разберем каждый параметр:
1 — используем слайдер №1
BALL — анимация типа «мячик», логотип должен выпрыгнуть сверху экрана

График анимации 'мячик'

-100 — это начальная координата по У т.е. вверху за пределами экрана
height/2 — значит посередине экрана по вертикали надо будет остановиться
Остальные параметры необязательные, но тогда при загрузке страницы логотип выпрыгнет сверху, что смотрится органично, а вот когда ему надо будет убраться обратно, анимация мячика будет смотреться не совсем естественно. Поэтому задаем тип обратной анимации, чтобы убирался он плавно с ускорением т.е. FADEIN.
Лично мне больше нравится, когда логотип улетает вниз а не обратно вверх, поэтому задаем куда ему потом улететь т.е. height+100.
Именно так выводится логотип в этом примере.

Так же мы плавно меняем прозрачность этой картинки:
glColor4f(1.0f, 1.0f, 1.0f, A(1, FADEIN, 0.0f, 1.0f));
Мы используем все тот же 1-й слайдер, чтобы синхронно менять и прозрачность, и координаты. Однако тип анимации ставим FADEIN (с ускорением) и меняем прозрачность от 0.0 до 1.0.

DrawOtherImage(A(2, INCENTER, -150, width+150), 100);
В этом случае картинка будет пролетать слева направо через весь экран, при этом будет плавно задерживаться посередине.
2 — используем слайдер №2, который проигрывается циклично и в два раза быстрее чем слайдер №1.
INCENTER — формула по которой картинка немного задержится посередине экрана

График анимации 'Пауза в середине'

-150 — начальная координата по Х
width+150 — конечная координата по Х

Еще пример

    glColor4f(1.0f, 1.0f, 1.0f, A(1, FADEBOTH, 0.0f, 1.0f));     DrawSomeImage1();     glColor4f(1.0f, 1.0f, 1.0f, A(-1, FADEBOTH, 0.0f, 1.0f));     DrawSomeImage2(); 

Как видно мы выводим две картинки с разной прозрачностью. Оба раза используем слайдер №1 и используем анимацию с плавным началом и концом. Отличается только знак в номере слайдера. -1 означает, что эта анимация перевернется и будет прятаться при слайдере равным 1.0, и выезжать при слайдере равным 0.0. Визуально эти две картинки будут плавно заменять друг друга.

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

ссылка на оригинал статьи http://habrahabr.ru/post/163197/


Комментарии

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

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