react-native-story-component
Version:
Story component for React Native.
221 lines • 7 kB
JavaScript
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import React, { useRef, useState, useEffect, useMemo, useImperativeHandle, forwardRef } from 'react';
import { PanResponder, Animated, Dimensions, StyleSheet, Platform } 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;
const CubeNavigationHorizontal = /*#__PURE__*/forwardRef((_ref, ref) => {
let {
loop = false,
responderCaptureDx = 60,
...props
} = _ref;
const {
pages,
fullWidth
} = useMemo(() => ({
pages: props.children.map((_child, index) => width * -index),
fullWidth: (props.children.length - 1) * width
}), [props.children]);
const [currentPage, setCurrentPage] = useState(0);
const _animatedValue = useRef(new Animated.ValueXY({
x: 0,
y: 0
})).current;
const _value = useRef({
x: 0,
y: 0
});
const _panResponder = PanResponder.create({
onMoveShouldSetPanResponderCapture: (_evt, gestureState) => Math.abs(gestureState.dx) > responderCaptureDx,
onPanResponderGrant: (_e, _gestureState) => {
if (props.callbackOnSwipe) {
props.callbackOnSwipe(true);
}
_animatedValue.stopAnimation();
_animatedValue.setOffset(_value.current);
},
onPanResponderMove: (e, gestureState) => {
if (loop) {
if (gestureState.dx < 0 && _value.current.x < -fullWidth) {
_animatedValue.setOffset({
x: width,
y: 0
});
} else if (gestureState.dx > 0 && _value.current.x > 0) {
_animatedValue.setOffset({
x: -(fullWidth + width),
y: 0
});
}
}
Animated.event([null, {
dx: _animatedValue.x
}], {
useNativeDriver: false
})(e, gestureState);
},
onPanResponderRelease: (_e, gestureState) => {
onDoneSwiping(gestureState);
},
onPanResponderTerminate: (_e, gestureState) => {
onDoneSwiping(gestureState);
}
});
useEffect(() => {
_animatedValue.addListener(value => {
_value.current = value;
});
}, [_animatedValue]);
useImperativeHandle(ref, () => ({
scrollTo(page) {
let animated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
const x = pages[page];
if (animated) {
Animated.spring(_animatedValue, {
toValue: {
x,
y: 0
},
friction: 5,
tension: 0.6,
useNativeDriver: false
}).start();
} else {
_animatedValue.setValue({
x,
y: 0
});
}
setCurrentPage(page);
}
}));
const onDoneSwiping = gestureState => {
if (props.callbackOnSwipe) {
props.callbackOnSwipe(false);
}
const mod = gestureState.dx > 0 ? 100 : -100;
const _currentPage = _closest(_value.current.x + mod);
const x = pages[_currentPage];
_animatedValue.flattenOffset();
Animated.spring(_animatedValue, {
toValue: {
x,
y: 0
},
friction: 5,
tension: 0.6,
useNativeDriver: false
}).start();
setTimeout(() => {
setCurrentPage(currentPage);
if (props.callBackAfterSwipe) props.callBackAfterSwipe(currentPage);
}, 500);
};
const _getTransformsFor = i => {
let scrollX = _animatedValue.x;
let pageX = -width * i;
const loopVariable = function (variable) {
let sign = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
return variable + Math.sign(sign) * (fullWidth + width);
};
const padInput = variables => {
if (!loop) return variables;
const returnedVariables = [...variables];
returnedVariables.unshift(...variables.map(variable => loopVariable(variable, -1)));
returnedVariables.push(...variables.map(variable => loopVariable(variable, 1)));
return returnedVariables;
};
function padOutput(variables) {
if (!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
}, {
translateX: translateXAfterRotate
}],
opacity
};
};
const _renderChild = (child, i) => {
let style = [child.props.style, {
width,
height
}];
let element = /*#__PURE__*/React.cloneElement(child, {
i,
style
});
return /*#__PURE__*/React.createElement(Animated.View, {
style: [StyleSheet.absoluteFill, styles.child, _getTransformsFor(i)],
key: `cube-child-${i}`,
pointerEvents: currentPage === i ? 'auto' : 'none'
}, element);
};
const _closest = num => {
let array = pages;
let minDiff = 1000;
let ans;
for (let i of array) {
let m = Math.abs(num - array[i]);
if (m < minDiff) {
minDiff = m;
ans = i;
}
}
return ans;
};
return /*#__PURE__*/React.createElement(Animated.View, _extends({
style: styles.container
}, _panResponder.panHandlers), /*#__PURE__*/React.createElement(Animated.View, {
style: styles.content
}, props.children.map(_renderChild)));
});
const styles = StyleSheet.create({
container: {
position: 'absolute'
},
child: {
backgroundColor: 'transparent'
},
content: {
backgroundColor: '#000',
position: 'absolute',
width,
height
}
});
export default CubeNavigationHorizontal;
//# sourceMappingURL=CubeNavigationHorizontal.js.map