Основы CoffeeScript во Framer

Антон Карташов

Без основ программирования овладеть Фреймером сложно: непонятны ни документация, ни чужие примеры; постоянные ошибки исправляются долго; а код не ощущается как удобный и послушный инструмент. Основы не так сложны, как может показаться.


Простейшая программа может выглядеть так:

2 + 2

Результат вычислений обычно сохраняют в переменную, чтобы использовать его позже:

a = 2 + 2

Чтобы просмотреть содержимое переменной, есть команда print():

a = 2 + 2
print(a)
# » 4

print Содержимое print() выводится справа снизу — во всплывающем белом окне.

Чтобы вывести результат вычислений, не обязательно сохранять его в переменную. Можно сразу написать внутри команды print():

print(5 * 10 + 2)
# » 52

Текст в отличие от чисел берут в кавычки (чтобы компьютер мог отличать имена переменных от значений):

hello = 'bye'

b =  hello
c = 'hello'

print(b) # » "bye"
print(c) # » "hello"

Еще есть зарезервированные слова true и false, чтобы хранить результаты сравнений. Например, «1 > 3» — это ложь, а «10 > 3» — правда:

print(1 > 3)
# » false

print(10 > 3)
# » true

Когда мы их используем, кавычки не нужны — true и false не являются текстом. Их тип данных — логика.

a = true
b = false

Условия

Код может выполняться не только по порядку строка за строкой. Используя сравнения, мы можем попросить программу выбрать один из двух (или более) возможных вариантов:

b = 3

if b < 2
    print 'Less than two'
else
    print 'Greater than or equal to two'

# » "Greater than or equal to two"
Неправильные отступы в CoffeeScript считаются ошибкой. С их помощью мы показываем вложенность блоков. В других языках эту функцию выполняют скобки, а отступы расставляют для лучшей читаемости кода.

Так, например, выглядят условия на JavaScript:

/* JavaScript */
if (b < 2) {
    print('Less than two');
} else {
    print('Greater than or equal to two');
}

Циклы

Еще один способ выполнять код не по порядку — зациклить какую-то его часть на некоторое время. Представьте, что нам нужно вывести все четные числа от 0 до 20:

print(0)
print(2)
print(4)
print(6)
print(8)
print(10)
print(12)
print(14)
print(16)
print(18)
print(20)

С помощью циклов это выполняется гораздо проще:

for x in [0..10]
    print(x * 2)

Такой код позволит быстро вывести даже четные числа от 0 до 2000.

К тому же с циклами быстрее вносить правки: в примере выше достаточно изменить одну строчку. А в предыдущем ему пришлось бы править весь код.


Функции

Внутри программы можно создать подпрограмму — функцию; дать ей любое имя и вызывать в разных частях прототипа:

sum = (x, y) ->
    x + y
summa имя функции
x, y параметры
–> заменяет слово function

Код внутри -> не выполняется, пока мы его не вызовем с какими-то параметрами. Параметры можно вводить каждый раз разные:

# Объявление функции
sum = (x, y) ->
    x + y

# Вызов функции
a = sum(2, 2)
b = sum(5 + 5, 20)
c = sum(a, b)

print a, b, c
# » 4, 30, 34

Кстати, print() — это тоже функция:

print(a)

Функция может быть без параметров. Тогда при ее вызове мы пишем пустые скобки (опять же, чтобы показать, что это запуск функции, а не переменная):

x = 2
y = 3

prod = ->
    x * y

z = prod()
print z
# » 6
CoffeeScript позволяет не ставить скобки при вызове функций, если это не вызывает разночтений.

Но если у функции нет ни одного параметра, то скобки убирать нельзя:

sum x, y
print "Hello world"
print x

prod()

Также функции бывают анонимными:

->
    x + y

Их используют, чтобы передавать в качестве параметров в другие функции либо в объекты.


Объекты

Если говорить грубо, то объект — это набор переменных, объединенных одним именем. Создание объекта выглядит так:

designer =
    name:  'Anton'
    city:  'Moscow'
    age:    29

Переменные внутри объекта называются свойствами.

designer объект
name, age, city свойства объекта
Чтобы при создании нового объекта присвоить значения его свойствам, используют двоеточие, а не знак равенства.

Теперь мы можем обращаться к свойствам объекта так:

print designer.name
year = 2016 - designer.age
designer.city = 'St. Petersburg'

Помимо свойств у объектов могут быть свои функции. Их называют методами. Чтобы создать метод, присвойте одному из свойств анонимную функцию:

designer
    name: 'Anton'
    age: 29
    sayHello: ->
        print 'Hello!'

designer.sayHello()

В работе над прототипами мы чаще будем работать с методами, чем с простыми функциями:

layerA.center()
layerA.placeBehind(layerB)
layerA.animate(x: 200, opacity: 0)
layerA.onClick(-> layerA.x = 0)

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

layerA.center()
layerA.placeBehind layerB
layerA.animate x: 200, opacity: 0
layerA.onClick -> layerA.x = 0

Методы .animate() и .onClick() еще разберем ниже.


Конструкторы и компоненты

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

Чтобы не пересобирать каждый раз однотипные объекты с одними и теми же свойствами, в JavaScript существуют конструкторы — шаблоны заранее настроенных объектов. Конструктор выглядит как обычная функция:

Car = ->
    this.name  = 'BMW'
    this.color = 'black'
    this.speed =  220

Теперь достаточно написать одну фразу new Car:

myCar = new Car

…и у нас появится новый объект с уже заполненными свойствами:

print myCar.name
print myCar.color
print myCar.speed
# » "BMW"
# » "black"
# » 220

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

Student = (obj) ->
    this.name = obj.name ? 'Ivan'
    this.age  = obj.age  ?  17
    this.city = obj.city ? 'Moscow'

Этот код достаточно сложный для новичков, но тут можно увидеть следующее: конструктор проверяет наличие введеных параметров (name ?, age ?, city ?) и если они отсутствуют, то сам заполняет их значениями по умолчанию ('Ivan', 17, 'Moscow').

Теперь мы можем создавать объект с большей гибкостью. Например, задать самостоятельно два свойства, а остальные оставить на совести конструктора:

developer = new Student
    age:   19
    name: 'Egor'

print developer.name
print developer.age
print developer.city
# » "Egor"
# » 19
# » "Moscow"

Во Framer.js есть 6 типов компонентов, на основе которых создаются все прототипы:

1. Слои Layer
2. Фоны BackgroundLayer
3. Скрол ScrollComponent
4. Пэйджинг PageComponent
5. Слайдеры SliderComponent
6. Видео VideoLayer

Например, для объекта Layer создатели Framer.js настроли более 100 свойств и методов, позволяющих вставлять изобржения, обрабатывать драг-н-дроп, вращать слои в 3D и т.д. Использование компонентов ускоряет работу, но без потери гибкости: мы можем менять любые свойства или добавлять свои.

Для создания слоя используют оператор new:

layerA = new Layer

По умолчанию Layer создает полупрозрачный синий квадрат размером 100×100 пикселей и размещает его по координате (0, 0) — в левом верхнем углу телефона.

new Layer

Попробуем вывести значения свойств, которые мы не задавали:

layerA = new Layer

print layerA.opacity
print layerA.width
# » 1
# » 100

Анимация — это функция

Мы уже разобрали функции и методы. Одни из самых используемых методов объекта Layer — это анимации. animate() — сложная функция.

layerA.animate(x: 200, y: 400, options: {time: 0.5, curve: "ease-in"})

CoffeeScript позволяет записать эту функцию так:

layerA.animate
    x: 200
    y: 400
    options:
        time: 0.5
        curve: "ease-in"

События

Чтобы сделать прототип интерактивным, под каждое действие пользователя (клик, скрол, долгое нажатие) пишут свою функцию. Прототип начнет ежесекундно проверять, совершил ли пользователь действие и запускать в ответ на него подпрограмму.

Действия пользователя называют событиями, а функцию, срабатывающую в ответ — колбэком или обработчиком события. Событие работает как обычный метод:

layerA.onClick(functionA)

Например, сделаем по клику слой красным:

# Колбэк
makeRed = ->
    layerA.backgroundColor = "red"

# Событие
layerA.onClick(makeRed)

Колбэк можно не брать в скобки:

layerA.onClick makeRed

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

layerA.onClick(-> layerA.backgroundColor = "red")

А без скобок:

layerA.onClick -> layerA.backgroundColor = "red"

layerA.onClick ->
    layerA.backgroundColor = "red"

myFirstPrototype.framer