Что такое Framer ?
Framer это программа для создания прототипов, в которой прототип создается сразу в коде, на языке CoffeeScript, который компилируется в JavaScript.
Framer позволяет создавать интерактивные анимации, облегчает разработчикам понимание того, как прототип должен функционировать и как представленную анимацию можно воплотить в жизнь. Еще он учит дизайнера понимать код.
Созданный с помощью Framer прототип можно отдавать тестировать клиентам прямо с девайса в натуральную величину, при этом времени на его создание значительно меньше, чем если бы прототип создавали разработчики.
Как может показаться в начале, Framer?—?сложная программа, требующая знания кода, хотя на самом деле писать код в ней намного легче, чем кажется.
Для работы с Framer достаточно нарисовать прототип в Sketch или Photoshop и придумать взаимодействие между слоями. Также здесь можно создавать свои слои, анимировать и менять их свойства.
Само написание кода во Framer интересный и увлекательный процесс, который не требует особой подготовки, к тому же на сайте программы есть очень подробная документация с примерами и туториалами.
В анимации, о которой речь пойдет дальше, я хотела отобразить loading, где проценты загрузки выполняют роль индикатора процесса.
Для создания данной анимации нужно знать 4 вещи: Layer, State, Event, Animation.
Layer?—?слой, может быть создан непосредственно во Framer либо им может быть любой слой из Sketch или Photoshop.
State—?это состояние объекта (слоя). Например, объект будет иметь какое то default состояние (такое, каким мы его нарисовали в Sketch или Photoshop) и бесконечное число состояний, которые мы ему можем дать, меняя его свойства (прозрачность, тень, размытость, размеры, положение и т.д.)
Event?—?это событие. Им может быть клик, swipe, drag, scroll и тд. При возникновении каждого из этих событий, мы можем менять состояния объектов (State), анимируя слои.
Animation?—?кроме состояний, мы можем создавать непосредственно во Framer либо задавать слоям, анимации (например вращение, изменение прозрачности и тд).
Итак, приступим к созданию анимации.
В результате у нас должно получиться следующее:
1 Шаг:
Рисуем прототип в Sketch или Photoshop.
Я пользовалась Sketch. Скачать мой прототип можно здесь.
Импортируем этот прототип во Framer.
Sketch (или Photoshop) с прототипом при импорте должны быть открыты.
В Framer выбираем тип девайса (иконка Viewer), в моем случае это iPhone 6+.
Так как мой прототип Sketch нарисован в маштабе 1:1, а для iPhone 6+ мы пользуемся множителем 3, то при импорте выбираем из выпадающего списка @3x и не забываем об этом (после, при описании позиции объекта по x и y координатам, которые можно посмотреть в Sketch, мы их умножаем на 3 соответственно).
Все слои в Sketch должны быть объединены в группы, только так они будут видимыми во Framer. Импорт не произойдет, если название хоть одной группы будет начинаться с цифры (выдаст ошибку).
После импорта должна появиться слудующая строчка:
# Import file "iPhone_loading" (sizes and positions are scaled 1:3)
Sketch = Framer.Importer.load("imported/iPhone_loading@3x")
“loading @3x”?—?означает, что прототип импортировался с множителем 3х, который мы предварительно выбрали. “Sketch” можно заменить любым словом или знаком (например $)?—?и ставить его впереди описания каждого слоя из Sketch прототипа. Например, в Sketch прототипе все слои включены по умолчанию, но поскольку верхние слои нас пока не интересуют, мы можем сделать их прозрачными:
Sketch.loadingfinish.opacity = 0 или можно написать $.loadingfinish.opacity = 0
где $ = Sketch
loadingfinish?—?это самый верхний слой.
Для того, чтобы не писать постоянно Sketch.слой или $.слой можно вставить следующую строчку:
Utils.globalLayers $
тогда чтобы сделать слой loadingfinish прозрачным, достаточно написать:
loadingfinish.opacity = 0
Детальнее про Utils можно прочитать во Framer документации здесь.
Здесь я оставила знак $.
Далее зададим для слоя header родительский элемент (SuperLayer), по отношению к которому header будет дочерним.
$.header.superLayer = Framer.Device.screen
Теперь header?—?дочерний элемент экрана девайса, а значит всегда будет видимым и неподвижным.
2 Шаг:
Задаем дальше параметры для слоев loadingfinish, oval, textButton, bgorange, ovalsecond, делая их прозрачными:
$.loadingfinish.opacity = 0
$.oval.opacity = 0
$.textButton.opacity = 0
$.bgorange.opacity = 0
$.ovalsecond.opacity = 0
Меняем положение y координаты для слоев textButton, blueButton, buttonbg, loadingfinish, loadingbg, time, text, чтобы они сдвинулись по вертикали либо вверх (значение с минусом), либо вниз. Как видим, мы установили для слоя loadingfinish прозрачность и сдвинули его вниз на 100px.
$.loadingfinish.y = 100
$.textButton.y = 3201
$.blueButton.y = 3201
$.buttonbg.y = 3201
$.loadingbg.y = 3201
$.time.y = -100
$.text.y = 3201
Для слоя line меняем x координату, чтобы слой выдвигался слева по горизонтали.
$.line.x = -1700
И наконец для слоя blueButton устанавливаем scale = 0, чтобы слой исчез.
$.blueButton.scale = 0
В итоге мы получили слой с пустыми белыми прямоугольниками.
Дальше нам нужно, чтобы при клике на второй прямоугольник сверху, появлялся оранжевый круг, расширялся (заливая оранжевым цветом белый прямоугольник), и затем увеличивался на весь экран. Для этого мы создаем новый слой (которого нет в Sketch прототипе), называем его Rectangle и задаем ему параметры как в CSS.
Первоначально это квадрат с заданными параметрами высоты и ширины (436px), оранжевого цвета, расположенный точно по центру белого прямоугольника( x: 410, y: 730). Чтобы из него получился круг, мы задаем ему border radius равный половине стороны(436/2 = 218px), делаем его невидимым с помощью параметра scale:0.
Rectangle = new Layer
width: 436
height: 436
backgroundColor: "#FF7841"
borderRadius: 218
x: 410
y: 730
scale: 1
В JavaScript это бы выглядело так:
(function () {
var Rectangle;
Rectangle = new Layer({
width: 436,
height: 436,
backgroundColor: '#FF7841'
borderRadius: 218,
x: 410,
y: 730,
scale: 1
});
}.call(this));
Как видим, в CoffeeScript, в отличие от JavaScript, нет многочисленных кавычек и запятых, тут они заменяются пробелами?—?поэтому следим, чтобы параметры в описании объекта начинались с новой строки через 3 пробела, иначе будет выдавать ошибку.
3 Шаг:
Чтобы наш круг из невидимого стал видимым, а потом собственно прямоугольником, задаем ему новые состояния с помощью Rectangle.states.add:
Rectangle.states.add
toFront:
scale:0.5
toRectangle:
scale: 1
opacity: 1
x: 37
y: 727
width: 1168
height: 436
borderRadius: 12
All:
scale:7
time:1.1
Мы добавили 3 состояния:
toFront (назвать можно как угодно)?—?при этом состоянии круг становится в половину больше
toRectangle?—?круг становится прямоугольником с параметрами точно такими же, как белый прямоугольник в нашем Sketch файле.
All?—?прямоугольник разворачивается на весь экран. Здесь мы также задали время 1.1 секунд, с которым это происходит.
4 Шаг
Теперь создаем событие (Events)?—?Клик (Click), при котором круг начнет трансформироваться согласно заданным состояниям (States).
Клик будет идти на второй прямоугольник сверху, который называется в Sketch прототипе слоем second.
Пишем?—?$.second.on Events.Click?—?что на человеческом языке читается так?—?при клике на второй прямоугольник происходит следующее?—?и описываем что именно:
$.second.on Events.Click, ->
Rectangle.states.switch("toFront", curve: "spring(300, 30, 0)")
Utils.delay .2, ->
Rectangle.states.switch("toRectangle", time: 1, curve: "easy")
Utils.delay 1, ->
Rectangle.states.switch("All", time: 1, curve: "ease out")
Состояние Rectangle переключается на toFront, потом 0,2 секундная задержка ( Utils.delay .2)?—?затем переключается на toRectangle?—?задержка 1 секунда ( Utils.delay 1) и последнее переключение на состояние All. Здесь также задаются параметры curve и time?—?меняя их, анимация будет происходить по разному.
Получаем:
5 Шаг
Дальше нам нужно задать состояния для остальных слоев, которые мы определили в начале. Пишем:
$.text.states.add
Show:
y: 921
$.time.states.add
Show:
y: 321
$.line.states.add
Show:
x: 36
$.buttonbg.states.add
Show:
y:1509
$.blueButton.states.add
Show:
scale:1
y: 1773
Extend:
scale: 10
$.textButton.states.add
Show:
opacity: 1
y:1845
$.loadingbg.states.add
Show:
y:0
$.oval.states.add
Show:
opacity: 1
$.loadingfinish.states.add
Succes:
y: 861
opacity: 1
Для слоя blueButton мы добавили 2 состояния?—?в первом (Show) он просто показывается, во втором (Extend)?—?расширяется на весь экран.
6 Шаг:
Теперь мы должны уничтожить Rectangle, который мы создали, чтобы он не мешал остальным слоям. Мы пишем:
Rectangle.on "end", ->
Rectangle.destroy()
$.bgorange.opacity = 1
$.text.states.switch("Show", time: 1.2, curve: "ease")
$.time.states.switch("Show", time: 1.2, curve: "ease")
$.line.states.switch("Show", time: .8, curve: "spring(500, 50, 0)")
$.buttonbg.states.switch("Show", time: 2, curve: "easy out")
$.blueButton.states.switch("Show", time: 2, curve: "easy")
$.textButton.states.switch("Show", time: 2, curve: "easy out")
Что означает?—?когда Rectangle закончил свои трансформации?—?он уничтожается (destroy), на его месте показываются указанные нами слои, согласно своим, заданным ранее, состояниям. Слои меняют свои состояния с определенным нами временем и по определенной нами кривой (например: time: 1.2, curve: “ease”)
Не забываем обновлять экран ( ?+R ).
Получаем:
7 Шаг:
Теперь нам нужно создать текстовый слой?—?который будет отображать проценты при loading.
textLabel = new Layer
width: 500
height: 150
backgroundColor: null
textLabel.center()
# Style text
textLabel.style.color = "#fff"
textLabel.style.fontSize = "100px"
textLabel.style.fontFamily = "Proxima Nova"
textLabel.style.lineHeight = "100px"
textLabel.style.textAlign = "center"
Задаем ему backgroundColor: null, то есть пустой (если backgroundColor мы не укажем, то он по умолчанию станет синим) и выравниваем по центру всего экрана благодаря функции textLabel.center().
Описываем параметры стиля # Style text.
Причем заметим, что мы не пишем какой именно текст будет отображаться, иначе мы бы написали html: “какой то Text”.
Чтобы менялись проценты мы добавим строчку кода в конце.
А пока нам нужно вспомнить, что в Sketch прототипе есть слой под названием “Oval”, который должен крутиться вокруг своей оси. Чтобы это происходило?—?мы создаем новую анимацию и называем ее “spinLoad”.
spinLoad = new Animation
layer: $.oval
properties:
rotation: 760
time: 2
curve: "easy"
Rotation мы поставили 760?—?значит он будет крутиться 2 полных оборота и 40 градусов дополнительно (360 + 360 + 40). Можно поставить любое значение градусов.
Добавляем еще одну анимацию showTextLabel:
showTextLabel = new Animation
layer: textLabel
properties:
opacity: 1
time: 4
curve: "easy"
Теперь создаем условие, при котором будет начинаться смена процентов при вращении овала. Пишем:
$.oval.on "change:rotation", ->
percentage = Utils.modulate $.oval.rotation, [0,760], [0,100]
textLabel.html = Utils.round percentage, 0
Тут мы тоже поставили градус вращения овала 760, а смену процентов?—?от 0 до 100 (то есть за 2,4 оборота овала проценты сменяться от 0 до 100).
8 Шаг:
Все, указанные выше, анимации, будет происходить при нажатии на кнопку “Save” (слой blueButton), поэтому создаем новое событие клик (Events.Click) :
$.blueButton.on Events.Click, ->
$.blueButton.states.switch("Extend", time: 1, curve: "easy in")
$.loadingbg.states.switch("Show", time: .5, curve: "spring(500, 50, 0)")
$.textButton.states.switch("default")
$.oval.states.switch("Show", time: 1, curve: "easy in")
spinLoad.start()
Utils.delay 1.5, ->
showTextLabel.start()
Что означает?—?по клику на кнопку, голубой бэкграунд (слой blueButton) начинает расширяться (состояние Extend), слой loadingbg меняет свое состояние на “Show”, текст “Save” на кнопке возвращается к своему первоначальному состоянию: $.textButton.states.switch(“default”) (то есть исчезает), появляется овал и запускается анимация spinLoad.start(), даем задержку 1,5 секунд и запускаем анимацию showTextLabel.start().
И наконец, последнее условие?—?после того, как проценты дойдут до 100 и овал совершит все свои обороты, должен появиться круг с галочкой, означающий успешное завершение операции.
spinLoad.on Events.AnimationEnd, ->
$.loadingfinish.states.switch('Succes')
$.oval.opacity = 0
textLabel.opacity = 0
Получаем итоговую анимацию!
Автор: Абросимова Надежда, дизайнер интерфейсов.