您是否曾在 React 应用程序中需要动画?传统上,实现动画并非易事。但现在,多亏了 Paul Henschel,我们有了一个专门用于此的新 React 工具。 react-spring 继承自 animated 和 react-motion,用于插值、优化性能和简洁的 API。
在本教程中,我们将了解 react-spring 中包含的 五个钩子 中的两个,分别是 useSpring 和 useTrail。我们将实现的示例利用了这两个 API。
如果您想跟着做,请安装 react-spring 来开始。
## yarn
yarn add react-spring
## npm
npm install react-spring --save
弹簧
Spring 属性可用于将数据从一种状态移动到另一种状态。我们提供了 from
和 to
属性来帮助我们定义动画的起始和结束状态。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 来实现上述示例。为此,我们将使用 useSpring
和 animated
钩子,以及 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
来设置所需的动画。当 contentStatus
为 true
时,我们希望 marginTop
和 opacity
的值分别为 0
和 1
。否则,它们应该是 -1000
和 0
。这些值被分配给 contentProps
,然后我们将其作为属性传递给 animated.div
。
当 contentStatus
的值由于单击按钮而发生更改时,opacity
和 marginTop
的值也会随之更改。这导致内容向下滑动。
查看 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。您可以看到我们还使用 from
和 to
属性来确定动画应从哪里开始以及结束。
现在,我们的用户列表将带有微妙的动画滑动进来
查看 CodePen 上的
React Spring - Trail 1,作者是 Kingsley Silas Chijioke (@kinsomicrote)
位于 CodePen 上。
钩子 API 使我们能够访问 useTrail
钩子。由于我们没有使用类组件,因此可以使用 useEffect
钩子(类似于 componentDidMount
和 componentDidUpdate
生命周期方法)来在组件挂载时获取用户。
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
钩子,我们创建了动画样式,并为其传递了 from
和 to
的值,并且我们还传递了要设置动画的项目的长度。在我们要渲染用户列表的部分,我们返回包含动画属性的数组。
查看 CodePen 上的
React Spring - Trail 2,作者是 Kingsley Silas Chijioke (@kinsomicrote)
位于 CodePen 上。
开始使用弹簧!
现在,您有了一种新的、相对轻松的方式来在 React 中使用动画。尝试为应用程序的不同方面设置动画,以满足您的需求。当然,请记住用户对动画的偏好,因为它们可能对可访问性造成负面影响。
同时,请务必查看 react-spring 的官方网站,因为那里有大量演示可以激发您对动画的想法的创造力。
很棒的教程!
太棒了