Как получить классные анимации в ваших проектах React

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

Проект сделан на Next и TypeScript, но вы можете применить все концепции к обычному проекту React на JavaScript.

Пример проекта

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

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

Вы можете посмотреть демонстрацию примера проекта в реальном времени:

  • Без Framer Motion*
  • Окончательная версия

*В этой версии добавлены CSS-анимации, чтобы сделать игру хотя бы играбельной.

Что такое Framer Motion?

Это библиотека анимации для React, созданная Framer, цель которой — позволить нам писать анимацию декларативно и без усилий с бесшовной интеграцией в экосистему React.

Вы можете добиться тех же результатов, используя чистый CSS, но Framer Motion позволит вам быстро внедрить красивую и плавную анимацию, сохраняя при этом более простой код, работая с реквизитами, как вы привыкли в React, и давая вам возможность реагировать на изменения состояния и другие виды поведения React.

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

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

Установка

Просто установите пакет framer-motion в проект:

yarn add framer-motion
npm install framer-motion
Войдите в полноэкранный режим Выйти из полноэкранного режима

После установки просто импортируйте компонент motion и используйте его в любом HTML-теге:

import { motion } from "framer-motion"

<motion.div animate={{ scale: 0.5 }} />
Войти в полноэкранный режим Выйти из полноэкранного режима

Motion обернет все элементы HTML и добавит свойства анимации, которые мы увидим в этом руководстве.

Основные анимации

Как мы видели ранее, добавить анимацию очень просто — достаточно использовать свойство animate на компоненте, обернутом с помощью motion.

Итак, в качестве первого теста давайте анимируем кнопку Play, расположенную на странице Intro.

// components/Intro

import { motion } from 'framer-motion'

const Intro = ({ next }: { next: () => void }) => {
  return (
    <div className="flex-vertical">
      <h1>Memory Game</h1>
      <motion.button
        onClick={next}
        animate={{ scale: 1.5 }}
        transition={{ delay: 1 }}
      >
        Play
      </motion.button>
    </div>
  )
}

export default Intro
Вход в полноэкранный режим Выйти из полноэкранного режима
  • Мы обернули тег button компонентом motion, что позволяет нам использовать дополнительные свойства, такие как animate.
  • Представленная анимация предназначена для увеличения масштаба на 1,5
  • Чтобы можно было увидеть разницу в размерах, мы добавляем дополнительное свойство transition, которое мы подробно рассмотрим позже, для задержки анимации на 1 секунду.

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

В приведенном выше примере framer motion по умолчанию передает нам свойство initial со всеми значениями по умолчанию, но мы можем определить его и переопределить все, что захотим, для различных состояний анимации.

// components/Intro

import { motion } from 'framer-motion'

const Intro = ({ next }: { next: () => void }) => {
  return (
    <div className="flex-vertical">
      <h1>Memory Game</h1>
      <motion.button
        onClick={next}
        initial={{ rotate: -360, scale: 3 }}
        animate={{ rotate: 0, scale: 1 }}
        transition={{ duration: 1 }}
      >
        Play
      </motion.button>
    </div>
  )
}

export default Intro
Вход в полноэкранный режим Выход из полноэкранного режима

Этим мы переключаемся с большой кнопки Play на кнопку нормального размера при вращении.

Переходы

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

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

// components/Intro

import { motion } from 'framer-motion'

const Intro = ({ next }: { next: () => void }) => {
  return (
    <div className="flex-vertical">
      <h1>Memory Game</h1>
      <motion.button
        onClick={next}
        animate={{ scale: 1.5 }}
        transition={{
          duration: 0.4,
          yoyo: Infinity,
        }}
      >
        Play
      </motion.button>
    </div>
  )
}

export default Intro
Вход в полноэкранный режим Выход из полноэкранного режима
  • Мы удалили параметр задержки, но он будет работать и с ним.
  • Теперь длительность 0,4 секунды — это общая длительность анимации.
  • Наконец, yoyo — это специальное свойство для возврата назад и вперед между начальным состоянием и анимацией, в данном случае бесконечное количество раз. С помощью этого свойства вы можете контролировать, сколько раз вы хотите запустить анимацию.

Переходы позволяют нам определить тип анимации, которую мы хотим использовать, мы можем использовать:

// components/Intro

<motion.button
        onClick={next}
        animate={{ rotate: 360 }}
        transition={{
          type: 'tween',
          duration: 0.4,
        }}
      >
        Play
</motion.button>
Войти в полноэкранный режим Выйти из полноэкранного режима
// components/Intro

<motion.button
        onClick={next}
        initial={{ x: '100vw' }}
        animate={{ x: 0 }}
        transition={{
          type: 'spring',
          stiffness: 300,
        }}
      >
        Play
</motion.button>
Войти в полноэкранный режим Выйти из полноэкранного режима
// components/Intro

<motion.button
        onClick={next}
        animate={{ rotate: 360 }}
        transition={{ type: 'inertia', velocity: 450 }}
      >
        Play
</motion.button>
Войти в полноэкранный режим Выход из полноэкранного режима

Попробуйте эти различные параметры в проекте-образце и проверьте получившиеся анимации.

Совет: Некоторые из приведенных выше настроек несовместимы с некоторыми свойствами, если вы используете TypeScript, ошибки будут появляться, если какая-либо комбинация не имеет смысла.

Еще одно полезное использование переходов — оркестровки, о которых мы расскажем позже, но сначала нужно знать несколько вещей.

Варианты

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

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

Давайте рефакторим один из примеров кнопки Play с помощью вариантов:

// components/Intro

import { motion } from 'framer-motion'

const buttonVariants = {
  hidden: {
    x: '100vw',
  },
  visible: {
    x: 0,
    transition: {
      type: 'spring',
      stiffness: 300,
    },
  },
}

const Intro = ({ next }: { next: () => void }) => {
  return (
    <div className="flex-vertical">
      <h1>Memory Game</h1>
      <motion.button
        onClick={next}
        initial="hidden"
        animate="visible"
        variants={buttonVariants}
      >
        Play
      </motion.button>
    </div>
  )
}

export default Intro
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь мы заменили весь код внутри компонента на:

  • Тег, относящийся к состоянию initial, в данном случае hidden (вы можете назвать его как угодно).
  • Тег, относящийся к состоянию animate (также содержит детали перехода).
  • Объект variants, который использует этот компонент.

Совет: Вы можете перенести все варианты в отдельный файл, как вы бы сделали это с обычным CSS или любой другой библиотекой CSS-in-JS, чтобы упростить ваш компонент.

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

Оркестровка

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

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

// components/SelectDeck

import { motion } from 'framer-motion'

import { DECKS } from '@/utils/Decks'

import Button from '../ListedButton'
import { childVariants, containerVariants } from './SelectDeck.variants'

type Props = {
  next: () => void
  setDeck: (deckName: string) => void
}

const SelectDeck: React.FC<Props> = ({ next, setDeck }) => {
  const handleSelect = (
    event: React.MouseEvent<HTMLButtonElement, MouseEvent>
  ) => {
    setDeck(event.currentTarget.value)
    next()
  }

  return (
    <motion.div
      variants={containerVariants}
      initial="hidden"
      animate="visible"
    >
      <h2>Select Deck</h2>
      <div className="flex-vertical stack">
        {Object.keys(DECKS).map((theme: string) => (
          <motion.div key={theme} variants={childVariants}>
            <Button onClick={handleSelect} value={theme}>
              {theme}
            </Button>
          </motion.div>
        ))}
      </div>
    </motion.div>
  )
}

export default SelectDeck
Вход в полноэкранный режим Выход из полноэкранного режима

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

// components/SelectDeck//SelectDeck.variants.ts

const containerVariants = {
  hidden: {
    opacity: 0,
    x: '100vw',
  },
  visible: {
    opacity: 1,
    x: 0,
    transition: {
      type: 'spring',
      mass: 0.4,
      damping: 8,
      when: 'beforeChildren',
      staggerChildren: 0.4,
    },
  },
}

const childVariants = {
  hidden: {
    opacity: 0,
  },
  visible: {
    opacity: 1,
  },
}

export { containerVariants, childVariants }
Вход в полноэкранный режим Выход из полноэкранного режима
  • В transition мы определяем два реквизита, которые определяют оркестровку when и staggerChildren.
    • В данном случае мы указываем beforeChildren, чтобы анимация родителя запускалась и завершалась раньше анимации детей.
    • Параметр staggerChildren будет применять каждую дочернюю анимацию по очереди с задержкой в 0,4 секунды между ними.

Другими способами оркестровки являются:

  • Использование delay, как мы делали в первом примере.
  • Задержка детской анимации с помощью delayChildren вместо того, чтобы делать ее зависимой от родительской анимации.
  • Повторять анимацию с помощью repeat.

С помощью оркестровки вы можете создавать мощные комбинации.

Жесты

В дополнение к встроенным слушателям React, framer motion включает жесты, которые позволяют нам выполнять анимацию в других ситуациях, таких как hover, tap, pan, viewport и drag.

Например, давайте вернемся к нашей кнопке Play на вступительном экране и выполним другие анимации, когда мы наводим курсор мыши на кнопку и касаемся ее:

// components/Intro

import { motion } from 'framer-motion'

const buttonVariants = {
  hidden: {
    x: '100vw',
  },
  visible: {
    x: 0,
    transition: {
      type: 'spring',
      stiffness: 300,
    },
  },
  hover: {
    scale: 1.5,
  },
  tap: {
    scale: 0.5,
  },
}

const Intro = ({ next }: { next: () => void }) => {
  return (
    <div className="flex-vertical">
      <h1>Memory Game</h1>
      <motion.button
        onClick={next}
        initial="hidden"
        animate="visible"
        whileHover="hover"
        whileTap="tap"
        variants={buttonVariants}
      >
        Play
      </motion.button>
    </div>
  )
}

export default Intro
Вход в полноэкранный режим Выход из полноэкранного режима
  • Мы добавляем слушателей whileHover и whileTap к новым вариантам hover и tap, как всегда, вы можете назвать их как угодно. С этими изменениями теперь, когда мы наводим курсор на кнопку, она будет увеличиваться, а когда мы нажимаем на нее, она будет уменьшаться.

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

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

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

// components/Card/Card.tsx

<motion.div
      initial="hidden"
      variants={cardVariants}
      animate={controls}
      whileInView="show"
      viewport={{ once: true }}
    >
...
</motion.div>
Вход в полноэкранный режим Выход из полноэкранного режима

*Я упростил этот компонент для этой статьи, но вы можете увидеть фактический код по ссылке выше.

Использование whileInView и передача варианта, который мы хотим запустить, — это все, что нам нужно для запуска анимации в этот точный момент. Мы также используем viewport once, чтобы запустить анимацию только один раз, а не каждый раз, когда этот элемент возвращается в представление.

Ключевые кадры

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

Например, давайте добавим анимацию для карт при их размещении на доске:

// components/Card/

import { motion } from 'framer-motion'

import { Card as TCard } from '@/types'

import styles from './Card.module.css'

const cardVariants = {
  hidden: { scale: 0, rotate: 0 },
  flip: {
    scale: [1, 0.5, 0.5, 1],
    rotate: [0, 180, 360, 0],
    transition: {
      duration: 0.8,
    },
  },
}

type Props = {
  card: TCard
  handleSelection: (card: TCard) => void
  flipped: boolean
  disabled: boolean
}

export default function Card({
  card,
  handleSelection,
  flipped,
  disabled,
}: Props) {
  const handleClick = () => {
    if (!disabled) handleSelection(card)
  }

  return (
    <motion.div
      className={styles.card}
      variants={cardVariants}
      initial="hidden"
      animate="flip"
    >
      <div className={`${styles.inner} ${flipped ? styles.flipped : ''}`}>
        <img className={styles.front} src={card.imageURL} alt="card front" />
        <img
          src={`${card.imageURL.split('/').slice(0, -1).join('/')}/cover.jpg`}
          alt="card back"
          className={styles.back}
          onClick={handleClick}
        />
      </div>
    </motion.div>
  )
}
Войти в полноэкранный режим Выход из полноэкранного режима

Внесены изменения:

  • Преобразован в motion div контейнер и добавлены состояния cardVariants, hidden и flip.
  • В cardVariants вместо использования значения в scale и rotation используется массив для указания точных значений в каждом ключевом кадре.

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

Управление анимацией

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

В качестве простого примера, допустим, мы хотим сделать две анимации, кроме перехода из скрытого состояния в видимое, на вступительном экране кнопки Play:

// components/Intro

import { useEffect } from 'react'

import { motion, useAnimation } from 'framer-motion'

const buttonVariants = {
  hidden: {
    x: '500vw',
  },
  visible: {
    x: 0,
    transition: { type: 'spring', delay: 0.3, duration: 1 },
  },
  loop: {
    scale: 1.5,
    transition: {
      duration: 0.4,
      yoyo: Infinity,
    },
  },
}

const Intro = ({ next }: { next: () => void }) => {
  const controls = useAnimation()

  useEffect(() => {
    const sequence = async () => {
      await controls.start('visible')
      return controls.start('loop')
    }

    sequence()
  }, [controls])

  return (
    <div className="flex-vertical">
      <h1>Memory Game</h1>
      <motion.button
        onClick={next}
        variants={buttonVariants}
        initial="hidden"
        animate={controls}
      >
        Play
      </motion.button>
    </div>
  )
}

export default Intro
Вход в полноэкранный режим Выход из полноэкранного режима
  • Как вы видите, после перехода от hidden к visible мы хотим сделать другую анимацию, которая в данном случае является анимацией Infinity yo-yo, одно из решений — взять момент точки монтирования компонента с помощью useEffect и выполнить необходимые действия.
  • Теперь кнопка имеет controls как значение animate, которое извлекается из хука useAnimation.
  • Когда компонент смонтирован, мы можем использовать controls для запуска любой анимации, которая возвращает обещание, разрешающееся по окончании анимации.

Controls поддерживает как варианты, так и JS-объект, который мы рассматривали в начале статьи.

Анимации выхода

В дополнение к initial и animate существует третье состояние exit, которое мы можем использовать для создания анимации при удалении компонента из DOM.

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

// components/Intro/

import { useEffect } from 'react'

import { motion, useAnimation } from 'framer-motion'

const containerVariants = {
  exit: {
    x: '-100vh',
    transition: { ease: 'easeInOut' },
  },
}

const Intro = ({ next }: { next: () => void }) => {
  const controls = useAnimation()

  useEffect(() => {
    const sequence = async () => {
      await controls.start('visible')
      return controls.start('loop')
    }

    sequence()
  }, [controls])

  return (
    <motion.div
      className="flex-vertical"
      variants={containerVariants}
      exit="exit"
    >
      <h1>Memory Game</h1>
      <button onClick={next}>Play</button>
    </motion.div>
  )
}

export default Intro
Вход в полноэкранный режим Выход из полноэкранного режима
  • В этом случае мы добавляем вариант exit, который перемещает содержимое влево, в сторону от области просмотра.

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

// pages/index.tsx

import { useState } from 'react'

import { AnimatePresence } from 'framer-motion'
import type { NextPage } from 'next'

import Game from '@/components/Game'
import Intro from '@/components/Intro'
import SelectDeck from '@/components/SelectDeck'
import SelectDifficulty, { Difficulties } from '@/components/SelectDifficulty'
import { Deck } from '@/types'
import { DECKS } from '@/utils/Decks'

const UIStates = {
  IntroScreen: 0,
  DifficultyScreen: 1,
  DeckScreen: 2,
  GameScreen: 3,
} as const

const Home: NextPage = () => {
  const [UIState, setUIState] = useState<number>(UIStates.IntroScreen)
  const [deck, setDeck] = useState<Deck>(DECKS['Dragon Ball'])
  const [difficulty, setDifficulty] = useState(Difficulties.Normal)

  return (
    <div>
      <AnimatePresence>
        {UIState === UIStates.IntroScreen && (
          <Intro next={() => setUIState(UIStates.DifficultyScreen)} />
        )}
        {UIState === UIStates.DifficultyScreen && (
          <SelectDifficulty
            next={() => setUIState(UIStates.DeckScreen)}
            setDifficulty={setDifficulty}
          />
        )}
        {UIState === UIStates.DeckScreen && (
          <SelectDeck
            next={() => setUIState(UIStates.GameScreen)}
            setDeck={(deckName: string) => setDeck(DECKS[deckName])}
          />
        )}
        {UIState === UIStates.GameScreen && (
          <Game
            selectedDeck={deck.slice(0, difficulty)}
            backToDifficulty={() => setUIState(UIStates.DifficultyScreen)}
            backToDeck={() => setUIState(UIStates.DeckScreen)}
          />
        )}
      </AnimatePresence>
    </div>
  )
}

export default Home
Вход в полноэкранный режим Выйти из полноэкранного режима

И мне жаль говорить, что, несмотря на добавление AnimatePresence, это все равно не работает! А все потому, что framer не различает, какой компонент мы пытаемся анимировать при переключении экранов, поэтому для каждого экрана нужно указать свой ключ.

{UIState === UIStates.IntroScreen && (
    <Intro
        next={() => setUIState(UIStates.DifficultyScreen)}
        key={UIStates.IntroScreen}
     />
 )}
Вход в полноэкранный режим Выйдите из полноэкранного режима

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

<AnimatePresence exitBefoeEnter>
Вход в полноэкранный режим Выход из полноэкранного режима

Анимации для SVG

Классной утилитой является возможность анимировать SVG, и это так легко и просто, как использование pathLength для анимирования процесса рисования пути SVG.

Сначала давайте добавим этот SVG на вводную страницу:

// components/Intro/index.tsx

<svg
        className={styles.Container}
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 512 512"
      >
        <motion.path
          fill="none"
          stroke="var(--primary)"
          strokeWidth={6}
          strokeLinecap="round"
          variants={pathVariants}
          d="M256 224C238.4 224 223.1 238.4 223.1 256S238.4 288 256 288c17.63 0 32-14.38 32-32S273.6 224 256 224zM470.2 128c-10.88-19.5-40.51-50.75-116.3-41.88C332.4 34.88 299.6 0 256 0S179.6 34.88 158.1 86.12C82.34 77.38 52.71 108.5 41.83 128c-16.38 29.38-14.91 73.12 25.23 128c-40.13 54.88-41.61 98.63-25.23 128c29.13 52.38 101.6 43.63 116.3 41.88C179.6 477.1 212.4 512 256 512s76.39-34.88 97.9-86.13C368.5 427.6 441 436.4 470.2 384c16.38-29.38 14.91-73.13-25.23-128C485.1 201.1 486.5 157.4 470.2 128zM95.34 352c-4.001-7.25-.1251-24.75 15-48.25c6.876 6.5 14.13 12.87 21.88 19.12c1.625 13.75 4.001 27.13 6.751 40.13C114.3 363.9 99.09 358.6 95.34 352zM132.2 189.1C124.5 195.4 117.2 201.8 110.3 208.2C95.22 184.8 91.34 167.2 95.34 160c3.376-6.125 16.38-11.5 37.88-11.5c1.75 0 3.876 .375 5.751 .375C136.1 162.2 133.8 175.6 132.2 189.1zM256 64c9.502 0 22.25 13.5 33.88 37.25C278.6 105 267.4 109.3 256 114.1C244.6 109.3 233.4 105 222.1 101.2C233.7 77.5 246.5 64 256 64zM256 448c-9.502 0-22.25-13.5-33.88-37.25C233.4 407 244.6 402.7 256 397.9c11.38 4.875 22.63 9.135 33.88 12.89C278.3 434.5 265.5 448 256 448zM256 336c-44.13 0-80.02-35.88-80.02-80S211.9 176 256 176s80.02 35.88 80.02 80S300.1 336 256 336zM416.7 352c-3.626 6.625-19 11.88-43.63 11c2.751-12.1 5.126-26.38 6.751-40.13c7.752-6.25 15-12.63 21.88-19.12C416.8 327.2 420.7 344.8 416.7 352zM401.7 208.2c-6.876-6.5-14.13-12.87-21.88-19.12c-1.625-13.5-3.876-26.88-6.751-40.25c1.875 0 4.001-.375 5.751-.375c21.5 0 34.51 5.375 37.88 11.5C420.7 167.2 416.8 184.8 401.7 208.2z"
        />
</svg>
Войти в полноэкранный режим Выход из полноэкранного режима

И самое настоящее волшебство, pathVariants.

// components/Intro/Intro.variants.ts

const pathVariants = {
  hidden: {
    pathLength: 0,
  },
  visible: {
    pathLength: 1,
    transition: {
      duration: 4,
      yoyo: Infinity,
      ease: 'easeInOut',
    },
  },
}
Войти в полноэкранный режим Выход из полноэкранного режима

Я переусложнил это с кучей дополнительных свойств, о которых мы уже знаем на данный момент, но ключевым является переход от 0 pathLenght к 1, framer motion будет следовать описанию пути нашего SVG и рисовать этот путь с указанными нами значениями анимации.

Заключение

С помощью этого простого проекта мы увидели, насколько легко, надежно и в соответствии с нашими текущими навыками можно включать в наши проекты как простые, так и сложные анимации.

Это лишь вводное руководство по framer-motion, внутри библиотеки есть еще много интересного, особенно множество полезных хуков для создания еще более безумных анимаций без особых усилий и продвинутых тем, таких как 3D-анимация, например, путем объединения этой библиотеки с react-three/fiber.

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

Оцените статью
Procodings.ru
Добавить комментарий