Статьи

Введение в Popmotion: указатели и физика

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

Во второй части мы рассмотрим отслеживание указателя и анимацию на основе скорости.

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

Анимации на основе скорости отличаются от анимации на основе времени, такой как анимация движения, тем, что основным свойством, которое влияет на поведение анимации, является velocity . Сама анимация может занять любое количество времени.

Мы рассмотрим три анимации на основе скорости в Popmotion, spring , decay и physics . Мы будем использовать velocity анимации отслеживания указателя, чтобы запустить эти анимации, и это продемонстрирует, как основанные на скорости анимации могут создавать привлекательные и игривые пользовательские интерфейсы способом, который временные анимации просто не могут.

Во-первых, откройте этот CodePen, чтобы поиграть.

Popmotion предоставляет функцию pointer для отслеживания и вывода координат мыши или указателя одним касанием.

Давайте импортируем это вместе со styler , который позволит нам установить положение мяча.

1
2
3
4
const { pointer, styler } = popmotion;
 
const ball = document.querySelector(‘.ball’);
const ballStyler = styler(ball);

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

1
2
3
4
5
6
7
8
let pointerTracker;
 
const startTracking = () => {
  pointerTracker = pointer().start(ballStyler.set);
};
 
ball.addEventListener(‘mousedown’, startTracking);
ball.addEventListener(‘touchstart’, startTracking);

Мы также хотим, чтобы некоторый код прекратил отслеживание, когда мы отпускаем шар:

1
2
3
4
const stopTracking = () => pointerTracker && pointerTracker.stop();
 
document.addEventListener(‘mouseup’, stopTracking);
document.addEventListener(‘touchend’, stopTracking);

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

Это потому, что по умолчанию pointer выводит позицию указателя относительно страницы .

Чтобы вывести позицию указателя относительно другой точки , в данном случае преобразования x / y шара, мы можем просто передать эту позицию pointer следующим образом:

1
2
3
4
5
6
const startTracking = () => {
  pointerTracker = pointer({
    x: ballStyler.get(‘x’),
    y: ballStyler.get(‘y’)
  }).start(ballStyler.set);
};

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

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

Это будет сложнее, потому что общее физическое усилие, необходимое для прокрутки карусели, будет выше.

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

Popmotion предоставляет функцию, которая может помочь нам отслеживать скорость. Это называется value . Давайте импортировать это:

1
const { pointer, styler, value } = popmotion;

Если говорить технически, все анимации Popmotion известны как действия . Действия — это реактивные потоки значений, которые можно запускать и останавливать.

value , наоборот, является реакцией . Это не может быть остановлено или начато. Он просто пассивно отвечает, когда вызывается его метод update . Он может отслеживать значения и может использоваться для запроса их скорости.

Итак, после того как мы определим ballStyler , давайте определим новое value для ballXY :

1
const ballXY = value({ x: 0, y: 0 });

Всякий раз, когда ballXY обновляет, мы хотим обновить ballStyler . Мы можем передать второй аргумент value , функцию, которая будет запускаться всякий раз, когда ballXY обновляется:

1
const ballXY = value({ x: 0, y: 0 }, ballStyler.set);

Теперь мы можем переписать наш pointer чтобы обновить ballXY вместо ballStyler.set :

1
2
3
4
const startTracking = () => {
  pointer(ballXY.get())
    .start(ballXY);
};

Теперь для любого указателя мы можем вызвать ballXY.getVelocity() и мы получим скорости как x и y , готовые подключиться к нашей анимации на основе скорости.

Первая анимация на основе скорости — spring . Он основан на тех же уравнениях, которые определяют Apple CASpringAnimation, весеннюю анимацию, стоящую за всей этой упругой игривостью iOS.

Импорт:

1
const { pointer, spring, styler, value } = popmotion;

Теперь stopTracking чтобы вместо остановки анимации pointerTracker запускалась spring анимация, например:

1
2
3
4
5
6
7
const stopTracking = () => spring({
  from: ballXY.get(),
  velocity: ballXY.getVelocity(),
  to: 0,
  stiffness: 100,
  damping: 20
}).start(ballXY);

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

Крутая вещь о пружинах — они выразительны. Регулируя mass , stiffness и damping свойства, вы можете получить совершенно разные ощущения пружины.

Например, если вы только измените stiffness выше 1000 , вы можете создать движение, похожее на щелчок с высокой энергией. Затем, изменяя mass до 20 , вы создаете движение, которое выглядит почти как гравитация.

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

Анимация decay , как следует из названия, уменьшает заданную скорость, так что анимация постепенно замедляется до полной остановки.

Это можно использовать для создания эффекта прокрутки импульса на смартфонах, например:

Импортируйте функцию decay :

1
const { decay, pointer, spring, styler, value } = popmotion;

И замените функцию stopTracking следующим:

1
2
3
4
const stopTracking = () => decay({
  from: ballXY.get(),
  velocity: ballXY.getVelocity()
}).start(ballXY);

decay автоматически рассчитывает новую цель на основе предоставленных from и velocity реквизита.

Можно отрегулировать ощущение замедления, путаясь с подпорками, описанными в документах, связанных выше, но, в отличие от spring и physics , decay разработано для работы из коробки.

Наконец, у нас есть physics анимация . Это швейцарский армейский нож Popmotion, основанный на скорости анимации. С его помощью вы можете симулировать:

  • постоянная скорость
  • ускорение
  • пружины
  • трение

spring и decay предлагают сверхточное движение и более широкий спектр ощущений. Вскоре они оба тоже будут вычищены.

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

вместо этого physics представляет собой интегрированное моделирование, более близкое к physics видеоигры. Он работает, один раз за кадр, принимая текущее состояние, а затем изменяя его на основе текущих свойств в данный момент времени.

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

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

Импортировать physics :

1
const { pointer, spring, physics, styler, value } = popmotion;

На этот раз мы собираемся изменить функцию startTracking . Вместо того, чтобы менять ballXY pointer , мы будем использовать physics :

01
02
03
04
05
06
07
08
09
10
const startTracking = () => {
  const physicsAnimation = physics({
    from: ballXY.get(),
    to: ballXY.get(),
    velocity: ballXY.getVelocity(),
    restSpeed: false,
    friction: 0.6,
    springStrength: 400
  }).start(ballXY);
};

Здесь мы устанавливаем и velocity нормальная. friction и springStrength регулируют свойства пружины.

restSpeed: false отменяет поведение по умолчанию остановки анимации при остановке движения. Мы хотим остановить это вручную в stopTracking .

Сама по себе эта анимация ничего не даст, потому что мы установили to цели пружины то же самое, что и from . Так что давайте на этот раз переопределим отслеживание pointer чтобы изменить physics цель весны. В последней строке startTracking добавьте:

1
2
3
pointerTracker = pointer(ballXY.get()).start((v) => {
  physicsAnimation.setSpringTarget(v);
});

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

Анимации на основе скорости в сочетании с отслеживанием указателя могут создавать привлекательные и игривые интерфейсы.

spring может быть использован для создания разнообразных ощущений весны, тогда как decay специально настроен для анимации прокрутки импульса. physics более ограничена, чем любая из них, с точки зрения конфигурируемости, но также предоставляет возможность изменять текущее моделирование, открывая новые возможности взаимодействия.

В следующей и последней части этой вводной серии по Popmotion мы собираемся взять все, что мы узнали в первых двух частях, и использовать их вместе с некоторой легкой функциональной композицией для создания анимации с возможностью очистки, а также с помощью скруббера. чистка с!