Во второй день бета-версии Framer X я построил доказательство концепции компонента параллакса, чтобы изучить возможности платформы. В течение следующих нескольких месяцев я делал несколько спонтанных обновлений всякий раз, когда мне надоедало (или я испытывал стресс) писать сценарии или записывать видео для Framer X + React. :)

В этом и следующем посте я хочу поделиться некоторыми своими ключевыми выводами. Надеюсь, они вам пригодятся. Это техническое углубление, требующее опыта работы с JavaScript и React. Если вы новичок, не торопитесь, чтобы сначала изучить основы, построить более простые вещи и вернуться. Я уверен, ты скоро будешь готов!

Вот некоторые ключевые моменты, которые будут затронуты:

  • как отлаживать во Framer X
  • как повторно использовать элементы управления свойствами существующего компонента, например Scroll
  • как добавить соединитель к компоненту, как у Scroll
  • как следить за положением прокрутки
  • как рекурсивно пройти по дочернему дереву и внести изменения
  • как использовать Context API в React, чтобы избежать клонирования дочернего дерева

Картинка высокого уровня

В пакете Parallax есть два компонента React: Parallax и ParallaxFrame. Их действительно следует называть ParallaxScroll и ParallaxLayer. Но я не хочу ломать чужие прототипы. Надеюсь, displayName скоро получит поддержку и все будет хорошо. В этом посте я назову их ParallaxScroll и ParallaxLayer.

Использовать Parallax несложно. Сначала вы используете ParallaxScroll так же, как Scroll: перетащите его на холст и подключите к более длинному фрейму содержимого. Во-вторых, в прокручиваемом контенте добавьте столько ParallaxLayer, сколько хотите, и соедините их с соответствующими фреймами контента. Эти слои будут перемещаться с другой скоростью, чем остальная часть кадра при прокрутке. В-третьих, настройте направление и скорость каждого ParallaxLayer и получите прибыль!

Как это работает?

ParallaxScroll отслеживает, насколько прокручена страница, обнаруживает все ParallaxLayer в children, вычисляет их позиции на основе их speed свойств и текущей позиции прокрутки и обновляет их позиции.

Как получить доступ к ParallaxLayers в children и обновить их? Я использовал три основных подхода:

  1. Клонируйте все children дерево. Попутно, когда вы видите ParallaxLayer, вычислите и замените его положение. Это требует повторного рендеринга при каждом обновлении позиции прокрутки. (Slooooow!)
  2. Клонируйте все children дерево. Но используйте его только для того, чтобы узнать все ParallaxLayer и заменить их позиции на Animatable. При прокрутке вместо повторного рендеринга и клонирования всего дерева мы можем просто обновить эти Animatable. (Намного лучше!)
  3. Никакого клонирования! Вместо этого «зарегистрируйте» ParallaxLayer и его позицию Animatables всякий раз, когда он монтируется. При прокрутке просто обновите Animatables. (Потрясающие!)

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

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

Как отлаживать во Framer X

Пока мы создаем что-то нетривиальное, нам нужно будет отлаживать наш код. Как отлаживать компоненты во Framer X?

По сути, Framer X - это модифицированный веб-браузер. Внутри работает просто веб-приложение. Так что теоретически мы сможем использовать обычные инструменты веб-разработки для отладки кода во Framer X.

Фактически, в Preview есть опция проверки, которая позволяет нам делать именно это!

Как только Web Inspector откроется, мы будем дома! Мы можем проверить элементы DOM отдельных элементов в предварительном просмотре. Мы можем вставить console.log в наш код и посмотреть результат в консоли. Мы можем отслеживать сетевой трафик. Мы даже можем поставить точки останова в код.

Когда я создавал Parallax, несколько console.log прошли долгий путь, чтобы помочь мне найти то, что мне нужно. Например, я просто распечатал дочернее дерево и нашел, как идентифицировать элемент по его componentIdentifier, настраиваемой опоре, которую использует Framer.

Если Web Inspector недостаточно, можно даже включить React Developer Tools. Я сам не исследовал это, но, видимо, плодовитый Дэн Абрамов доказал, что это возможно.

Сделайте идентичный Scroll

Давайте сначала посмотрим, как сделать компонент, который ведет себя точно так же, как Scroll. Это включает три аспекта:

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

3. На панели свойств он должен иметь те же свойства, что и Scroll.

Самый простой способ - это, конечно, повторно использовать компонент Scroll:

import { Scroll } from "framer";

Затем в методе render мы получим интерфейс коннектора, если сделаем следующее:

<Scroll> {this.props.children} </Scroll>

Фактически, если мы используем опору children почти в любом месте нашего компонента, мы получим интерфейс коннектора. Довольно круто, да? Попробуй сам!

Как добавлять вещи на панель свойств? Вы ведь умеете вставлять туда отдельные элементы управления? Если нет, посмотрите пост Бена.

Но было бы глупо копировать вручную вещи из Scroll. Разве мы не можем просто повторно использовать параметры в Scroll?

Конечно!

export class ParallaxScroll extends Component { 
  static defaultProps = Scroll.defaultProps; 
  static propertyControls = Scroll.propertyControls; 
  ... 
}

На самом деле я предпочитаю следующее, поскольку при желании могу настроить элементы управления свойствами:

export class ParallaxScroll extends Component {
  static defaultProps = { ...Scroll.defaultProps }; 
  static propertyControls = { ...Scroll.propertyControls }; 
  ... 
}

Фактически реальный код для propertyControls выглядит так:

export class ParallaxScroll extends Component { 
  static propertyControls = { 
    ...Scroll.propertyControls, 
    direction: { 
      title: "direction", 
      type: ControlType.SegmentedEnum, 
      options: ["horizontal", "vertical"] }, 
    directionLock: {} 
  }; 
  ... 
}

Видите ли, я скопировал все из Scroll.propertyControls, но обновил свойство direction и удалил directionLock.

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

<Scroll {...this.props}> {this.props.children} </Scroll>

Для тех, кто не знаком с ES6, этот ... синтаксис называется оператором распространения. Это позволяет нам легко дублировать объект и, при необходимости, вносить изменения при создании нового объекта. Я оставлю вас читать документацию для получения подробной информации, поскольку это не является основной темой данной публикации.

Положение прокрутки монитора

Теперь у нас есть ParallaxScroll компонент, который работает точно так же, как Scroll. Пора сделать его немного интереснее. Мы хотим отслеживать положение прокрутки и что-то делать с этим. Верно?

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

export interface ScrollEvents { 
  onScrollStart: ScrollEventHandler; 
  onScroll: ScrollEventHandler; 
  onScrollEnd: ScrollEventHandler; 
  onScrollSessionStart: ScrollEventHandler; 
  onScrollSessionEnd: ScrollEventHandler; 
}

Все эти события выглядят многообещающими. Однако, как только я копнул глубже, я понял, что на самом деле это не то, что я хотел.

Посмотрите этот вывод консоли:

Похоже, что onScroll не отображает текущую позицию прокрутки. Это дает нам просто дельту с момента последнего события прокрутки. Это может быть полезно в других сценариях, но в Parallax нам нужны фактические позиции прокрутки.

С другой стороны, есть onMove событие, которое дает нам именно то, что мы хотим:

Итак, наш компонент ParallaxScroll будет выглядеть примерно так:

export class ParallaxScroll extends Component { 
  ...
  handleScroll = ({x, y}) => { 
    // update positions of ParallaxLayers
  } 
  render() { 
    return ( 
      <Scroll {...this.props} onMove={this.handleScroll}>
        {this.props.children} 
      </Scroll> 
    ); 
  } 
}

Подведение итогов

Хорошо! Я только что показал вам, как выполнять отладку во Framer X, как создать идентичный Scroll и как отслеживать положение прокрутки. Но самое интересное только начинается! В следующем посте я покажу вам, как рекурсивно пройти по дочернему дереву и внести изменения, а также лучшую альтернативу этому с помощью Context API в React.

Если вам нравится то, что вы здесь читаете, подпишитесь на мой информационный бюллетень, который посвящен React и дизайну.

Увидимся в следующий раз!

Первоначально опубликовано на сайте learnreact.design 12 октября 2018 г.