react-spring的官方文档--链接
安装
yarn add @react-spring/web
应用
组件式动画
const springs = useSpring({
from: { x: 0 },
to: { x: 100 },
})
return (
<animated.div
style={{
width: 80,
height: 80,
background: '#ff6d6d',
borderRadius: 8,
...springs,
}}
/>
)
通过<animated.div/>
组件声明该组件可以定义的from
和to
改变组件的x坐标,从而在默认时间中移动形成动画。
api控制spring
api.start({
from: {
x: 0,
},
to: {
x: 100,
},
})
概念介绍
react-spring
是在SpringValues
和animated
这两个组件封装而成的,一个animated
组件通过style
属性的参数更新组件状态,而这个过程并不引起组件的render
。它是基于Higher-Order component
(HOC)原理实现的,而这种原理是根据div
上所传递的参数类别形成的一个元素集合或者通过hooks
函数插入SpringValues
,从而达到你想要效果。
Animating elements
animated
组件可用于任何一个web
元素上,然而,由于使用的是原始element
,一个animated
组件被用于具体的目标上。
import { animated } from '@react-spring/web'
// ✅ This will work because `div` is a web element
<animated.div />
// ❌ This will not work because `mesh` is not a web element.
<animated.mesh />
如果你之前用过framer-motion
,那么你应该熟悉组件的.
语法结构。
所以当你能够熟练的使用animated.element
,大多数情况下你可以在element
写出你想要的效果,react-spring
在样式上并没有特殊的写法,常见的例如css modules
tailwind
写法,react-spring
都可以支持,因为animated
组件可以接受原始element
中的属性,例如:className
。
如果你打算用css
库去修饰组件,那么styled
函数支持你这样做的,就像嵌套组件的方式那样把animated
组件和styled
组合在一起。
import { styled } from '@stitches/react'
const MyModal = styled(animated.div, {
width: '40vw',
height: '20vh',
borderRadius: '8px',
backgroundColor: '$white80',
})
Controllers & Springs & API
如果你已经使用过useSpring
函数,那么你对下面的代码比较熟悉了
const [styles, api] = useSpring(() => ({
x: 0,
y: 0,
backgroundColor: '#ff0000',
scale: [1, 1, 1],
config: {
precision: 0.0001,
},
}))
useSpring
函数返回了包含两个元素styles``api
的数组对象,它是一个包含SpringValue
的对象,而SpringValue
是一个由动态的key
,当然这些key
是你自己定义的。
例如:
type SpringValues<SpringConfig extends Record<string, any> = {
[Key in keyof OnlyAnimatableKeys<SpringConfig>]: SpringValue
}
在上面的例子中,OnlyAnimatableKeys
只是以x,y,backgroundColor,scale
这些key
简单的配置参数,那么正因为我们知道这些key
是可变化的,因此这些key
就会成为这个函数中简单版的type
参数了。
Controller
那么什么是Controller呢?实际上可以把每个spring
当作一个Controller
。因此,当你使用useSpring
函数就创建了一个Controller
对象或者传递多个参数到useSprings
函数中,那么你就创建了多个Controller
对象。
这些Controller
对象就是管理那些通过配置参数创建的SpringValue
对象的,这些方法和SpringValue
类中类似,Controller
中主要的方法例如start``stop``pause
,就是通过管理数组中SpringValue
对象的。
// The set method from the Controller class
set(values) {
for (const key in values) {
const value = values[key]
if (!is.und(value)) {
this.springs[key].set(value)
}
}
}
// Would be used like this
controller.set({
x: 0,
scale: [0,0,0]
})
useSpring
函数配置后的对象和Controller
类构造第一个参数的方式是相同的,这样你就能知道,在react
环境中useSpring
函数操作了Controller
类的生命周期并且通过SpringRef
的方式把它添加到controller
的对象中,而SpringRef
提供了非常简单而快捷的方式管理一个或者多个Controller
的类对象,这样,两者比较之下,你可以忽略用hook
的方式而直接使用Controller
类的方式。
Spring value
SpringValues
可以满足正常的交互需求,它们的参数明确地传入animated
组件中,这些参数可以添加进去,而不需要在组件被使用的时候去命名。
const {
backgroundColor, // SpringValue<string>
o, // SpringValue<number>
trans, // SpringValue<number[]>
} = useSpring({
backgroundColor: '#00ff00',
o: 0,
trans: [0, 1, 2],
})
这是因为那些在Controller
和SpringValue
中使用你命名的key
,它仅仅是关心你传入的参数类型的值。在SpringValue
类中,我们可以控制运动过程中的整个生命周期,从事件的通过使用的不同类型方式的触发,SpringValue
是运动过程中的驱动力。
Imperative API
这些命令式的API
可以让你不需要在页面渲染的时候更新你的动画,对于动画来说有很大的好处,这样就不用把动画和组件的生命周期捆绑在一起,从而让动画根据用户的想法做出迅速的改变。
事实上,简单地在Controller
函数中把SpringRef
对象以参数的方式附属在上面,你可以把SpringRef
添加到多个Controller
中,从而可以生成出一组动画,这个思想和useChain
这个函数类似。
下面就看看SpingValue
和Controller
的具体区别
import { useState } from 'react'
import { useSpring, useSpringRef, animated } from '@react-spring/web'
const ApiComponent = () => {
const api = useSpringRef()
const springs = useSpring({
ref: api,
from: { x: 0 },
})
const handleClick = () => {
api.start({
to: {
x: springs.x.get() === 100 ? 0 : 100,
},
})
}
return (
<div className="flex-container">
<animated.div
onClick={handleClick}
style={{
width: 80,
height: 80,
background: '#ff6d6d',
borderRadius: 8,
...springs,
}}
/>
<span>Render ID – {Math.random()}</span>
</div>
)
}
const StateComponent = () => {
const [forward, setForward] = useState(false)
const springs = useSpring({
x: forward ? 100 : 0,
})
const handleClick = () => {
setForward(s => !s)
}
return (
<div className="flex-container">
<animated.div
onClick={handleClick}
style={{
width: 80,
height: 80,
background: '#ff6d6d',
borderRadius: 8,
...springs,
}}
/>
<span>Render ID – {Math.random()}</span>
</div>
)
}
export default function MyComponent() {
return (
<div className="flex-container--column">
<ApiComponent />
<StateComponent />
</div>
)
}
可以看到一种方式是Controller
的以API
改变动画,而第二种方式是SpringValue
中的参数值,在页面重新渲染的时候,根据值的不同去实现动画。