Хочу поделиться с вами простым, но эффектным способом анимировать юзер-интерфейс вашего приложения или сайта. В статье представлен готовый код на С++, который я использовал для анимирования 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/
Добавить комментарий