Pull to refresh

Framer Tutorial: Custom device, Input поля, перетекающий loading и радуга в конце

Reading time 9 min
Views 3.8K
В моей прошлой статье я рассказала про то, как начать с работу с Framer, создавать красивые прототипы с помощью импорта слоев из Sketch, а также показала, как создать симпатичный loading с процентами.

После того, как статью опубликовали на habrahabr — вот здесь, я решила продолжить свои эксперименты с Framer, просто потому что уже не вижу своих дизайнерских будней без этой увлекательной штуковины.

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

Итак, вот что мы создадим в этот туториале:

Три экрана простого прототипа:

Экран ввода логина и пароля
Экран загрузки
Экран приветствия

Результат можно посмотреть здесь: http://share.framerjs.com/s6q2nzb8xf3f/

image

Все элементы интерфейса мы будем создавать сразу во Framer (без импорта слоев из Sketch или Photoshop).

Мы усложним задачу, попробовав создать прототип для девайса, которого нет в списке Viewer во Framer. Но, сперва, смотрим какие девайсы все же есть:

image

Как видим, здесь нет Samsung Galaxy Tablet. Его то мы и и будем использовать.

Если для прототипа важна точность, тогда нужно найти картинку Samsung Galaxy Tablet равную натуральным размерам планшета (2560*1600 px).

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

Я использовала вот эту картинку.

Ее размеры (1500*1000 px).

Записываем параметры:

Framer.DeviceView.Devices["custom"] =
"deviceType": "tablet"
"screenWidth": 1258
"screenHeight": 784
"deviceImage":"http://www.androidheadlines.com/wp-content/uploads/2014/09/81WUwHeaVKL._SL1500_.jpg"
"deviceImageWidth": 1500
"deviceImageHeight": 1000
Framer.Device.deviceType = "custom"

Размеры экрана 1258 * 784 нам подходят, так как полностью закрывают то, что изображено на экране девайса на картинке.

Создадим бэкграунд слой для экрана “Sign In” :

Framer.DeviceView.Devices["custom"] =
"deviceType": "tablet"
"screenWidth": 1258
"screenHeight": 784
"deviceImage":"http://www.androidheadlines.com/wp-content/uploads/2014/09/81WUwHeaVKL._SL1500_.jpg"
"deviceImageWidth": 1500
"deviceImageHeight": 1000
Framer.Device.deviceType = "custom"


Теперь, можно рисовать на этом слое поля для логина, пароля и кнопку “Sign in”.

Для того, чтобы можно было в поля вписывать пароль и логин, нужно скачать модуль InputField.coffee. Я использовала модуль, созданный Jordan Robert Dobson. Скачать его можно здесь.

После загрузки, заходим в папку “modules,” копируем файл InputField.coffee и вставляем в папку “modules” своего проекта.

image

Во Framer записываем:

Framer.DeviceView.Devices["custom"] =
"deviceType": "tablet"
"screenWidth": 1258
"screenHeight": 784
"deviceImage":"http://www.androidheadlines.com/wp-content/uploads/2014/09/81WUwHeaVKL._SL1500_.jpg"
"deviceImageWidth": 1500
"deviceImageHeight": 1000
Framer.Device.deviceType = "custom"


Теперь, мы можем создавать input — поля. Начнем с поля пароль, так как это поле будет расположено по центру экрана:

Password = new InputField
  width: 640
  height: 100
  color: "#212121"
  backgroundColor: "#fff"
  borderWidth: 1
  borderColor: "#60B9F4"
  borderRadius: 10
  fontSize: 36
  fontFamily: "Proxima Nova"
  indent:   48
  placeHolder: "Type Password"
  placeHolderFocus: "********"
Password.center()


В параметрах, мы, кроме основных характеристик, также описали, что должно отображаться в поле, когда мы будем ставить туда курсор — в данном случае просто звездочки: placeHolderFocus: “********”

Дальше, записываем параметры поля логин:

Login = new InputField
  width: 640
  height: 100
  color: "#212121"
  y: Password.y - 130
  backgroundColor: "#fff"
  borderWidth: 1
  borderColor: "#60B9F4"
  borderRadius: 10
  fontSize: 36
  fontFamily: "Proxima Nova"
  indent:   48
  placeHolder: "Type Login"
  placeHolderFocus: "example@gmail.com"
Login.centerX()


Как видим, мы задали этому полю положение y = Password.y — 130, то есть на 130 px выше поля Password и выровняли его по X координате по центру.

Теперь создаем кнопку, которая будет находиться ниже поля Password на 160 px:

Button = new Layer
   width: 640
   height: 100
   borderWidth: 1
   borderColor: "#60B9F4"
   borderRadius: 10
   html: "SIGN IN"
   backgroundColor: "#60B9F4"
   y: Password.y + 160   
Button.centerX()
Button.style.color = "#fff"
Button.style.fontSize = "48px"
Button.style.fontFamily = "Proxima Nova"
Button.style.lineHeight = "100px"
Button.style.textAlign = "center"


Напишем заголовок данного экрана. Например, фразу “Welcome to our system!” .

Чтобы в дальнейшем не задавать заголовку отдельно определенное состояние, можем задать для него родительский слой — superLayer. Им будет, например, поле Password. Теперь, заголовок будет менять состояние вместе с родительским для него слоем — полем Password.

Text = new Layer
   backgroundColor: null
   width: 640
   html: "Welcome to our system!"
   color: "#212121"
   superLayer: Password
   y: - 240
Text.centerX() 
Text.style.fontSize = "48px"
Text.style.fontFamily = "Proxima Nova"
Text.style.textAlign = "center"


Так как мы уже можем вписывать в поля свой логин и пароль, настало время нажать на кнопку “Sign in”. Сделаем для нее hover.

В состоянии hover кнопка и надпись на ней будут менять цвет и немного увеличиваться в размерах. Чтобы цвет менялся плавно, предварительно зададим кнопке стиль transition и spring curve:

Button.style.transition = "background-color 1s ease"
Button.states.animationOptions = curve: "spring(600,60,0)"


Теперь, можно записывать событие onMouseOver:

Button.onMouseOver ->
 Button.style.backgroundColor = "white"
 Button.style.color = "#60B9F4"
 Button.scale = 1.05


Чтобы кнопка возвращалась в исходное состояние, когда мы уводим от нее мышку, нужно создать обратное событие onMouseOut:

Button.onMouseOut ->
 Button.style.backgroundColor = "#60B9F4"
 Button.style.color = "white"
 Button.scale = 1


Первый экран готов.

image

Следующий экран загрузки будет появляться после того, как мы нажмем на кнопку.

Первым делом нам надо убрать экран логина и пароля. Пусть он будет сдвигаться влево при нажатии на кнопку. Задаем состояния для элементов экрана.

signInBackground.states.add
   Pressed: 
      x: -2000
Button.states.add
    Pressed:
      x: -2000  
Login.states.add
   Pressed:
      x: -2000
Password.states.add
   Pressed:
      x: -2000


Теперь все элементы имеют новое состояние — их положение сдвинуто влево на 2000 px от первоначального. Как видим, мы не задаем отдельное состояние для слоя Text — так как он будет сдвигаться вместе со своим родительским элементом — слоем signInBackground.

Создаем событие — клик на кнопку:

Button.on Events.Click, ->
 Button.states.switch("Pressed", time: .4, curve: "easy out")
 Password.states.switch("Pressed", time: .4, curve: "easy")
 Login.states.switch("Pressed", time: .4, curve: "easy")
 signInBackground.states.switch("Pressed", time: 1, curve: "easy out")


Здесь мы также написали время, с которым каждый элемент будет менять свое состояние, а также задали кривую этих изменений.

Начинаем создавать экран загрузки. Нам понадобится 2 бэкграунд слоя. Первый мы сделаем голубым, второй белым.

Причем для второго слоя родительским сделаем первый слой.

Второму слою зададим режим наложения — экран (mixBlendMode : “screen”).

loadColor = new BackgroundLayer
   backgroundColor: "#60B9F4"
  
backgroundLayer = new BackgroundLayer
   backgroundColor: "#fff"
   style: mixBlendMode : "screen"
   superLayer: loadColor


Рисуем 4 абсолютно одинаковых круга (0, 1, 2, 3, 4) с абсолютно одинаковым положением в центре экрана. Чтобы не рисовать отдельно каждый, упростим себе задачу, создав массив (array).

circles = []
for i in [0..3]
 circle = new Layer
  backgroundColor: "black"
  superLayer: backgroundLayer
  borderRadius: 100
  width: 50
  height: 50
  blur: 20
  name: "circle #{i}"
 circle.center()
 circles.push(circle)


Родительским слоем для них сделаем слой backgroundLayer. Так как этот слой имеет другой режим наложения (screen) и для него родительским слоем является слой loadColor голубого цвета, то круги стали голубыми, хотя мы задали им backgroundColor: “black”.

Например, если мы напишем вместо “black” — “red”, то они станут розовыми.

Мы также сделали круги размытыми (blur: 20), чтобы они плавно перетекали один в другой.

image

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

Решается все просто. Мы возвращаемся выше, к тому месту, где описывали слой backgroundLayer и добавляем еще одну строчку — задаем сильный контраст:

backgroundLayer = new BackgroundLayer
   backgroundColor: "#fff"
   style: mixBlendMode : "screen"
   superLayer: loadColor
   contrast: 10000


Теперь, круг выглядит как круг, а не как размытое пятно.

image

Заставим круги двигаться. Для этого создадим анимации.

Их будет 2 — для первого и второго круга, а круг 0 и 3 не двигаются.

load1 = new Animation
   layer: circles[1]
   properties: 
         x: circles[0].x - 180
    time: .7
    curve: "easy"
load2 = new Animation
   layer: circles[2]
   properties: 
         x: circles[0].x - 90
    time: .7
    curve: "easy"


Здесь, первый круг (circles[1]) отодвигается от нулевого круга на 180 px по х координате — x: circles[0].x — 180. Второй круг соответственно — на 90 px.

Мы создали движение в одну сторону — влево, теперь создадим такое же движение вправо.

load11 = new Animation
   layer: circles[1]
   properties: 
         x: circles[0].x + 180
    time: .7
    curve: "easy"
load22 = new Animation
   layer: circles[2]
   properties: 
         x: circles[0].x + 90
    time: .7
    curve: "easy"


Возвращаемся к нашему событию — клик по “Sign In” кнопке и дописываем туда наши новые анимации:

Button.on Events.Click, ->
 Button.states.switch("Pressed", time: .4, curve: "easy out")
 Password.states.switch("Pressed", time: .4, curve: "easy")
 Login.states.switch("Pressed", time: .4, curve: "easy")
 signInBackground.states.switch("Pressed", time: 1, curve: "easy out")
 load1.start()
 Utils.delay .1, ->
 load2.start()
 Utils.delay .1, ->
load2.on Events.AnimationEnd, ->
    load11.start()
    Utils.delay .1, ->
    load22.start()
    Utils.delay .1, ->


Как видим, между каждым перемещением кругов, мы даем задержку в 0,1 секунды ( Utils.delay .1). Движение в правую сторону происходит после того, как заканчивается движение в левую сторону . Поэтому, мы написали load2.on Events.AnimationEnd, ->….., что значит —

когда заканчивается load2 , начинается load11 и load22.

Чтобы анимация длилась вечно , замыкаем цикл, дописывая ниже:

load22.on Events.AnimationEnd, ->
 load1.start()
 Utils.delay .1, ->
 load2.start()
 Utils.delay .1, ->


Второй экран готов.

Приступаем к 3 экрану с приветствием и изменением цвета.

Создадим анимацию для круга 0, при котором он будет расширяться на весь экран:

big = new Animation
   layer: circles[0]
   properties: 
      borderRadius: 0
      width: backgroundLayer.width
      height: backgroundLayer.height
      y:0
      x: 0
      blur: 0
      curve: "spring(300, 30, 0)"


Теперь, дописываем старт этой анимации ниже в событии клик по кнопке, делая перед ним задержку 2:

Utils.delay 2, ->
    big.start()


Нарисуем невидимый слой TextFinish, который представляет собой слово “Hello!”:

TextFinish = new Layer
   backgroundColor: null
   width: 640
   y: 300
   opacity: 0
   html: "Hello!"
   color: "#fff"
TextFinish.centerX()
TextFinish.style.fontSize = "148px"
TextFinish.style.fontFamily = "Proxima Nova"
TextFinish.style.textAlign = "center"
TextFinish.style.lineHeight = "154px"


Добавим ему 3 состояния (одно, при котором он становится видимым, и 2 остальных, где слово Hello меняется на Привет и на Вітаю):

TextFinish.states.add
   activate:
      opacity:1
   nactivate:
      html: "Привет!"
   oactivate:
      html: "Вітаю!" 
TextFinish.states.animationOptions = time: .6, curve: "easy out"


Дописываем смену состояний слоя TextFinish в событие клик по кнопке. Теперь полностью все событие выглядит так:

Button.on Events.Click, ->
 Button.states.switch("Pressed", time: .4, curve: "easy out")
 Password.states.switch("Pressed", time: .4, curve: "easy")
 Login.states.switch("Pressed", time: .4, curve: "easy")
 signInBackground.states.switch("Pressed", time: 1, curve: "easy out")
 load1.start()
 Utils.delay .1, ->
 load2.start()
 Utils.delay .1, ->
load2.on Events.AnimationEnd, ->
    load11.start()
    Utils.delay .1, ->
    load22.start()
    Utils.delay .1, ->
load22.on Events.AnimationEnd, ->
 load1.start()
 Utils.delay .1, ->
 load2.start()
 Utils.delay .1, ->
 Utils.delay 2, ->
    big.start()
    TextFinish.states.next()


Здесь не хватает только одной, последней строчки — изменения цвета бэкграунда. Чтобы ее добавить, добавляем слою loadColor два состояния:

loadColor.states.add
   active:
      backgroundColor: "#f87b36"
   nactivate:
      backgroundColor: "#fa635f"
loadColor.states.animationOptions = time: .6, curve: "easy"


И дописываем в событие:

loadColor.states.next()


Готово!

Я надеюсь, что мой рассказ пролил свет на то, какие крутые вещи каждый дизайнер может создать благодаря Framer. С удовольствием послушаю ваши мысли по поводу Framer, и вообще как считаете — пора ли нам дизайнерам начинать кодить?

На этом заканчиваю, но буду и дальше писать статьи на полезные материалы по дизайнерскому прототипированию — программированию.
Tags:
Hubs:
+1
Comments 0
Comments Leave a comment

Articles