將要做動畫的元件用Animated.View 進行包裝
設定Animated.Value
並給予一個動畫尚未開始前的初始值
對動畫進行行為疊加 (Parallel, Sequence, Loop) 並執行動畫
const { scaleAnimatedValue } = this.state
const scaleAnimation = {
transform: [ { scale: scaleAnimatedValue } ]
};
return (
<Animated.View style={[scaleAnimation]}>
<Icon size={56} color="#777" name="arrow-left" />
</Animated.View>
)
}
const yourTransformStyle = {
transform: [{ scale: 1 }]
};
目前useNativeDriver: true 只support Transform Props 與 opacity
<WebView
scrollEnabled={false}
style={styles.description}
source={{ html: productDetail.description, baseUrl: '' }}
ref={(ref) => {
this.webview = ref;
}}
onNavigationStateChange={(event) => {
if (event.url.indexOf('about:blank') < 0) {
this.webview.stopLoading();
Linking.openURL(event.url);
}
}}
/>
import RatingBar from 'react-native-star-rating';
...
<RatingBar
selectedStar={() => {
this.setState({
rating: rating === 0 ? 1 : 0
});
}}
maxStars={1}
rating={rating}
iconSet="FontAwesome"
/>
componentDidMount() {
const scaleUp = Animated.timing(this.state.scaledValue, {
duration: 200,
toValue: 1.3
});
const scaleDown = Animated.timing(this.state.scaledValue, {
duration: 200,
toValue: 1
});
this.bounceAnimation = Animated.sequence([scaleUp, scaleDown]);
}
render(){
return (
<Animated.View
style={[styles.rating, { transform: [{ scale: this.state.scaledValue }] }]}
>
<RatingBar
selectedStar={() => {
this.setState({
rating: rating === 0 ? 1 : 0
});
this.bounceAnimation.start(() => {
this.bounceAnimation.reset();
});
}}
maxStars={1}
rating={rating}
iconSet="FontAwesome"
/>
</Animated.View>
)
}
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
StyleSheet,
View,
ViewPropTypes,
Text,
Image,
Animated,
TouchableOpacity,
Easing
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
const styles = StyleSheet.create({
container: {
flex: 1
},
image: {
height: '100%',
width: '100%',
position: 'absolute'
},
iconLeft: {
left: 10,
position: 'absolute'
},
iconRight: {
right: 10,
position: 'absolute'
},
iconContainer: {
justifyContent: 'center'
},
animatedIcon: {
height: 50,
width: 50
},
center: {
justifyContent: 'center',
alignItems: 'center'
}
});
export default class ImageGallery extends Component {
static propTypes = {
images: PropTypes.array.isRequired,
contentContainerStyle: ViewPropTypes.style.isRequired
};
state = {
currentIdx: 0,
scaleAnimatedValue: new Animated.Value(1)
};
componentDidMount = () => {
const DURATION = 1000;
const toBig = Animated.timing(this.state.scaleAnimatedValue, {
duration: DURATION,
easing: Easing.linear,
toValue: 1.3
});
const toSmall = Animated.timing(this.state.scaleAnimatedValue, {
duration: DURATION,
easing: Easing.linear,
toValue: 1
});
const breathe = Animated.loop(Animated.sequence([toBig, toSmall]));
breathe.start();
};
next = (nextInc) => {
const { currentIdx } = this.state;
const next =
currentIdx + nextInc < 0 || currentIdx + nextInc >= this.props.images.length
? 0
: currentIdx + nextInc;
return next;
};
toRight = () => {
this.setState({
currentIdx: this.next(1)
});
};
toLeft = () => {
this.setState({
currentIdx: this.next(-1)
});
};
render() {
const { currentIdx, scaleAnimatedValue } = this.state;
const currentImageUri = this.props.images[currentIdx];
const scaleAnimation = {
transform: [{ scale: scaleAnimatedValue }]
};
return (
<View style={[this.props.contentContainerStyle]}>
<Image style={styles.image} source={{ uri: currentImageUri }} />
<View style={[styles.container, styles.iconContainer]}>
<TouchableOpacity
activeOpacity={0.7}
style={[styles.iconRight, styles.center, styles.animatedIcon]}
onPress={this.toRight}
>
<Animated.View style={[scaleAnimation]}>
<Icon size={28} color="#ccc" name="arrow-right" />
</Animated.View>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
style={[styles.iconLeft, styles.center, styles.animatedIcon]}
onPress={this.toLeft}
>
<Animated.View style={[scaleAnimation]}>
<Icon size={28} color="#ccc" name="arrow-left" />
</Animated.View>
</TouchableOpacity>
</View>
</View>
);
}
}