学习笔记--方便下次查看
大多数情况下,在 React Native 中创建动画是推荐使用 Animated API 的,其提供了三个主要的方法用于创建动画:
1.API
- Animated.timing() -- 最常用的动画类型,使一个值按照一个过渡曲线而随时间变化。
- Animated.decay() -- 衰变效果,以一个初始的速度和一个衰减系数逐渐减慢变为0。
- Animated.spring() -- 弹簧效果,基础的单次弹跳物理模型实现的 spring 动画
2.动画组件
Animated 封装了四个可以动画化的组件:
Animated.View、Animated.Text、Animated.Image、Animated.ScrollView
动画必须依赖这几个组件--使用当然和平常开发一样
4.Animated.timing()的使用
最常用的动画--我们看如何使用吧;
这边是一个渐变的效果;
渐变效果.gif
import React from "react";
import { Animated } from "react-native";
export default class FadeInView extends React.Component {
state = {
fadeInOpacity: new Animated.Value(0) // 透明度初始值设为0
};
componentDidMount() {
Animated.timing(
// 随时间变化而执行动画
this.state.fadeInOpacity, // 动画中的变量值
{
toValue: 1, // 透明度最终变为1,即完全不透明
duration: 6000 // 让动画持续一段时间
}
).start();
}
render() {
const { fadeInOpacity } = this.state;
return (
<Animated.View // 使用专门的可动画化的View组件
style={{
width: 100,
height: 100,
backgroundColor: "red",
opacity: fadeInOpacity // 将透明度指定为动画变量值
}}
/>
);
}
}
这个demo 简单说一下代码的意思--方便理解
- 在state中初始化一个动画效果值fadeInOpacity为0
- 在生命周期中-使用动画的函数timing- 让fadeInOpacity在6秒钟从0变成1
- fadeInOpacity的值使用在render里面控制opacity的值
从而达到了一个渐变的效果
Animated.timing中的toValue配置项不一定是配置成1,也可以配置成其他数字
比如控制一个view的宽度从0到100的过程,也可以试一下;
3.插值函数(interpolate)
interpolate():将输入值范围转换为输出值范围。
譬如:把0-1映射到0-10。
接下来,我们结合一个个的例子介绍它们的用法-当初自己学习挺懵逼的-先看demo
旋转.gif
import { Animated, Easing, StyleSheet, View } from "react-native";
export default class Rotate extends React.Component<any, any> {
state = {
spinValue: new Animated.Value(0) //初始化动画的状态值
};
componentDidMount() {
Animated.timing(this.state.spinValue, { //动画函数
toValue: 1, //渐变的值
duration: 3000, //时间
}).start();
}
render() {
const spin = this.state.spinValue.interpolate({
inputRange: [0, 1], //入参---是从0变化到1
outputRange: ["0deg", "360deg"] //出参---是从0deg变化到360deg
});
return (
<View style={styles.container}>
<Animated.Image
style={{
width: 300,
height: 300,
transform: [{ rotate: spin }]
}}
resizeMode={"contain"}
source={{
uri:
"https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png"
}}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
}
});
这是一个旋转的动画效果--代码和上文的渐变效果其实基本一致--唯一不同的是使用了interpolate
需要注意的是对inputRange
和outputRange
的理解:动画属性值有一个从0到1的过程,我们其他属性也需要一个这样的过程,比如动画从0-360的旋转过程。那就需要一个映射的过程了。动画属性值从0到1 那我们旋转就从0-360
。从而达到动画的效果了;
4.1interpolate的其他使用方式
inputRange是从0-1增长的过程。当然我们可以设置成多段的比如[0,0.5,1]
,那我们outputRange的也可以映射自己想要的过程。比如过程编导0.5的的时候,我想让他逆时针旋转,我们outputRange就可以写成这样[0,'360deg',0]
下面看下各种demo
import React from "react";
import { Animated, Easing, StyleSheet, View, Text } from "react-native";
export default class FadeInView extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
animatedValue: new Animated.Value(0)
};
}
componentDidMount() {
this.spin();
}
spin() {
this.state.animatedValue.setValue(0);
Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 2000,
easing: Easing.linear
// delay:1000
}).start(() => this.spin());
}
render() {
let { animatedValue } = this.state;
const marginLeft = animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 100]
});
const opacity = animatedValue.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 1, 0]
});
const movingMargin = animatedValue.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 300, 0]
});
const textSize = animatedValue.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [18, 32, 18]
});
const rotateX = animatedValue.interpolate({
inputRange: [0, 0.5, 1],
outputRange: ["0deg", "180deg", "0deg"]
});
return (
<View style={styles.container}>
<Animated.View
style={{
marginLeft,
height: 30,
width: 40,
backgroundColor: "red"
}}
/>
<Animated.View
style={{
opacity,
marginTop: 10,
height: 30,
width: 40,
backgroundColor: "blue"
}}
/>
<Animated.View
style={{
marginLeft: movingMargin,
marginTop: 10,
height: 30,
width: 40,
backgroundColor: "orange"
}}
/>
<Animated.Text
style={{
fontSize: textSize,
marginTop: 10,
color: "green"
}}
>
Animated Text!
</Animated.Text>
<Animated.View
style={{
transform: [{ rotateX }],
marginTop: 50,
height: 30,
width: 40,
backgroundColor: "black"
}}
>
<Text style={{ color: "white" }}>Hello from TransformX</Text>
</Animated.View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
我们可以用动画的值用在opacity、margins、text sizes 和 rotation 等样式属性中。就出现了上面的运动旋转等动画了
5.start()循环播放
如何实现动画的持续播放呢--那就需要用到我们start()的回调中调用自己
,当然他是在动画结束后才会调用的-这个过程让我想到了 递归
spin() {
this.state.animatedValue.setValue(0);
Animated.timing(this.state.animatedValue, {
.....
}).start(() => this.spin());
}
tips:有一个小细节就this.state.animatedValue.setValue(0);这一步。动画结束后需要将初始值变成最初的值
因为他有一个0-1的渐变过程。所以需要重置一下变量;
6.Animated.spring()
这个就是弹框效果了--直接看代码吧。使用都一样的
弹簧效果.gif
import React from "react";
import { Animated, StyleSheet, Text, View } from "react-native";
export default class FadeInView extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
springValue: new Animated.Value(0)
};
}
componentDidMount() {
this.spring();
}
spring() {
this.state.springValue.setValue(0);
Animated.spring(this.state.springValue, {
toValue: 1, //放大的值:越大放大越大
friction: 1, //摩擦力:越大就越不能晃动的厉害
tension:1 //张力:越大就越有弹性-生动点
}).start();
}
render() {
return (
<View style={styles.container}>
<Text
style={{ marginBottom: 100 }}
onPress={() => {
this.spring();
}}
>
点击可以出发动画
</Text>
<Animated.Image
style={{
width: 227,
height: 200,
transform: [{ scale: this.state.springValue }]
}}
source={{
uri:
"https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png"
}}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
}
});
里面有一些属性需要注意一下--
toValue
: 放大的值:越大放大越大
friction
: 摩擦力:越大就越不能晃动的厉害
tension
: 张力:越大就越有弹性-生动点
这边我好想吐槽一句--记笔记真的烦人--
7.Animated.parallel()
Animated.parallel() 会同时开始一个动画数组里的全部动画。
多个动画同时执行.gif
import React from "react";
import {
Animated,
Easing,
StyleSheet,
Text,
TouchableHighlight,
View
} from "react-native";
export default class FadeInView extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
animatedValue1: new Animated.Value(0),
animatedValue2: new Animated.Value(0),
animatedValue3: new Animated.Value(0)
};
}
componentDidMount() {
this.animate();
}
animate() {
this.state.animatedValue1.setValue(0);
this.state.animatedValue2.setValue(0);
this.state.animatedValue3.setValue(0);
const createAnimation = function(value, duration, easing, delay = 0) {
return Animated.timing(value, {
toValue: 1,
duration,
easing,
delay
});
};
Animated.parallel([
createAnimation(this.state.animatedValue1, 2000, Easing.ease),
createAnimation(this.state.animatedValue2, 1000, Easing.ease, 1000),
createAnimation(this.state.animatedValue3, 1000, Easing.ease, 2000)
]).start();
}
spring() {
this.state.springValue.setValue(0.3);
Animated.spring(this.state.springValue, {
toValue: 1,
friction: 1
// tension:1
}).start();
}
render() {
const scaleText = this.state.animatedValue1.interpolate({
inputRange: [0, 1],
outputRange: [0.5, 2]
});
const spinText = this.state.animatedValue2.interpolate({
inputRange: [0, 1],
outputRange: ["0deg", "720deg"]
});
const introButton = this.state.animatedValue3.interpolate({
inputRange: [0, 1],
outputRange: [-100, 400]
});
return (
<View style={[styles.container]}>
<Animated.View style={{ transform: [{ scale: scaleText }] }}>
<Text>Welcome</Text>
</Animated.View>
<Animated.View
style={{ marginTop: 20, transform: [{ rotate: spinText }] }}
>
<Text style={{ fontSize: 20 }}>to the App!</Text>
</Animated.View>
<Animated.View style={{ top: introButton, position: "absolute" }}>
<TouchableHighlight
onPress={this.animate.bind(this)}
style={styles.button}
>
<Text style={{ color: "white", fontSize: 20 }}>
Click Here To Start
</Text>
</TouchableHighlight>
</Animated.View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
},
button: {
width: 320,
height: 90,
backgroundColor: "blue",
alignItems: "center",
justifyContent: "center"
}
});
8.Animated.parallel()
和 Animated.parallel() 一样, Animated.sequence() 接受一个动画数组。但不同的是,Animated.sequence() 是按顺序执行一个动画数组里的动画,等待一个完成后再执行下一个。
一个个输出播放.gif
import React from "react";
import { Animated, StyleSheet, View } from "react-native";
const arr = [];
for (let i = 0; i < 500; i++) {
arr.push(i);
}
export default class FadeInView extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
animatedValue: []
};
arr.forEach(value => {
this.state.animatedValue[value] = new Animated.Value(0);
});
}
componentDidMount() {
this.animate();
}
animate() {
const animations = arr.map(item => {
return Animated.timing(this.state.animatedValue[item], {
toValue: 1,
duration: 100
});
});
Animated.sequence(animations).start();
}
render() {
const animations = arr.map((a, i) => {
return (
<Animated.View
key={i}
style={{
opacity: this.state.animatedValue[a],
height: 20,
width: 20,
backgroundColor: "red",
marginLeft: 3,
marginTop: 3
}}
/>
);
});
return <View style={styles.container}>{animations}</View>;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row",
flexWrap: "wrap"
}
});
9. Animated.Stagger()
和 Animated.parallel() 和 Animated.sequence() 一样, Animated.Stagger 接受一个动画数组。但不同的是,Animated.Stagger 里面的动画有可能会同时执行(重叠),不过会以指定的延迟来开始。与上述两个动画主要的不同点是 Animated.Stagger 的第一个参数,delay 会被应用到每一个动画
可以同时播放的.gifimport React, {Component} from "react";
import {Animated, StyleSheet, View} from "react-native";
const arr = [];
for (let i = 0; i < 200; i++) {
arr.push(i);
}
export default class animations extends Component<any, any> {
constructor(props) {
super(props);
this.state = {
animatedValue: []
};
arr.forEach(value => {
this.state.animatedValue[value] = new Animated.Value(0);
});
}
componentDidMount() {
this.animate();
}
animate() {
this.state.animatedValue.map(c=>{
c.setValue(0);
})
const animations = arr.map(item => {
return Animated.timing(this.state.animatedValue[item], {
toValue: 1,
duration: 1000
});
});
Animated.stagger(10, animations).start(()=>{this.animate()});
}
render() {
const animations = arr.map((a, i) => {
return (
<Animated.View
key={i}
style={{
opacity: this.state.animatedValue[a].interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 1, 0.3]
}),
height: 20,
width: 20,
backgroundColor: "red",
marginLeft: 3,
marginTop: 3
}}
/>
);
});
return <View style={styles.container}>{animations}</View>;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row",
flexWrap: "wrap"
}
});
网友评论