今天继续更新 RN 相关的博客. 上篇博客详细的聊了 RN 中关于 Flex 布局的相关东西, 具体请参见《ReactNative 之参照具体示例来看 RN 中的 FlexBox 布局》. 本篇博客继续更新 RN 的动画部分, 博客中的内容依然是依托于具体的示例来进行的.
下方是官网对 RN 动画的的一个综述, 意思就是说在 RN 的组件中 View,Text,Image 和 ScrollView 是支持动画的, 不过你可以使用 Animated.createAnimatedComponent() 这个方法来创建一个支持动画的组件稍后会介绍到. 这些支持动画的组件在使用动画是都差不多, 本篇博客中的示例主要以 View 为主, 也会有 Text,Image 的部分动画.
Animated exports four animatable component types: View, Text, Image, and ScrollView, but you can also create your own using Animated.createAnimatedComponent().
一, 一个简单的 Moving 动画
第一部分我们先从一个简单的 Moving 动画来入手. 这个 Moving 动画是非常简单的, 就是一个 View, 然后点击这个 View, 会从一个地方移动到另一个地方. 然后再次点击会回归到原位. 下方是效果实现的分析:
首先我们会为 View 添加一个点击事件, 点击 View 时, 从一个位置移到另一个位置.
再次点击时, 会回到上次的一个位置.
在移动时我们加了一个 Bounce 的一个动画效果.
下方代码段是上述动画效果的部分代码. 代码比较简单:
首先在 State 中定义了一个类型为 Animated.Value 的动画值, 该值就负责来记录动画路径的值. 该值在组件的构造器中进行了初始化, 其初始值为零.
然后就是 pressView 方法了, 该方法就是上述红色 View 点击时所执行的方法. 为了点击反复移动, 我们使用了 toValue 来记录下次要运动的重点值. 这个 toValue 值 0 和 1 稍后会详细介绍.
然后就是关键了, 调用了 Animated 的 timing 方法, 该方法是用来配置一些动画效果的, 比如设置动画执行时间的 duration(单位为 ms), 设置目标值的 toValue 属性, 以及指定缓动效果的熟悉 easing. 关于这个 easing 下方会有详细的 Demo 介绍到.
设置完动画所执行的参数后, 就调用了 start() 方法来执行这个动画了.
上面这段代码是动画设置的相关代码, 下方这块代码是动画使用的相关代码片段. 下方是对这段代码的解析:
首先是从 state 中取出了动画值, 我们将该值付给了 moveValue.
然后我们是根据这个 moveValue 通过差值函数创建了一个 toValue 的值, 这个值就是我们动画的目标值, 也是我们真正要使用的值.
这个 interpolate() 差值函数负责用来指定 toValue 的生成规则, 该函数可以把这个
完整代码片段:
- type States = {
- moveValue: Animated.Value // 存储动画的值
- }
- class MoveViewTestComponent extends Component<null, States> {
- toValue = 0
- constructor (props) {
- super(props)
- this.state = {
- moveValue: new Animated.Value(0)
- }
- }
- pressView = () => {
- this.toValue = this.toValue === 0 ? 1 : 0
- Animated.timing(
- this.state.moveValue, // 初始化从 0 开始
- {
- toValue: this.toValue, // 目标值
- duration: 1000, // 时间间隔
- easing: Easing.bounce // 缓动函数
- }
- ).start()
- }
- render () {
- let { moveValue } = this.state
- let toValue = moveValue.interpolate({
- inputRange: [0,1],
- outputRange: ['10%', '60%']
- })
- return (
- <TouchableOpacity onPress={this.pressView}>
- <Animated.View // 使用专门的可动画化的 View 组件
- style={{
- width: 100,
- height: 50,
- backgroundColor: 'red',
- left: toValue,
- }}
- >
- <Text style={{ color: '#fff' }}> Tap Me Move </Text>
- </Animated.View>
- </TouchableOpacity>
- )
- }
- }
- View Code
上面设设置的 left 属性, 我们还可以对上述代码进行修改, 使用动画来改变每个角的弧度, 具体动画效果如下所示:
代码就比较简单了, 就是把动画的值直接赋值给我们的 borderRadius 属性即可.
二, 使用 Easing 函数指定相关的动画效果
在上面的示例中我们指定的移动动画的 Bounce 效果, 下方我们将通过一个示例来看一下 Easing 中所有的效果, 具体动画效果如下所示. 从下方的示例中我们不难看出, 每种效果的动画运动轨迹都不同, 我们在平时开发中可以根据自己的需要来选择相关的效果. 当然我们还可以通过矩阵来定义动画的变换路径, 在此就不做过多赘述了.
接下来我们来看一下上述动画实现的一个 Demo 的设计与实现.
首先我们定义了一个 MoveView 组件, 也就是对应着上述 Demo 中的每个 Button, 上面可点击可移动的每个 View 就是一个 MoveView. 该 View 会从外部接收一个 easing 参数, 该参数会被设置为该 View 的动画效果. 具体代码如下所示:
然后我们创建了一个 TestMoveView 的一个组件, 这个组件就是上述演示的内容. 在 TestMoveView 中我们定义了两个数组, 第一个数组用来存放每个按钮的 Title, 第二个数组则用来存放相关按钮的动画类型. 稍后会用到下方的这两个数组.
下方就是两个比较核心的方法了, 下方是对这两个方法的介绍
第一个 item 方法用来创建一个 MoveView, 该 View 对应的就是上述一个按钮. 第一个参数 Title 就是按钮上的 title, 二后边的 easing 则是动画效果.
在 Item 方法中我们给 MoveView 设置了一个 ref 的属性, 该属性的 Value 值我们用的是按钮上的 Title. 设置完这个 ref 值后, 我们可以使用 this.refs 来获取相关 key 值的对象. 稍后我们会用到.
然后就是 createItem 方法了, 该方法负责调用 上面我们事先创建好的数组, 从数组中取出相关的值, 然后调用 item 方法创建一系列的 MoveView 放到相关的数组里并返回.
在 Render 方法中我们就可以调用下方的这个 createItem 方法来创建相关的按钮了. 上的图片中能动的按钮都是通过这个 CreateItem 方法来创建的.
最后部分我们就来看一下 点击 Tap Me 按钮所执行的相关方法了, 下方的 Click 就是该方法. 在 Click 方法中主要做的就是调用 this.refs 方法获取所有可移动的 MoveView 的对象, 然后调用该对象的 pressView 方法执行相关的动画. 所有点击 Tap Me 按钮, 下方所有的按钮都会运动.
完整代码:
- import React,{ Component } from 'react'
- // import MoveView from './MoveView'
- import { Animated, Easing, Text, TouchableOpacity, View } from 'react-native'
- type EasingType = (value: number) => number
- export default class TestMoveView extends Component {
- animatedKey: string[] = [
- 'linear',
- 'quad',
- 'cubic',
- 'sin',
- 'exp',
- 'bounce',
- 'poly-5',
- 'elastic-2',
- 'back-2',
- 'bezier'
- ]
- animatedEasingType: EasingType[] = [
- Easing.linear,
- Easing.quad,
- Easing.cubic,
- Easing.sin,
- Easing.exp,
- Easing.bounce,
- Easing.poly(5),
- Easing.elastic(2),
- Easing.back(2),
- Easing.bezier(0,1.6, 1,-0.67)
- ]
- click = () => {
- for (let i = 0; i <this.animatedKey.length; i++) {
- this.refs[this.animatedKey[i]].pressView()
- }
- }
- item = (title: string, easing: EasingType) => {
- return (
- <MoveView
- easing= {easing}
- ref = {title}>
- <Text style={{ fontSize: 17, textAlign: 'center' }}>
- {title}
- </Text>
- </MoveView>
- )
- }
- createItems = () => {
- let items = []
- for (let i = 0; i <this.animatedKey.length; i++) {
- items.push(this.item(this.animatedKey[i], this.animatedEasingType[i]))
- }
- return items
- }
- render () {
- console.log('lizelu')
- return (
- <View style={{ flex: 1, justifyContent: 'center' }}>
- <TouchableOpacity onPress={this.click}>
- <Text>Tap Me</Text>
- </TouchableOpacity>
- { this.createItems() }
- </View>
- )
- }
- }
- // MoveView
- type MoveViewProps = {
- easing?: (value: number) => number
- }
- class MoveView extends Component<MoveViewProps> {
- toValue = 0
- state = {
- moveValue: new Animated.Value(0)
- }
- pressView = () => {
- this.toValue = this.toValue === 0 ? 1 : 0
- Animated.timing(
- this.state.moveValue, // 初始化从 0 开始
- {
- toValue: this.toValue, // 目标值
- duration: 1000, // 时间间隔
- easing: this.props.easing // 动画效果
- }
- ).start()
- }
- render () {
- let { moveValue } = this.state
- let toValue = moveValue.interpolate({
- inputRange: [0,1],
- outputRange: ['10%', '70%']
- })
- return (
- <TouchableOpacity onPress={this.pressView}>
- <Animated.View // 使用专门的可动画化的 View 组件
- style={[{
- width: 80,
- height: 30,
- backgroundColor: 'powderblue',
- margin: 2,
- left: toValue // 动画的目标值
- }]}
- >
- {this.props.children}
- </Animated.View>
- </TouchableOpacity>
- )
- }
- }
- Easing
三, 动画的插值函数及 transform
1, 插值函数
接下来我们通过一个 Loading 中经常使用的旋转动画, 来看一下 RN 动画中的插值函数. 下方的 Loading 动画本质上就是一张图片在不停的转圈, 不过在转圈的时候我们设置了一个 Circle 的动画效果.
需要实现的效果就是上面这个效果, 然后我们看一下代码实现以及插值函数的使用. 首先我们来看一下上述动画启动时的相关代码:
首先在 ComponentDidMount 方法中调用了启动方法的函数 startAnimation
在 startAnimation 函数中, 我们通过 Animation 的 loop 方法来执行循环动画动画的值从 0 到 1
并且我们设置了动画效果为 circle
最后就是调用 start 方法启动动画了.
然后就是 Render 方法中获取动画值, 给相关的组件设置动画了, 具体代码如下所示:
首先我们从 state 中获取到相关的动画值 animationValue
然后调用该动画值的插值函数 interpolate, 将动画值中的 0~1 的范围映射成角度 0deg ~ 360deg.
最后就是将这个插值函数生成的值 rotateZValue 设置给 Image 的 transform 即可.
2,Transform
经过上述步骤我们的图片就转起来了. 插值函数在动画中还是比较常用的, 上面是把 0 ~ 1 映射成角度, 我们还可以将该值映射成透明度, 颜色等等, 总之插值函数是 RN 动画中比较重要的角色. 而且我们可以给一个 RN 元素设置多个插值动画, 这样这个元素就会有多个动画效果.
下方这个动画就由多个插值函数结合着多种变换方式组合而成的, 分别是移动, 旋转, 放大这三种变换同时作用到一个 View 上所展示的效果, 具体效果如下所示:
上述效果是在第一个转圈的动画中丰富了一下而形成的, 具体代码如下:
前两个负责生成移动和缩放效果使用的值的插值函数和上面那个转圈的比较一致, 只不过映射的值不同.
然后看第三个旋转使用的插值函数就稍微有点不同了, 该插值函数可以将 0 ~ 1 不同的区间的值映射成不同范围的值, 从这个旋转的插值函数的映射关系不难看出, 上述 View 的旋转路径是先快后慢的, 这一点在插值函数中也是比较常用的.
最后就是将这三个插值函数所生成的结果设置在 View 的 transform 的各个 key 中就可以了.
上面是缩放, 移动, 旋转的变换. 下面我们来看一下斜切的变换. 下方第一个是 X 方向上的斜切, 第二个是 Y 轴方向上的斜切.
代码也比较简单, 下方是设置斜切的相关代码:
天不早了, 今天博客就先到这儿, 下篇博客继续 RN 动画的相关内容. 下篇博客我们会通过一系列的 "拉皮条" 操作来看一下 RN 中的 Spring 动画. 下篇的 "拉皮条" 的示例还是比较有意思的. 稍后会更新.
来源: https://www.cnblogs.com/ludashi/p/9876610.html