[React Native]动画-Animated

在上一篇文章中,我们学习了React Native实现动画的几种方式,其中重点介绍了LayoutAnimation。文章的末尾也提到,如果你需要更强大的动画功能,就需要使用高级API—Animated

如果你还不了解LayoutAnimation,建议先阅读下上一篇文章[React Native]动画-LayoutAnimation,其中的一些概念能让你更好的理解本篇文章的内容。

本篇文章会一步步介绍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);

效果图如下:

opacity.gif

值类型:

  • 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

...
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();
}

效果图如下:

compose.gif

循环执行动画:
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);
});

好了,到这里我们把Animated的常用方法都介绍了,也给出了代码示例(略微有点多)。建议大家动手尝试下每个效果,这样,可以理解的更加深刻

本文的源码地址Demo8

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容