rn-tween
Version:
Help create animated react native tween component
322 lines (268 loc) • 5.95 kB
JavaScript
import React from 'react';
import { Animated, Easing } from 'react-native';
const transformProperties = [
'perspective',
'rotate',
'rotateX',
'rotateY',
'rotateZ',
'scale',
'scaleX',
'scaleY',
'skewX',
'skewY',
'translateX',
'translateY'
];
export { Easing as TweenEasing };
const defaultProps = {
autoStartName: null,
firstUsedConfigName: null,
initialConfig: null,
onComplete: null
};
export class RNTween extends React.Component {
constructor(props) {
super(props);
this.state = {
animatedStyles: [],
tweenConfig: {},
playedTweenName: '',
mounted: false
};
this.prepare(props.initialConfig);
const configNames = Object.keys(this.state.tweenConfig);
if (configNames.length > 0) {
const config = this.state.tweenConfig[
props.firstUsedConfigName || configNames[0]
];
if (config) {
this.state.animatedStyles = this._getComputedAnimatedStyles(config);
}
}
this.props.autoStartName &&
this.start({ name: this.props.autoStartName, reversed: false });
}
componentDidMount() {
this.setState({ mounted: true });
}
render() {
const { style, AnimatedComponent, ...restProps } = this.props;
return (
<AnimatedComponent
{...restProps}
style={[style, ...this.state.animatedStyles]}
/>
);
}
_createAnimatedTween(config, reversed) {
const {
delay,
value,
from,
to,
interpolated,
duration,
easing,
useNative
} = config;
value.setValue(
reversed ? (interpolated ? 1 : to) : interpolated ? 0 : from
);
const createTiminingTween = () =>
Animated.timing(value, {
toValue: reversed ? (interpolated ? 0 : from) : interpolated ? 1 : to,
delay: delay === null || delay === undefined ? 0 : delay,
duration: duration === null || duration === undefined ? 0 : duration,
useNativeDriver:
useNative === null || useNative === undefined ? true : useNative,
easing: easing || Easing.linear
});
if (config.loop) {
return Animated.loop(createTiminingTween());
} else {
return createTiminingTween();
}
}
_getComputedAnimatedStyles(config) {
const animatedStyles = config.configs
.filter(e => (e.styleProperty ? true : false))
.map(e => {
const isStyleForTransform = transformProperties.includes(
e.styleProperty
);
let value = null;
if (e.interpolated) {
value = e.value.interpolate({
inputRange: [0, 1],
outputRange: [e.from, e.to]
});
} else {
value = e.value;
}
if (isStyleForTransform) {
return {
transform: [
{
[e.styleProperty]: value
}
]
};
} else {
return {
[e.styleProperty]: value
};
}
});
return animatedStyles;
}
prepare(config) {
this.stop();
if (!config) {
return;
}
const tweenConfig = {};
for (let k in config) {
const c = config[k];
const newConfig = { mode: c.mode };
newConfig.configs = c.configs
.filter(e => (e.styleProperty ? true : false))
.map(e => {
return {
...e,
value: new Animated.Value(typeof e.from === 'string' ? 0 : e.from),
interpolated: typeof e.from === 'string'
};
});
tweenConfig[k] = newConfig;
}
if (this.state.mounted) {
this.setState({ tweenConfig });
} else {
this.state = { ...this.state, tweenConfig };
}
}
start(option) {
const config = this.state.tweenConfig[option.name];
if (!config) {
return;
}
this.stop();
if (this.state.playedTweenName != option.name) {
const animatedStyles = this._getComputedAnimatedStyles(config);
if (this.state.mounted) {
this.setState({
playedTweenName: option.name,
animatedStyles
});
} else {
this.state = {
...this.state,
playedTweenName: option.name,
animatedStyles
};
}
}
const tweens = config.configs.map(e =>
this._createAnimatedTween(e, option.reversed)
);
let tweenStarter =
!config.mode || config.mode == 'parallel'
? Animated.parallel
: Animated.sequence;
this._runningTween = tweenStarter(tweens);
this._runningTween.start(() => {
this._runningTween = null;
this.props.onComplete && this.props.onComplete();
option.onComplete && option.onComplete();
});
}
stop() {
this._runningTween && this._runningTween.stop();
this._runningTween = null;
}
}
RNTween.defaultProps = {
...defaultProps
};
export class RNTweenView extends React.Component {
render() {
const { AnimatedComponent, ...restProps } = this.props;
return (
<RNTween
ref={this._onRef}
{...restProps}
AnimatedComponent={Animated.View}
/>
);
}
_onRef = ref => {
this._tween = ref;
};
prepare(config) {
this._tween && this._tween.prepare(config);
}
start(option) {
this._tween && this._tween.start(option);
}
stop() {
this._tween && this._tween.stop();
}
}
RNTweenView.defaultProps = {
...defaultProps
};
export class RNTweenText extends React.Component {
render() {
const { AnimatedComponent, ...restProps } = this.props;
return (
<RNTween
ref={this._onRef}
{...restProps}
AnimatedComponent={Animated.Text}
/>
);
}
_onRef = ref => {
this._tween = ref;
};
prepare(config) {
this._tween && this._tween.prepare(config);
}
start(option) {
this._tween && this._tween.start(option);
}
stop() {
this._tween && this._tween.stop();
}
}
RNTweenText.defaultProps = {
...defaultProps
};
export class RNTweenImage extends React.Component {
render() {
const { AnimatedComponent, ...restProps } = this.props;
return (
<RNTween
ref={this._onRef}
{...restProps}
AnimatedComponent={Animated.Image}
/>
);
}
_onRef = ref => {
this._tween = ref;
};
prepare(config) {
this._tween && this._tween.prepare(config);
}
start(option) {
this._tween && this._tween.start(option);
}
stop() {
this._tween && this._tween.stop();
}
}
RNTweenImage.defaultProps = {
...defaultProps
};