react-native系列(13)动画篇:Animated动画库和LayoutAnimation布局动画详解
动画概念了解
流畅、有意义的动画对于APP户体验来说是非常重要的,用RN开发实现动画有三种方法:
- requestAnimationFrame:称为帧动画,原理是通过同步浏览器的刷新频率不断重新渲染界面实现动画效果,现在网页H5动画基本都由这种实现。帧动画最初是Flash用于实现网页动画和游戏,即AS3编程。由于H5实现对动画更优的支持及Adobe对Flash的停止维护,这种编程现在基本已经被取代。另外,由于性能消耗较大,故一般不会用于APP的动画实现。
- LayoutAnimation:称为布局动画,这种方法使用起来非常便捷,它会在如透明度渐变、缩放这类变化时触发动画效果,动画会在下一次渲染或布局周期运行。布局动画还有个优点就是无需使用动画化组件,如Animated.View。
- Animated:用于实现精细动画效果。需要配合动画化组件使用,目前官方提供的动画化组件有4种:Animated.Image,Animated.ScrollView,Animated.Text 和 Animated.View。它们非常强大,基本可以满足大部分动画需求,在实际应用场景中,可以应用于透明度渐变、位移、缩放、颜色的变化等。
解决虚拟机上运行动画卡顿的问题
在进入主题前,我们先解决一下虚拟机运行动画有卡顿感的问题。为了让虚拟机可以流畅运行动画,建议使用64位镜像文件和电脑的显卡来渲染动画。
以Android开发为例:打开Android Studio的SDK配置项,勾选64位的虚拟机镜像并下载(可根据自己需求下载相应的API镜像):
创建虚拟机时选择该镜像,同时配置渲染方式为Hardware - GLES 2.0
通过这种方式创建出来虚拟机运行动画时将不会有卡顿感。如果还有卡顿感可以降低API的版本。
若使用的是较高版本的API,在debug的时有可能会报出了如下错误:
java.net.UnknownServiceException: CLEARTEXT communication ** not permitted by network security polic...
这是因为Google为保证用户数据和设备的安全,针对下一代 Android 系统(Android P) 的应用程序,将要求默认使用加密连接,这意味着 Android P 将禁止 App 使用所有未加密的连接,因此运行 Android P 系统的安卓设备无论是接收或者发送流量,未来都不能明码传输,需要使用下一代(Transport Layer Security)传输层安全协议,而 Android Nougat 和 Oreo 则不受影响。有以下三种解决方案:
1. APP改用https请求。
2. targetSdkVersion 降到27以下。
3. 在 res 下新增一个 xml 目录,然后创建一个名为:network_security_config.xml 文件(名字自定) ,内容如下,大概意思就是允许开启http请求。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config><base-config cleartextTrafficPermitted="true" />
</network-security-config>
然后在APP的AndroidManifest.xml文件下的application标签增加以下属性
<application
...android:networkSecurityConfig="@xml/network_security_config"
...
/>
requestAnimationFrame帧动画
这种方法主要是通过循环执行requestAnimationFrame函数并在函数内实现随着帧频变化刷新样式,由于性能消耗会非常巨大,故不建议使用requestAnimationFrame来实现APP的动画效果。帧动画实现的核心代码如下:
// 循环执行requestAnimationFrame
requestAnimationFrame(()=>{width++; // 每次循环宽度+1// setNativeProps 是指仅重新渲染样式变化的子组件,并不像setState那样把全部组件都重新渲染。this.view.current.setNativeProps({style: {width}});
});// 渲染
render(){return(<View ref={(view) => { this.view = view; }} />);
}
LayoutAnimation 布局动画
布局动画是指在组件布局发生变化时触发的动画。目前官方API仅支持透明度渐变、缩放两种动画。
在Android上使用 LayoutAnimation 布局动画,需要在UIManager
中启用,加上这段代码即可:
constructor(props){super(props);// 当为Android系统时,启用UIManager,否则布局动画将无效if (Platform.OS == 'android') {UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);}
}
LayoutAnimation属性:
属性 | 描述 |
---|---|
Types |
为枚举格式,表示展现的动画方式。 1. spring:类似于弹簧动画效果 2. linear:线性动画效果 3. easeInEaseOut:缓出缓入动画效果 |
Properties |
为枚举格式,表示哪些样式的变化使用动画过渡 目前仅支持两种 1. opacity:透明度变化时使用该属性 2. scaleXY:缩放,容器宽高的变化时使用该属性 |
Presets |
包括了系统默认的三种动画实现配置{spring:{...}, linear:{...},easeInEaseOut:{...}},若需其它额外动画效果,可通过LayoutAnimation.configureNext方法自定义。 |
LayoutAnimation方法:
方法 | 描述 |
---|---|
spring() |
使用默认的动画配置(即LayoutAnimation.Presets.spring对象) 实现弹性动画效果。 |
linear() |
使用默认的动画配置(即LayoutAnimation.Presets.linear对象) 实现线性动画效果。 |
easeInEaseOut() |
使用默认的动画配置(即LayoutAnimation.Presets.easeInEaseOut对象) 实现缓出缓入动画效果 |
configureNext(config,onAnimationDidEnd,onError) |
自定义动画配置。参数为: 1. config 自定义的动画配置对象 3. onError 动画运行错误时触发的函数,仅IOS支持 |
根据LayoutAnimation提供的方法,可以知道实现布局动画的方式有两种:
- 默认布局动画
- 自定义布局动画
1、默认布局动画:
这种方式实现起来足够简单,只需要在生命周期函数UNSAFE_componentWillUpdate中触发动画函数即可,如:
UNSAFE_componentWillUpdate () {LayoutAnimation.spring(); // 布局发生变化时触发弹簧动画效果
}
贴上完整代码:
import React, { Component } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, LayoutAnimation,Platform, UIManager } from 'react-native';class LayoutAnimationComp extends Component {constructor(props){super(props);this.state = {width: 100,height: 100};if (Platform.OS == 'android') {UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);}}UNSAFE_componentWillUpdate () {LayoutAnimation.spring();}_onPress = () => {this.setState({width: this.state.width + 50, height: this.state.height + 50});}render(){return(<View style={styles.container}><View style={[styles.viewStyle, {width: this.state.width, height: this.state.height}]}><Text>Hello RN!</Text></View><TouchableOpacity style={styles.btnContainerStyle} onPress={this._onPress}><Text style={{color:'#FFFFFF'}}>触发动画</Text></TouchableOpacity></View>);}
}const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center'},viewStyle: {justifyContent: 'center',alignItems: 'center',borderWidth: 1},btnContainerStyle: {width: 100,height: 30,marginTop: 20,borderRadius: 10,justifyContent: 'center',alignItems: 'center',backgroundColor:'red'}
});export default LayoutAnimationComp;
效果:
2、自定义布局动画
自定义配置函数configureNext(config,onAnimationDidEnd,onError)的参数格式:
LayoutAnimation.configureNext({duration: 1000, // 动画持续时间默认值// 组件创建时的动画// create: {},// 组件更新时的动画update: { duration: 3000, // 动画持续时间,没有设置时使用配置的默认值(即1000毫秒)delay: 0, // 动画延时执行时间type: LayoutAnimation.Types.spring, // 动画类型: spring弹性|linear线性|easeInEaseOut缓出缓入|easeIn缓入|easeOut缓出springDamping: 0.4, // 弹跳动画阻尼系数,配合动画类型为spring使用,其他类型不需要property: LayoutAnimation.Properties.scaleXY, // 动画特性: opacity透明度|scaleXY缩放},// 组件删除时的动画// delete:{}},()=>{console.log('onAnimationDidEnd'); // 当动画结束的时候被调用。只在iOS设备上支持。},()=>{console.log('onError'); // 当动画产生错误的时候被调用。只在iOS设备上支持。
});
布局动画可以在组件创建、更新和销毁时都可以自定义动画效果,贴上完整代码:
import React, { Component } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, LayoutAnimation,Platform, UIManager } from 'react-native';class LayoutAnimationComp extends Component {constructor(props){super(props);this.state = {width: 100,height: 100};if (Platform.OS == 'android') {UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);}}UNSAFE_componentWillUpdate () {LayoutAnimation.configureNext({duration: 1000, // 动画持续时间默认值update: { duration: 3000, // 动画持续时间,没有设置时使用配置的默认值(即1000毫秒)delay: 0, // 动画延时执行时间type: LayoutAnimation.Types.spring, // 动画类型: spring弹性|linear线性|easeInEaseOut缓出缓入|easeIn缓入|easeOut缓出springDamping: 0.4, // 弹跳动画阻尼系数,配合动画类型为spring使用property: LayoutAnimation.Properties.scaleXY, // 动画特性: opacity透明度|scaleXY缩放},},()=>{console.log('onAnimationDidEnd'); // 当动画结束的时候被调用。只在iOS设备上支持。},()=>{console.log('onError'); // 当动画产生错误的时候被调用。只在iOS设备上支持。});}_onPress = () => {this.setState({width: this.state.width + 50, height: this.state.height + 50});}render(){return(<View style={styles.container}><View style={[styles.viewStyle, {width: this.state.width, height: this.state.height}]}><Text>Hello RN!</Text></View><TouchableOpacity style={styles.btnContainerStyle} onPress={this._onPress}><Text style={{color:'#FFFFFF'}}>触发动画</Text></TouchableOpacity></View>);}
}const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center'},viewStyle: {justifyContent: 'center',alignItems: 'center',borderWidth: 1},btnContainerStyle: {width: 100,height: 30,marginTop: 20,borderRadius: 10,justifyContent: 'center',alignItems: 'center',backgroundColor:'red'}
});export default LayoutAnimationComp;
效果:
Animated
Animated是一个性能极高的动画库。Animated封装了四个可以动画化的组件:Animated.Image,Animated.ScrollView,Animated.Text 和 Animated.View,这表示使用Animated实现动画时由这些组件来完成动画渲染。
Animated实现动画有三个步骤,以透明度渐变为例:
- 定义一个动画变量。在组件中定义一个状态值this.state.fadeInOpacity,把它与动画变量绑定。
- 渲染一个动画化组件。如Animated.View,把第一步定义的this.state.fadeInOpacity作为该组件透明度opacity样式的赋值
- 触发一个动画函数。如线性效果触发 Animated.timing(.....).start(),this.state.fadeInOpacity会根据动画函数实现变化,从而实现透明度渐变的动画效果。
1、定义动画变量
创建方法有两种:
- 纯数值类型变量:new Animated.Value(num),通常与插值函数interpolate配合使用,用于透明度、缩放、位移等动画效果。
- 坐标类型变量:new Animated.ValueXY({x:numX, y:numY}); 仅用于位移动画。
this.state = {fadeInOpacity: new Animated.Value(0),translateXYValue: new Animated.ValueXY({x:0, y:0})
};
那么创建动画变量有什么用?
通过动画变量的变化驱动组件样式的变化,从而实现动画效果。以透明度渐变由隐藏到显示为例,要实现透明度渐变就需要把组件样式中的opacity值由0~1,那么我们可以创建一个变量值初始值为0的动画变量:new Animated.Value(0),通过触发Animated动画函数(篇章后面会介绍),使初始值0变为1并赋值到组件样式中,这样就能实现最终的动画效果。
插值函数interpolate
可以把它当成是实现值映射的功能。如从透明度输入值0~1映射成坐标系的输出值0~100,当透明度在渐变的时候,同时实现组件发生100像素的位移。我们看一段代码:
this.state = {fadeInOpacity: new Animated.Value(0)
};componentDidMount(){Animated.timing(this.state.fadeInOpacity, // 动画中的变量值{toValue: 1, // 动画中的变量值最终数值设定easing: Easing.linear, // 动画类型duration: 1000, // 让动画持续一段时间}).start();
}render(){return(<Animated.View style={[styles.viewStyle, { opacity: this.state.fadeInOpacity,transform: [{translateX: this.state.fadeInOpacity.interpolate({inputRange: [0, 1],outputRange: [0, 300],})}]}]}></Animated.View>);
}
最终的动画效果是组件透明度由0变为1,同时在x轴上位移300屏幕像素。
2、渲染一个动画化组件
目前支持Animated动画库的动画化组件只有4个:Animated.Image,Animated.ScrollView,Animated.Text 和 Animated.View。
组件的样式格式写法一般是这样子的:
<Animated.View style={[styles.viewStyle, { opacity: this.state.fadeInOpacity...}]}
>
</Animated.View>
styles.viewStyle表示组件的基础样式,对象{ opacity: this.state.fadeInOpacity }表示动画变化时的样式改变。
3、触发动画函数
动画函数主要用于驱动动画变量从初始值到最终值之间按照一定的规律发生变化,如线性、弹性或衰变变化。把配置好的动画函数用.start来启动动画和.stop来停止动画。另外,.start将会接收一个动画执行完成后触发的回调函数,该函数可用于实现循环动画效果,如在动画结束后重现在启动一次动画。
线性动画:
Animated.timing(this.state.xxx, // 动画变量变化前的值{toValue: 1, // 动画变量变化后的值easing: Easing.linear, // 动画缓动类型,可以了解Easing库duration: 1000, // 让动画持续一段时间delay: 1000 // 让动画延时开始}
).start();
弹性动画:
Animated.spring(this.state.xxx, // 动画变量变化前的最终值{toValue: 0, // 动画变量变化后的最终值velocity: 0, // 初始速度,默认0// 下面两组配置只能用一种,出来的效果是一样的,只是写法不同// tension: 7, // 张力系数,默认7// friction: 40, // 摩擦系数,默认40// 或bounciness: 8, //反弹系数,默认8speed: 12, //速度,默认12}
).start();
衰变动画:
Animated.decay(this.state.xxx, {velocity: 0.1, // 起始速度,必填参数。deceleration: 0.997 // 速度衰减比例,默认为0.997。
}).start();
根据这三个步骤,就可以实现动画。如透明度渐变动画实例:
import React, { Component } from 'react';
import { View, Text, Animated, StyleSheet, Easing, TouchableOpacity } from 'react-native';class AnimatedComp extends Component {state = {fadeInOpacity: new Animated.Value(0.1),};_onPress = () => {Animated.timing(this.state.fadeInOpacity,{toValue: 1,easing: Easing.linear,duration: 3000}).start();}render(){return(<View style={styles.container}><Animated.View style={[styles.viewStyle, { opacity: this.state.fadeInOpacity,}]}></Animated.View><TouchableOpacity style={styles.btnContainerStyle} onPress={this._onPress}><Text style={{color:'#FFFFFF'}}>触发动画</Text></TouchableOpacity></View>);}
}const styles = StyleSheet.create({container: {flex: 1, justifyContent: 'center',alignItems: 'center'},viewStyle: {width: 100,height: 100,justifyContent: 'center',alignItems: 'center',borderWidth: 1,backgroundColor: 'green'},btnContainerStyle: {width: 100,height: 30,marginTop: 20,borderRadius: 10,justifyContent: 'center',alignItems: 'center',backgroundColor:'red'}
});export default AnimatedComp;
效果:
位移动画实例:
import React, { Component } from 'react';
import { View, Text, Animated, StyleSheet, Easing, TouchableOpacity } from 'react-native';class AnimatedComp extends Component {state = {translateXYValue: new Animated.ValueXY({x:0, y:0})};_onPress = () => {Animated.timing(this.state.translateXYValue,{toValue: ({x:100, y:0}),easing: Easing.linear,duration: 1000}).start();}render(){return(<View style={styles.container}><Animated.View style={[styles.viewStyle, { transform: [{translateX: this.state.translateXYValue.x}, // x轴移动{translateY: this.state.translateXYValue.y}, // y轴移动]}]}></Animated.View><TouchableOpacity style={styles.btnContainerStyle} onPress={this._onPress}><Text style={{color:'#FFFFFF'}}>触发动画</Text></TouchableOpacity></View>);}
}const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center'},viewStyle: {width: 100,height: 100,justifyContent: 'center',alignItems: 'center',borderWidth: 1,backgroundColor: 'green'},btnContainerStyle: {width: 100,height: 30,marginTop: 20,borderRadius: 10,justifyContent: 'center',alignItems: 'center',backgroundColor:'red'}
});export default AnimatedComp;
效果等同于通过interpolate映射的方式实现位移(与上例仅写法不同)
import React, { Component } from 'react';
import { View, Text, Animated, StyleSheet, Easing, TouchableOpacity } from 'react-native';class AnimatedComp extends Component {state = {translateValue: new Animated.Value(1)}_onPress = () => {Animated.timing(this.state.translateValue,{toValue: 0,easing: Easing.linear,duration: 1000}).start();}render(){return(<View style={styles.container}><Animated.View style={[styles.viewStyle, { transform: [{translateX: this.state.translateValue.interpolate({inputRange: [0, 1],outputRange: [100, 0]})}]}]}></Animated.View><TouchableOpacity style={styles.btnContainerStyle} onPress={this._onPress}><Text style={{color:'#FFFFFF'}}>触发动画</Text></TouchableOpacity></View>);}
}const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center'},viewStyle: {width: 100,height: 100,justifyContent: 'center',alignItems: 'center',borderWidth: 1,backgroundColor: 'green'},btnContainerStyle: {width: 100,height: 30,marginTop: 20,borderRadius: 10,justifyContent: 'center',alignItems: 'center',backgroundColor:'red'}
});export default AnimatedComp;
效果:
衰变动画实例:
import React, { Component } from 'react';
import { View, Text, Animated, StyleSheet, TouchableOpacity } from 'react-native';class AnimatedComp extends Component {state = {bounceValue : new Animated.Value(1)};componentDidMount(){// 监听bounceValue衰变过程this.state.bounceValue.addListener((state) => { console.log('bounceValue=>' + state.value);});this.state.bounceValue.stopAnimation((state) => { console.log('bounceValue=>' + state.value);});}_onPress = () => {Animated.decay(this.state.bounceValue, {velocity: 0.02, // 起始速度,必填参数。deceleration: 0.997 // 速度衰减比例,默认为0.997。}).start();}render(){return(<View style={styles.container}><Animated.View style={[styles.viewStyle, { transform: [{scale: this.state.bounceValue}]}]}><Text>Hello RN!</Text></Animated.View><TouchableOpacity style={styles.btnContainerStyle} onPress={this._onPress}><Text style={{color:'#FFFFFF'}}>触发动画</Text></TouchableOpacity></View>);}
}const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center'},viewStyle: {width: 100,height: 100,justifyContent: 'center',alignItems: 'center',borderWidth: 1,backgroundColor: 'green'},btnContainerStyle: {width: 100,height: 30,marginTop: 20,borderRadius: 10,justifyContent: 'center',alignItems: 'center',backgroundColor:'red'}
});export default AnimatedComp;
效果:
通过interpolate映射同步改变多个样式:
import React, { Component } from 'react';
import { View, Text, Animated, StyleSheet, TouchableOpacity } from 'react-native';class AnimatedComp extends Component {state = {translateValue: new Animated.Value(1)};_onPress = () => {Animated.spring(this.state.translateValue, {toValue: 0,velocity: 0,bounciness: 10,speed: 12} ).start();}render(){return(<View style={styles.container}><Animated.View style={[styles.viewStyle, { transform: [{scale: this.state.translateValue.interpolate({inputRange: [0, 1],outputRange: [1, 3],})},{translateX: this.state.translateValue.interpolate({inputRange: [0, 1],outputRange: [0, 50],})},{rotate: this.state.translateValue.interpolate({inputRange: [0, 1],outputRange: ['0deg', '720deg'],})},]}]}></Animated.View><TouchableOpacity style={styles.btnContainerStyle} onPress={this._onPress}><Text style={{color:'#FFFFFF'}}>触发动画</Text></TouchableOpacity></View>);}
}const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center'},viewStyle: {width: 100,height: 100,justifyContent: 'center',alignItems: 'center',borderWidth: 1,backgroundColor: 'green'},btnContainerStyle: {width: 100,height: 30,marginTop: 20,borderRadius: 10,justifyContent: 'center',alignItems: 'center',backgroundColor:'red'}
});export default AnimatedComp;
效果:
多个动画执行顺序
多个动画间有三种顺序方式:
- Animated.sequence(Animates<Array>),表示按顺序运行动画
- 并发:Animated.parallel(Animates<Array>),表示并发运行动画
- Animated.stagger(delayTime<Number>, Animates<Array>),表示在上一个动画开始隔delayTime毫秒后执行下一个动画
顺序运行动画实例:
import React, { Component } from 'react';
import { View, Text, Animated, StyleSheet, Easing, TouchableOpacity } from 'react-native';class AnimatedComp extends Component {state = {fadeInOpacity: new Animated.Value(0.1),translateValue: new Animated.Value(1)};_onPress = () => {Animated.sequence([// Animated.delay(1000), // 延时1秒后开始第一个动画Animated.timing(this.state.fadeInOpacity,{toValue: 1,easing: Easing.linear,duration: 2000}),Animated.timing(this.state.translateValue,{toValue: 0,easing: Easing.linear,duration: 2000,})]).start();}render(){return(<View style={styles.container}><Animated.View style={[styles.viewStyle, { opacity: this.state.fadeInOpacity,transform: [{translateX: this.state.translateValue.interpolate({inputRange: [0, 1],outputRange: [100, 0]})}]}]}></Animated.View><TouchableOpacity style={styles.btnContainerStyle} onPress={this._onPress}><Text style={{color:'#FFFFFF'}}>触发动画</Text></TouchableOpacity></View>);}
}const styles = StyleSheet.create({container: {flex: 1,justifyContent: 'center',alignItems: 'center'},viewStyle: {width: 100,height: 100,justifyContent: 'center',alignItems: 'center',borderWidth: 1,backgroundColor: 'green'},btnContainerStyle: {width: 100,height: 30,marginTop: 20,borderRadius: 10,justifyContent: 'center',alignItems: 'center',backgroundColor:'red'}
});export default AnimatedComp;
效果:
滚动动画
滚动动画是指随着ScrollView组件滚动实现页面内的样式变化。贴上代码:
import React from 'react';
import { View, Animated, StyleSheet, ScrollView, Image } from 'react-native';let deviceHeight = require('Dimensions').get('window').height;
let deviceWidth = require('Dimensions').get('window').width;class ScrollAnimatedComp extends React.Component {state = {xOffset: new Animated.Value(0),xyOffset: new Animated.ValueXY({x:0,y:0})};componentDidMount(){// new Animated.ValueXY() 类型监听this.state.xyOffset.addListener((value) => { console.log('xyOffset=>x:' + value.x + ' y:' + value.y);});this.state.xyOffset.stopAnimation((value) => { console.log('xyOffset=>x:' + value.x + ' y:' + value.y);});// new Animated.Value() 类型值监听this.state.xOffset.addListener((state) => { console.log('xOffset=>' + state.value);});this.state.xOffset.stopAnimation((state) => { console.log('xOffset=>' + state.value);});}// ****Animated.event是实现手势控制动画的关键,允许手势或其它事件直接绑定到动态值上。这里的Aniamted.event的输入是一个数组,用来做数据绑定 。 render(){return(<View style={styles.container}><ScrollViewpagingEnabled={true}horizontal={true}showsHorizontalScrollIndicator={false}style={{width:deviceWidth,height:deviceHeight}}scrollEventThrottle={100}onScroll={Animated.event([{nativeEvent: {contentOffset: {x: this.state.xOffset}}}] // 把contentOffset.x绑定给this.state.xOffset)}><Animated.Image source={require('../../../assets/images/watch.jpg')}style={{height:deviceHeight,width:deviceWidth,opacity: this.state.xOffset.interpolate({ //映射到0.0,1.0之间inputRange: [0, deviceWidth],outputRange: [1.0, 0.0]})}}resizeMode='cover'/><Image source={require('../../../assets/images/watch.jpg')} style={{height:deviceHeight, width:deviceWidth}} resizeMode='cover' /></ScrollView></View>);}}const styles = StyleSheet.create({container: {marginTop:25,flex: 1},
});export default ScrollAnimatedComp;
效果:
至此,RN动画篇将全部完毕,欢迎交流。
react-native系列(13)动画篇:Animated动画库和LayoutAnimation布局动画详解相关推荐
- react native学习笔记29——动画篇 Animated高级动画
1.前言 上一节我们学习了全局的布局动画api--LayoutAnimation,体验到其流畅柔和的动画效果,但有时我们需要实现一些更精细化的动画,或者完成一些组合动画,这时我们可以使用React N ...
- 【REACT NATIVE 系列教程之十三】利用LISTVIEW与TEXTINPUT制作聊天/对话框获取组件实例常用的两种方式...
本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/react-native/2346.html ...
- 【REACT NATIVE 系列教程之十二】REACT NATIVE(JS/ES)与IOS(OBJECT-C)交互通信
一用到跨平台的引擎必然要有引擎与各平台原生进行交互通信的需要.那么Himi先讲解React Native与iOS之间的通信交互. 本篇主要分为两部分讲解:(关于其中讲解的OC语法等不介绍,不懂的请自行 ...
- html中如何设置动画鼠标,使用animate动画库添加鼠标经过动画
使用animate动画库添加鼠标经过动画 蓝叶 网站设计 2016-11-27 11679 4评论 animate是一个css3动画库,使用它简单几步就能很轻松的为网站增加各种 ...
- (转)C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
原文链接:https://www.cnblogs.com/landeanfen/p/5501487.html 阅读目录 一.void无返回值 二.IHttpActionResult 1.Json(T ...
- 【5年Android从零复盘系列之二十】Android自定义View(15):Matrix详解(图文)【转载】
[转载]本文转载自麻花儿wt 的文章<android matrix 最全方法详解与进阶(完整篇)> [5年Android从零复盘系列之二十]Android自定义View(15):Matri ...
- c linux time微秒_学习linux,看这篇1.5w多字的linux命令详解(6小时讲明白Linux)
用心分享,共同成长 没有什么比每天进步一点点更重要了 本篇文章主要讲解了一些linux常用命令,主要讲解模式是,命令介绍.命令参数格式.命令参数.命令常用参数示例.由于linux命令较多,我还特意选了 ...
- 第二篇supervisor集群管理工具cesi安装详解-如何安装supervisor-cesiwebUI
第二篇supervisor集群管理工具cesi安装详解-如何安装supervisor-cesiwebUI 介绍 安装 解压 安装依赖 修改配置 注册为系统服务 启动 登录一下,发现报错了 解决方法 介 ...
- [Python从零到壹] 五十一.图像增强及运算篇之图像灰度直方图对比分析万字详解
欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...
最新文章
- c语言区间,C 语言实例 – 循环输出区间范围内的奇数/偶数 | 菜鸟教程
- 清华开源Jittor:首个国内高校自研深度学习框架,一键转换PyTorch
- 程序员 :超越软件蓝领的七种武器
- ORB-SLAM2源代码中ROS部分ros-mono源代码中subscribe /camera/image_raw topic谁发布publish的
- Cocos2D-X2.2.3学习笔记5(UI系统)
- 使用Prometheus监控Linux系统各项指标
- MySQL--SQL中的安全问题
- 堪称暴力美学的可视化大屏是怎么做的?附无代码硬核教程
- C ++ 指针 | 指针的详细概念和使用_1
- Leetcode每日一题:70.climbing-stairs(爬楼梯)
- 数据库建表设计规范及原则
- GB28181协议简介及实践
- bam文件读取_sam和bam文件处理
- 基于php的校园垃圾分类网站的设计与实现
- 网页里添加Skype、WhatsApp及时聊天窗口
- datax运行无法加载主类
- 浏览器被恶意篡改(百分百成功)
- 华为鸿蒙harmonyos面相全场,鸿蒙系统官网2.0报名
- C语言经典例题及答案3
- 复分析理论---如何形象理解平均值公式和最大模原理