使用 React Spring 创建动画

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 价值 $200 的免费积分!

您是否曾在 React 应用程序中需要动画?传统上,实现动画并非易事。但现在,多亏了 Paul Henschel,我们有了一个专门用于此的新 React 工具。 react-spring 继承自 animatedreact-motion,用于插值、优化性能和简洁的 API。

在本教程中,我们将了解 react-spring 中包含的 五个钩子 中的两个,分别是 useSpringuseTrail。我们将实现的示例利用了这两个 API。

如果您想跟着做,请安装 react-spring 来开始。

## yarn
yarn add react-spring

## npm
npm install react-spring --save

弹簧

Spring 属性可用于将数据从一种状态移动到另一种状态。我们提供了 fromto 属性来帮助我们定义动画的起始和结束状态。from 属性决定渲染期间数据的初始状态,而我们使用 to 来表示动画完成后它应该位于何处。

在第一个示例中,我们将使用创建弹簧动画的渲染属性版本。

查看 CodePen 上的
react spring 1
,作者是 Kingsley Silas Chijioke (@kinsomicrote)
位于 CodePen 上。

在初始渲染时,我们想要隐藏一个框,并在单击按钮时将其向下滑动到页面中心。当然,可以不用 react-spring 来实现这一点,但我们想要在单击按钮时,将框动画化到视图中。

class App extends React.Component {
  state = {
    content: false
  }

  displayContent = (e) => {
    e.preventDefault()
    this.setState({ content: !this.state.content })
  }

  render() {
    return (
      <div className="container">
          // The button that toggles the animation
          <div className="button-container">
            <button
              onClick={this.displayContent}
              className="button">
                Toggle Content
            </button>
          </div>
          {
            !this.state.content ?
              (
                // Content in the main container
                <div>
                  No Content
                </div>
              )
            : (
              // We call Spring and define the from and to props
              <Spring
                from={{
                  // Start invisible and offscreen
                  opacity: 0, marginTop: -1000,
                }}
                to={{
                  // End fully visible and in the middle of the screen
                  opacity: 1, marginTop: 0,
                }}
              >
                { props => (
                  // The actual box that slides down
                  <div  className="box" style={ props }>
                    <h1>
                      This content slid down. Thanks to React Spring
                    </h1>
                  </div>
              )}
            </Spring>
            )
        }
      </div>
    )
  }
}

大多数代码都是您可能已经习惯看到的 React 基本代码。我们在需要在 content 的值更改为 true 后有条件地渲染内容的部分使用 react-spring。在本示例中,我们希望内容从页面顶部滑动到页面中心,因此我们使用 marginTop 并将其设置为 -1000 的值来将其定位在屏幕外,然后将不透明度定义为 0,作为 from 属性的值。这意味着该框最初将从页面顶部出现,并且不可见。

在组件渲染后单击按钮将更新组件的状态,并导致内容从页面顶部向下滑动。

我们还可以使用钩子 API 来实现上述示例。为此,我们将使用 useSpringanimated 钩子,以及 React 的内置钩子。

const App = () => {
  const [contentStatus, displayContent] = React.useState(false);
  // Here's our useSpring Hook to define start and end states
  const contentProps = useSpring({
    opacity: contentStatus ? 1 : 0,
    marginTop: contentStatus ? 0 : -1000
  })
  return (
    <div className="container">
      <div className="button-container">
        <button
          onClick={() => displayContent(a => !a)}
          className="button">Toggle Content</button>
      </div>
        {
          !contentStatus ?
            (
              <div>
                No Content
              </div>
            )
          : (
            // Here's where the animated hook comes into play
            <animated.div className="box" style={ contentProps }>
              <h1>
                This content slid down. Thanks to React Spring
              </h1>
            </animated.div>
          )
        }
    </div>
  )
}

首先,我们为组件设置状态。然后,我们使用 useSpring 来设置所需的动画。当 contentStatustrue 时,我们希望 marginTopopacity 的值分别为 01。否则,它们应该是 -10000。这些值被分配给 contentProps,然后我们将其作为属性传递给 animated.div

contentStatus 的值由于单击按钮而发生更改时,opacitymarginTop 的值也会随之更改。这导致内容向下滑动。

查看 CodePen 上的
react spring 2
,作者是 Kingsley Silas Chijioke (@kinsomicrote)
位于 CodePen 上。

轨迹

Trail 属性为项目列表设置动画。动画将应用于第一个项目,然后其兄弟姐妹依次执行。为了了解它是如何运作的,我们将构建一个组件,该组件发出 GET 请求以获取用户列表,然后我们将为其渲染设置动画。就像使用 Spring 一样,我们将分别了解如何使用渲染属性和钩子 API 来实现这一点。

首先,是渲染属性。

class App extends React.Component {
  state = {
    isLoading: true,
    users: [],
    error: null
  };
  
  // We're using the Fetch <abbr>API</abbr> to grab user data
  // https://css-tricks.org.cn/using-data-in-react-with-the-fetch-api-and-axios/
  fetchUsers() {
    fetch(`https://jsonplaceholder.typicode.com/users`)
      .then(response => response.json())
      .then(data =>
        // More on setState: https://css-tricks.org.cn/understanding-react-setstate/
        this.setState({
          users: data,
          isLoading: false,
        })
      )
      .catch(error => this.setState({ error, isLoading: false }));
  }

  componentDidMount() {
    this.fetchUsers();
  }

  render() {
    const { isLoading, users, error } = this.state;
    return (
      <div>
        <h1>Random User</h1>
        {error ? <p>{error.message}</p> : null}
        {!isLoading ? (
          // Let's define the items, keys and states using Trail
          <Trail
            items={users}
            keys={user => user.id}
            from={{ marginLeft: -20, opacity: 0, transform: 'translate3d(0,-40px,0)' }}
            to={{ marginLeft: 20, opacity: 1, transform: 'translate3d(0,0px,0)' }}
          >
          {user => props => (
          <div style={props} className="box">
            {user.username}
          </div>
        )}
      </Trail>
        ) : (
          <h3>Loading...</h3>
        )}
      </div>
    );
  }
}

当组件挂载时,我们从第三方 API 服务发出 请求以获取 一些随机用户。然后,我们使用 API 返回的数据更新 this.state.users。我们可以不使用动画列出这些用户,这将看起来像这样

users.map(user => {
  const { username, name, email } = user;
  return (
    <div key={username}>
      <p>{username}</p>
    </div>
  );
})

但是,由于我们想要为列表设置动画,因此必须将项目作为属性传递给 Trail 组件

<Trail
  items={users}
  keys={user => user.id}
  from={{ marginLeft: -20, opacity: 0, transform: 'translate3d(0,-40px,0)' }}
  to={{ marginLeft: 20, opacity: 1, transform: 'translate3d(0,0px,0)' }}
>
  {user => props => (
    <div style={props} className="box">
      {user.username}
    </div>
  )}
</Trail>

我们将键设置为每个用户的 ID。您可以看到我们还使用 fromto 属性来确定动画应从哪里开始以及结束。

现在,我们的用户列表将带有微妙的动画滑动进来

查看 CodePen 上的
React Spring - Trail 1
,作者是 Kingsley Silas Chijioke (@kinsomicrote)
位于 CodePen 上。

钩子 API 使我们能够访问 useTrail 钩子。由于我们没有使用类组件,因此可以使用 useEffect 钩子(类似于 componentDidMountcomponentDidUpdate 生命周期方法)来在组件挂载时获取用户。

const App = () => {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    fetch(`https://jsonplaceholder.typicode.com/users`)
      .then(response => response.json())
      .then(data =>
        setUsers(data)
      )
  }, [])
  
  const trail = useTrail(users.length, {
    from: { marginLeft: -20, opacity: 0, transform: 'translate3d(0,-40px,0)' },
    to: { marginLeft: 20, opacity: 1, transform: 'translate3d(0,0px,0)' }
  })

  return (
    <React.Fragment>
      <h1>Random User</h1>
      {trail.map((props, index) => {
        return (
          <animated.div
            key={users[index]}
            style={props}
            className="box"
          >
            {users[index].username}
          </animated.div>
        )
      })}
    </React.Fragment>
  );
}

我们将 users 的初始状态设置为一个空数组。使用 useEffect,我们从 API 获取用户,并使用在 useState 钩子的帮助下创建的 setUsers 方法设置新状态。

使用 useTrail 钩子,我们创建了动画样式,并为其传递了 fromto 的值,并且我们还传递了要设置动画的项目的长度。在我们要渲染用户列表的部分,我们返回包含动画属性的数组。

查看 CodePen 上的
React Spring - Trail 2
,作者是 Kingsley Silas Chijioke (@kinsomicrote)
位于 CodePen 上。

开始使用弹簧!

现在,您有了一种新的、相对轻松的方式来在 React 中使用动画。尝试为应用程序的不同方面设置动画,以满足您的需求。当然,请记住用户对动画的偏好,因为它们可能对可访问性造成负面影响。

同时,请务必查看 react-spring 的官方网站,因为那里有大量演示可以激发您对动画的想法的创造力。