前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >react-native 动画笔记 && 监听

react-native 动画笔记 && 监听

原创
作者头像
conanma
修改2021-05-11 10:01:49
1.2K0
修改2021-05-11 10:01:49
举报
文章被收录于专栏:正则正则

LayoutAnimation 比较简单的api

当布局变化时,自动将视图运动到它们新的位置上。

一个常用的调用此API的办法是调用LayoutAnimation.configureNext(config),然后调用setState。

具体参数请查看项目文件夹下/node_modules/react-native/Libraries/LayoutAnimation/LayoutAnimation.js该路径下的js文件

一个标准的config格式如下:(create、update和delete,分别表示视图创建、更新和删除时候的动画)

{   

  duration: 700,   //持续时间   

  create: {   // 视图创建         

type: LayoutAnimation.Types.linear,     

    property: LayoutAnimation.Properties.scaleXY // opacity、scaleXY   

  },   

  update: { // 视图更新     

type: LayoutAnimation.Types.spring,     

    springDamping: 0.4

  },

}

y一个动画的参数-下图截图至上面提供的路径上,参数介绍如下

delay:延迟指定时间(单位:毫秒)

springDamping:弹跳动画阻尼系数(配合spring使用)

initialVelocity:初始速度

type:类型定义在LayoutAnimation.Types中:

spring:弹跳

linear:线性

easeInEaseOut:缓入缓出

easeIn:缓入

easeOut:缓出

property:类型定义在LayoutAnimation.Properties中:

opacity:透明度

scaleXY:缩放

create函数接受三个参数:

duration:动画持续时间。

type:create和update时的动画类型,定义在* LayoutAnimation.Types。

creationProp:create时的动画属性,定义在LayoutAnimation.Properties。

实际上,系统已经为我们提供了3个默认的动画,定义在LayoutAnimation.Presets中:

easeInEaseOut:缓入缓出

linear:线性

spring:弹性

调用方式:LayoutAnimation.Presets.linear

优点:

1、效果非常的流畅,而且动画的过程很柔和,丝毫没有生硬的感觉。

2、可以灵活的配置动画参数,基本能满足单个动画的需求。

缺点:

1、如果要实现‘组合顺序’动画,比如先缩小50%、再向左平移100像素,那么就比较麻烦了,需要监听上一个动画的结束事件,再进行下一个。那么问题来了,configureNext第二个参数是可以监听动画结束的,但是只在IOS平台有效!

2、如果动画的效果更为复杂,比如同时执行动画,再顺序执行,对于编程来讲,需要做的事情很多,复杂度也大大提升。

animated

spring:基础的单次弹跳物理模型

timing:从时间范围映射到渐变的值

decay:以一个初始速度开始并且逐渐减慢停止

创建动画的参数:

value:AnimatedValue | AnimatedValueXY(X轴或Y轴 | X轴和Y轴)

config:SpringAnimationConfig | TimingAnimationConfig | DecayAnimationConfig(动画的参数配置)

组件类型:

Animated.Text

Animated.Image

Animated.View:可以用来包裹任意视图

Animated.createAnimatedComponent():其它组件(较少用,用Animated.View包裹可以达到同样的效果)

//图片透明度2秒内从不透明到全透明,线性变化

class Demo8 extends Component {

// 构造

  constructor(props) {

super(props);

// 初始状态

this.state = {

          fadeOutOpacity: new Animated.Value(0),

      };

  }

  render() {

return ( 

        <Animated.View // 可选的基本组件类型: Image, Text, View(可以包裹任意子View)

            style = {{flex: 1,alignItems: 'center',justifyContent: 'center',

                    opacity: this.state.fadeOutOpacity,}}> 

            <Image source = {{uri: 'http://i.imgur.com/XMKOH81.jpg'}}

                style = {{width: 400,height: 400}}/>

        </Animated.View > 

      );

  }

  startAnimation() {

this.state.fadeOutOpacity.setValue(1);

Animated.timing(this.state.fadeOutOpacity, {

          toValue: 0,

          duration: 2000,

          easing: Easing.linear,// 线性的渐变函数

      }).start();

  }

  componentDidMount() {

this.startAnimation();

  }

}

AppRegistry.registerComponent('Demo8', () = >Demo8);

值类型:

AnimatedValue:单个值

AnimatedValueXY:向量值

多数情况下,AnimatedValue可以满足需求(上面的示例),但有些情况下我们可能会需要AnimatedValueXY。

//图片沿着X轴和Y轴交叉方向,向右下角移动一小段距离。

class Demo8 extends Component {

// 构造

  constructor(props) {

super(props);

// 初始状态

this.state = {

          translateValue: new Animated.ValueXY({x:0, y:0}), // 二维坐标

      };

  }

  render() {

return ( 

        <Animated.View // 可选的基本组件类型: Image, Text, View(可以包裹任意子View)

            style = {{flex: 1,alignItems: 'center',justifyContent: 'center',

                  transform: [ 

                    {translateX: this.state.translateValue.x}, // x轴移动

                    {translateY: this.state.translateValue.y}, // y轴移动

                  ]

                  }}> 

            <Image source = {{uri: 'http://i.imgur.com/XMKOH81.jpg'}}

                style = {{width: 400,height: 400}}/>

        </Animated.View > 

      );

  }

  startAnimation() {

this.state.translateValue.setValue({x:0, y:0});

Animated.decay( // 以一个初始速度开始并且逐渐减慢停止。  S=vt-(at^2)/2   v=v - at

this.state.translateValue,

          {

              velocity: 10, // 起始速度,必填参数。

              deceleration: 0.8, // 速度衰减比例,默认为0.997。

          }

      ).start();

  }

  componentDidMount() {

this.startAnimation();

  }

}

AppRegistry.registerComponent('Demo8', () = >Demo8);

其中,transform是一个变换数组,常用的有scale, scaleX, scaleY, translateX, translateY, rotate, rotateX, rotateY, rotateZ:

一.旋转 rotate

用法:transform: rotate(45deg);

共一个参数“角度”,单位deg为度的意思,正数为顺时针旋转,负数为逆时针旋转,上述代码作用是顺时针旋转45度。

二.缩放 scale

用法:transform: scale(0.5)  或者  transform: scale(0.5, 2);

参数表示缩放倍数;

一个参数时:表示水平和垂直同时缩放该倍率

两个参数时:第一个参数指定水平方向的缩放倍率,第二个参数指定垂直方向的缩放倍率。

三.倾斜 skew

用法:transform: skew(30deg)  或者 transform: skew(30deg, 30deg);

参数表示倾斜角度,单位deg

一个参数时:表示水平方向的倾斜角度;

两个参数时:第一个参数表示水平方向的倾斜角度,第二个参数表示垂直方向的倾斜角度。

关于skew倾斜角度的计算方式表面上看并不是那么直观,这里借鉴某大拿绘制的图举例说明一下:

首先需要说明的是skew的默认原点transform-origin是这个物件的中心点

四.移动 translate

用法:transform: translate(45px)  或者 transform: translate(45px, 150px);

参数表示移动距离,单位px,

一个参数时:表示水平方向的移动距离;

两个参数时:第一个参数表示水平方向的移动距离,第二个参数表示垂直方向的移动距离。

五.基准点 transform-origin

在使用transform方法进行文字或图像的变形时,是以元素的中心点为基准点进行的。使用transform-origin属性,可以改变变形的基准点。

用法:transform-origin: 10px 10px;

共两个参数,表示相对左上角原点的距离,单位px,第一个参数表示相对左上角原点水平方向的距离,第二个参数表示相对左上角原点垂直方向的距离;

两个参数除了可以设置为具体的像素值,其中第一个参数可以指定为left、center、right,第二个参数可以指定为top、center、bottom。

六.多方法组合变形

上面我们介绍了使用transform对元素进行旋转、缩放、倾斜、移动的方法,这里讲介绍综合使用这几个方法来对一个元素进行多重变形。

用法:transform: rotate(45deg) scale(0.5) skew(30deg, 30deg) translate(100px, 100px);

这四种变形方法顺序可以随意,但不同的顺序导致变形结果不同,原因是变形的顺序是从左到右依次进行,这个用法中的执行顺序为1.rotate  2.scalse  3.skew  4.translate

...

transform: [  // scale, scaleX, scaleY, translateX, translateY, rotate, rotateX, rotateY, rotateZ

    {scale: this.state.bounceValue},  // 缩放

    {rotate: this.state.rotateValue.interpolate({ // 旋转,使用插值函数做值映射

        inputRange: [0, 1],

        outputRange: ['0deg', '360deg']})},

    {translateX: this.state.translateValue.x}, // x轴移动

    {translateY: this.state.translateValue.y}, // y轴移动

],

...

插值函数:

将输入值范围转换为输出值范围,如下:将0-1数值转换为0deg-360deg角度,旋转View时你会用到

this.state.rotateValue.interpolate({ // 旋转,使用插值函数做值映射

        inputRange: [0, 1],

        outputRange: ['0deg', '360deg']})

组合动画:

parallel:同时执行

sequence:顺序执行

stagger:错峰,其实就是插入了delay的parrllel

delay:组合动画之间的延迟方法,严格来讲,不算是组合动画

//图片首先缩小80%,2秒之后,旋转360度,之后沿着X轴与Y轴交叉方向向右下角移动一段距离,最后消失变成全透明

startAnimation() {

this.state.bounceValue.setValue(1.5); // 设置一个较大的初始值

this.state.rotateValue.setValue(0);

this.state.translateValue.setValue({x: 0,y: 0});

this.state.fadeOutOpacity.setValue(1);

  Animated.sequence([

      Animated.sequence([ //  组合动画 parallel(同时执行)、sequence(顺序执行)、stagger(错峰,其实就是插入了delay的parrllel)和delay(延迟)

        Animated.spring( //  基础的单次弹跳物理模型

this.state.bounceValue, {

            toValue: 0.8,

            friction: 1,// 摩擦力,默认为7.

            tension: 40,// 张力,默认40。

          }), 

        Animated.delay(2000), // 配合sequence,延迟2秒

        Animated.timing( // 从时间范围映射到渐变的值。

this.state.rotateValue, {

            toValue: 1,

            duration: 800,// 动画持续的时间(单位是毫秒),默认为500

            easing: Easing.out(Easing.quad),// 一个用于定义曲线的渐变函数

            delay: 0,// 在一段时间之后开始动画(单位是毫秒),默认为0。

          }), 

        Animated.decay( // 以一个初始速度开始并且逐渐减慢停止。  S=vt-(at^2)/2   v=v - at

this.state.translateValue, {

            velocity: 10,// 起始速度,必填参数。

            deceleration: 0.8,// 速度衰减比例,默认为0.997。

        }),

      ]), 

      Animated.timing(this.state.fadeOutOpacity, {

        toValue: 0,

        duration: 2000,

        easing: Easing.linear,// 线性的渐变函数

      })

  ]).start();

}

循环执行动画:

start方法可以接受一个函数,通过监听动画结束,再调用startAnimation可以重复执行动画,

startAnimation() {

this.state.translateValue.setValue({x:0, y:0});

   Animated.decay( // 以一个初始速度开始并且逐渐减慢停止。  S=vt-(at^2)/2   v=v - at

this.state.translateValue,

      {

         velocity: 10, // 起始速度,必填参数。

         deceleration: 0.8, // 速度衰减比例,默认为0.997。

      }

   ).start(() => this.startAnimation());

}

监听当前的动画值:

addListener(callback):动画执行过程中的值

stopAnimation(callback):动画执行结束时的值

监听AnimatedValueXY类型translateValue的值变化:

this.state.translateValue.addListener((value) => {   

console.log("translateValue=>x:" + value.x + " y:" + value.y);

});

this.state.translateValue.stopAnimation((value) => {   

console.log("translateValue=>x:" + value.x + " y:" + value.y);

});

监听AnimatedValue类型rotateValue的值变化:

this.state.rotateValue.addListener((state) => {   

console.log("rotateValue=>" + state.value);

});

this.state.rotateValue.stopAnimation((state) => {   

console.log("rotateValue=>" + state.value);

});

//🌰

import React, { Component } from 'react';

import {

  AppRegistry,

  StyleSheet,

  TouchableOpacity,

  Animated,

  Image,

  Text,

  Easing,

  View

} from 'react-native';

export default class Hello extends Component {

  constructor(props) {

      super(props);

      // 初始状态

      this.state = {

          translateValue: new Animated.ValueXY({x:0, y:0}), // 二维坐标

          bounceValue: new Animated.Value(0),

          rotateValue: new Animated.Value(0),

          fadeOutOpacity: new Animated.Value(0),

      };

  }

  render() {

    return (

      <Animated.View                         // 可选的基本组件类型: Image, Text, View(可以包裹任意子View)

        style={{

              flex: 1,

              alignItems: 'center',

              justifyContent: 'center',

              transform: [  // scale, scaleX, scaleY, translateX, translateY, rotate, rotateX, rotateY, rotateZ

                {scale: this.state.bounceValue},  // 缩放

                {rotate: this.state.rotateValue.interpolate({ // 旋转,使用插值函数做值映射

                        inputRange: [0, 1],

                        outputRange: ['0deg', '360deg'],

                    })

                },

                {translateX: this.state.translateValue.x}, // x轴移动

                {translateY: this.state.translateValue.y}, // y轴移动

              ],

              opacity: this.state.fadeOutOpacity, // 透明度

            }}>

          <Image

            source={require('./asset/icon.png')}

          style={{width: 400, height: 400}}

          />

      </Animated.View>

  );

  }

  startAnimation() {

      this.state.bounceValue.setValue(1.5); // 设置一个较大的初始值

      this.state.rotateValue.setValue(0);

      this.state.translateValue.setValue({x: 0,y: 0});

      this.state.fadeOutOpacity.setValue(1);

      Animated.sequence([

          Animated.sequence([ //  组合动画 parallel(同时执行)、sequence(顺序执行)、stagger(错峰,其实就是插入了delay的parrllel)和delay(延迟)

            Animated.spring( //  基础的单次弹跳物理模型

              this.state.bounceValue, {

                toValue: 0.8,

                friction: 1,// 摩擦力,默认为7.

                tension: 40,// 张力,默认40。

              }), 

            Animated.delay(2000), // 配合sequence,延迟2秒

            Animated.timing( // 从时间范围映射到渐变的值。

              this.state.rotateValue, {

                toValue: 1,

                duration: 800,// 动画持续的时间(单位是毫秒),默认为500

                easing: Easing.out(Easing.quad),// 一个用于定义曲线的渐变函数

                delay: 0,// 在一段时间之后开始动画(单位是毫秒),默认为0。

              }), 

            Animated.decay( // 以一个初始速度开始并且逐渐减慢停止。  S=vt-(at^2)/2   v=v - at

              this.state.translateValue, {

                velocity: 10,// 起始速度,必填参数。

                deceleration: 0.8,// 速度衰减比例,默认为0.997。

            }),

          ]), 

          Animated.timing(this.state.fadeOutOpacity, {

            toValue: 0,

            duration: 2000,

            easing: Easing.linear,// 线性的渐变函数

          })

  ]).start();

    // 监听值的变化

    this.state.rotateValue.addListener((state) => {

      console.log("rotateValue=>" + state.value);

    });

    // ValueXY

    this.state.translateValue.addListener((value) => {

      console.log("translateValue=>x:" + value.x + " y:" + value.y);

    });

    this.state.fadeOutOpacity.addListener((state) => {

      console.log("fadeOutOpacity=>" + state.value);

    });

}

  componentDidMount() {

      this.startAnimation();

  }

}

const styles = StyleSheet.create({

  container: {

    flex: 1,

    justifyContent: 'center',

    alignItems: 'center',

    backgroundColor: '#F5FCFF',

  },

  editMore: {

    paddingLeft: 20,

    height: 44,

    justifyContent: 'center',

    alignItems: 'center',

    backgroundColor: 'white',

  },

  welcome: {

    fontSize: 20,

    textAlign: 'center',

    margin: 10,

  },

  instructions: {

    textAlign: 'center',

    color: '#333333',

    marginBottom: 5,

  },

});

AppRegistry.registerComponent('Hello', () => Hello);

以下为监听

rn自带

DeviceEventEmitter,

NativeAppEventEmitter,

RCTDeviceEventEmitter.js

RCTNativeAppEventEmitter.js

下面两个文件路径node_modules/react-native/Libraries/EventEmitter

理论上下面两个均支持添加和发送监听

项目里目前是上面的接收监听,下面的发送监听

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档