Прототип интерактивного текстового поля с помощью Framer. Часть 2
В первой части материала я рассказал о том, как сделать интерактивный инпут. Получилось более 100 строчек кода и, чтобы, не писать столько строк для каждого текстового поля в прототипе мы сделаем класс.
Классы позволяют создавать новые типы объектов определять для них свойства и методы, которые будут работать для всех экземпляров класса. Layer
, TextLayer
, PageComponent
— это все встроенные во Framer классы. Если вы работаете в Figma, то ближайший аналог класса — компонент.
Создаем класс
class InputComponent extends Layer
¬ constructor: (@options={}) ->
¬ ¬ @isFocus = false
¬ ¬ super _.defaults @options,
¬ ¬ ¬ width: 360
¬ ¬ ¬ height: 76
¬ ¬ ¬ backgroundColor: "#fff"
¬ ¬ ¬ inputLabel: "Label"
Разберем, что это значит:
class InputComponent extends Layer
— создаем новый класс под названием InputComponent
на основе встроенного во Framer класса Layer
. Наш новый класс будет наследовать все свойства и методы от класса Layer
и расширять его.
constructor: (@options={}) ->
— специальный метод, который используется для создания классов.
@isFocus
— будем использовать для проверки, является ли наше поле в фокусе.
super _.defaults @options,
— конструкция, с помощью которой мы получаем свойства и родительского класса (т.е. Layer
), переопределяем те, которые нам интересны и добавляем новые. _.defaults
— это функция из встроенной в Framer библиотеки lodash.
Элементы текстового поля
1. Название
Добавим название текстового поля, для этого внутри нашего класса добавим TextLayer
. И добавим два состояния: базовое (лейбл по центру поля) и активное (лейбл смещается вверх и уменьшается):
@label = new TextLayer
¬ parent: @
¬ text: @options.inputLabel
¬ fontSize: 16
¬ lineHeight: 1.25
¬ y: 18
@label.states =
¬ basic:
¬ ¬ fontSize: 16
¬ ¬ y: 18
¬ active:
¬ ¬ fontSize: 12
¬ ¬ y: 0
2. Линия подчеркивания
Добавляем линию и ее состояния:
@line = new Layer
¬ parent: @
¬ width: @.width
¬ height: 2
¬ y: 54
¬ backgroundColor: "#808080"
¬ opacity: 0.5
@line.states =
¬ basic:
¬ ¬ backgroundColor: "#808080"
¬ ¬ opacity: 0.5
¬ hover:
¬ ¬ backgroundColor: "#808080"
¬ ¬ opacity: 1
¬ active:
¬ ¬ backgroundColor: "#000000"
¬ ¬ opacity: 1
3. Поле ввода
@inputField = document.createElement("input")
@inputField.style["width"] = @.width + 'px'
@inputField.style["height"] = "56px"
@inputField.style["color"] = "#000000"
@inputField.style["font-size"] = "16px"
@inputField.style["background-color"] = "rgba(0,0,0,0)"
@inputField.style["outline"] = "none"
@inputField.value = ""
@._element.appendChild(@inputField)
События
Нам осталось сделать наше поле интерактивным. Нам нужно создать две пары событий для нашего текстового поля:
- При наведении и убирании курсора мыши;
- При фокусе и анфокусе поля.
Для наведения будем использовать знакомые события onMouseOver
и onMouseOut
:
@.onMouseOver () ->
¬ @line.animate("hover")
@.onMouseOut () ->
¬ if !@isFocus # Если курсор стоит в поле, то обратную анимацию не делаем
¬ ¬ @line.animate("basic")
Для фокуса и, так называемого, блюра (то есть убирании фокуса с поля) будем использовать onfocus
и onblur
. Я сейчас не буду вдаваться в подробности, почему мы импользуем разный синтаксис, можете отдельно почитать про fat arrows.
@inputField.onfocus = =>
¬ @isFocus = true
¬ @line.animate("active")
¬ @label.animate("active")
@inputField.onblur = =>
¬ @isFocus = false
¬ @line.animate("basic")
¬ if @inputField.value == "" # Если пользователь оставил поле пустым, то возвращаем лейбл на место
¬ ¬ @label.animate("basic")
Ура, наш класс готов! Осталось превратить его в модуль.
Класс → Модуль
Модуль — это отдельный файл содержащий один или несколько классов, который лежит в папке modules. Так проще организовывать и подключать только те классы, которые вам нужны.
Для появления модуля нужно:
- При создании класса перед названием напиать
exports.
. Таким образом первая строка нашего модуля будет выглядеть так:
class exports.InputComponent extends Layer
- Сохранить наш класс в отдельный файл с названием
InputComponent.coffee
в папке/modules
нашего прототипа. Обратите внимание: если в вашем модуле только один класс, то модуль должен называться точно также, как класс. Если в модуле несколько классов, то название модуля не должно повторять название какого-либо класса в модуле. - Подключить модуль к прототипу с помощью команды
{InputComponent} = require "InputComponent"
Теперь посмотрим, как мы можем им пользоваться.
Прототип формы
Сделаем функцию, в которую мы будем передавать список нужных полей, и она будет строить форму:
createForm = (fieldsList) ->
¬ formWidth = 412 # Ширина создаваемой формы
¬ gap = 25 # Отступ между полями
¬ firstInputX = 50 # Положение по X первого поля
¬ firstInputY = 5 # Положение по Y первого поля
¬ inputs = [] # Создаем пустой массив в который запишем все создаваемые поля
¬ for field in fieldsList # Проходим циклом по всем названиям полей, которые получили в переменной fieldsList и создаем соответствующие поля
¬ ¬ inputs[field] = new InputComponent
¬ ¬ width: formWidth
¬ ¬ x: firstInputX
¬ ¬ y: firstInputY + fieldsList.indexOf(field)*(76+gap)
¬ ¬ inputLabel: field
Нам остается вызвать написанную функцию…
createForm(["First name", "Last name", "E-mail", "Phone number"])
… и мы видим готовую форму!
Готовый прототип с модулем InputComponent можно взять на гитхабе. Этот же модуль вы можете использовать в любом своем прототипе скопировав его в папку modules.
Полезные ссылки: