react-native-insta-story-2
Version:
custom patched version of react-native-insta-story Story component for React Native.
284 lines (256 loc) • 9.35 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import {
PanResponder,
Animated,
Dimensions,
StyleSheet,
Platform,
LogBox
} from 'react-native';
const {width, height} = Dimensions.get('window');
const PESPECTIVE = Platform.OS === 'ios' ? 2.38 : 1.7;
const TR_POSITION = Platform.OS === 'ios' ? 2 : 1.5;
export default class CubeNavigationHorizontal extends React.Component {
constructor(props) {
super(props);
this.pages = this.props.children.map((child, index) => width * -index);
this.fullWidth = (this.props.children.length - 1) * width;
this.state = {
currentPage: 0,
scrollLockPage: this.pages[this.props.scrollLockPage]
};
}
UNSAFE_componentWillMount() {
this._animatedValue = new Animated.ValueXY();
this._animatedValue.setValue({x: 0, y: 0});
this._value = {x: 0, y: 0};
this._animatedValue.addListener(value => {
this._value = value;
});
const onDoneSwiping = (gestureState) => {
if (this.props.callbackOnSwipe) {
this.props.callbackOnSwipe(false);
}
let mod = gestureState.dx > 0 ? 100 : -100;
const currentPage = this._closest(this._value.x + mod)
let goTo = this.pages[currentPage];
this._animatedValue.flattenOffset({
x: this._value.x,
y: this._value.y
});
Animated.spring(this._animatedValue, {
toValue: {x: goTo, y: 0},
friction: 5,
tension: 0.6,
useNativeDriver: false
}).start();
setTimeout(() => {
this.setState({
currentPage
});
if (this.props.callBackAfterSwipe)
this.props.callBackAfterSwipe(currentPage);
}, 500);
}
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => Math.abs(gestureState.dx) > this.props.responderCaptureDx,
onMoveShouldSetPanResponderCapture: (evt, gestureState) =>
Math.abs(gestureState.dx) > this.props.responderCaptureDx,
onPanResponderGrant: (e, gestureState) => {
if (this.props.callbackOnSwipe) {
this.props.callbackOnSwipe(true);
}
this._animatedValue.stopAnimation();
this._animatedValue.setOffset({x: this._value.x, y: this._value.y});
},
onPanResponderMove: (e, gestureState) => {
if (this.props.loop) {
if (gestureState.dx < 0 && this._value.x < -this.fullWidth) {
this._animatedValue.setOffset({x: width});
} else if (gestureState.dx > 0 && this._value.x > 0) {
this._animatedValue.setOffset({x: -(this.fullWidth + width)});
}
}
Animated.event([null, {dx: this._animatedValue.x}], {useNativeDriver: false})(e, gestureState);
},
onPanResponderRelease: (e, gestureState) => {
onDoneSwiping(gestureState);
},
onPanResponderTerminate: (e, gestureState) => {
onDoneSwiping(gestureState);
},
});
}
componentWillReceiveProps(props) {
this.setState({
scrollLockPage: props.scrollLockPage
? this.pages[props.scrollLockPage]
: undefined
});
}
/*
@page: index
*/
scrollTo(page, animated) {
animated = animated == undefined ? true : animated;
if (animated) {
Animated.spring(this._animatedValue, {
toValue: {x: this.pages[page], y: 0},
friction: 5,
tension: 0.6,
useNativeDriver: false
}).start();
} else {
this._animatedValue.setValue({x: this.pages[page], y: 0});
}
this.setState({
currentPage: page
});
}
/*
Private methods
*/
_getTransformsFor = i => {
let scrollX = this._animatedValue.x;
let pageX = -width * i;
let loopVariable = (variable, sign = 1) => variable + Math.sign(sign) * (this.fullWidth + width);
let padInput = (variables) => {
if (!this.props.loop)
return variables;
const returnedVariables = [...variables];
returnedVariables.unshift(...variables.map(variable => loopVariable(variable, -1)))
returnedVariables.push(...variables.map(variable => loopVariable(variable, 1)))
return returnedVariables;
}
let padOutput = (variables) => {
if (!this.props.loop)
return variables;
const returnedVariables = [...variables];
returnedVariables.unshift(...variables)
returnedVariables.push(...variables)
return returnedVariables;
}
let translateX = scrollX.interpolate({
inputRange: padInput([pageX - width, pageX, pageX + width]),
outputRange: padOutput([(-width - 1) / TR_POSITION, 0, (width + 1) / TR_POSITION]),
extrapolate: 'clamp'
});
let rotateY = scrollX.interpolate({
inputRange: padInput([pageX - width, pageX, pageX + width]),
outputRange: padOutput(['-60deg', '0deg', '60deg']),
extrapolate: 'clamp'
});
let translateXAfterRotate = scrollX.interpolate({
inputRange: padInput([
pageX - width,
pageX - width + 0.1,
pageX,
pageX + width - 0.1,
pageX + width
]),
outputRange: padOutput([
-width - 1,
(-width - 1) / PESPECTIVE,
0,
(width + 1) / PESPECTIVE,
+width + 1
]),
extrapolate: 'clamp'
});
let opacity = scrollX.interpolate({
inputRange: padInput([
pageX - width,
pageX - width + 10,
pageX,
pageX + width - 250,
pageX + width
]),
outputRange: padOutput([0, 0.6, 1, 0.6, 0]),
extrapolate: 'clamp'
});
return {
transform: [
{perspective: width},
{translateX},
{rotateY: rotateY},
{translateX: translateXAfterRotate}
],
opacity: opacity
};
};
_renderChild = (child, i) => {
let expandStyle = this.props.expandView
? {paddingTop: 100, paddingBottom: 100, height: height + 200}
: {width, height};
let style = [child.props.style, expandStyle];
let props = {
i,
style
};
let element = React.cloneElement(child, props);
return (
<Animated.View
style={[
StyleSheet.absoluteFill,
{backgroundColor: 'transparent'},
this._getTransformsFor(i, false)
]}
key={`child- ${i}`}
pointerEvents={this.state.currentPage == i ? 'auto' : 'none'}
>
{element}
</Animated.View>
);
};
_closest = num => {
let array = this.pages;
let i = 0;
let minDiff = 1000;
let ans;
for (i in array) {
let m = Math.abs(num - array[i]);
if (m < minDiff) {
minDiff = m;
ans = i;
}
}
return ans;
};
render() {
let expandStyle = this.props.expandView
? {top: -100, left: 0, width, height: height + 200}
: {width, height};
return (
<Animated.View
style={[{position: 'absolute'}]}
ref={view => {
this._scrollView = view;
}}
{...this._panResponder.panHandlers}
>
<Animated.View
style={[
{backgroundColor: '#000', position: 'absolute', width, height},
expandStyle
]}
>
{this.props.children.map(this._renderChild)}
</Animated.View>
</Animated.View>
);
}
}
CubeNavigationHorizontal.propTypes = {
callBackAfterSwipe: PropTypes.func,
callbackOnSwipe: PropTypes.func,
scrollLockPage: PropTypes.number,
responderCaptureDx: PropTypes.number,
expandView: PropTypes.bool,
loop: PropTypes.bool
};
CubeNavigationHorizontal.defaultProps = {
responderCaptureDx: 60,
expandView: false,
loop: false
};