UNPKG

react-native-page-flipper

Version:
300 lines (277 loc) 8.77 kB
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, { useCallback, useEffect, useRef, useState } from 'react'; import { Pressable, StyleSheet, View } from 'react-native'; import { PanGestureHandler } from 'react-native-gesture-handler'; import Animated, { Easing, Extrapolate, interpolate, runOnJS, useAnimatedGestureHandler, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming } from 'react-native-reanimated'; import BackShadow from '../BookPage/BackShadow'; import FrontShadow from '../BookPage/FrontShadow'; import PageShadow from '../BookPage/PageShadow'; import { clamp, snapPoint } from '../utils/utils'; const timingConfig = { duration: 800, easing: Easing.inOut(Easing.cubic) }; const BookPagePortrait = /*#__PURE__*/React.forwardRef((_ref, ref) => { let { current, prev, onPageFlip, containerSize, enabled, isPressable, setIsAnimating, getPageStyle, isAnimating, isAnimatingRef, next, onFlipStart, onPageDrag, onPageDragEnd, onPageDragStart, renderPage } = _ref; const containerWidth = containerSize.width; const pSnapPoints = !prev ? [-containerSize.width, 0] : [-containerSize.width, 0, containerSize.width]; const x = useSharedValue(0); const isMounted = useRef(false); const rotateYAsDeg = useSharedValue(0); // might not need this // useEffect(() => { // if (!enabled) { // setIsDragging(false); // } // }, [enabled]); const turnPage = useCallback(id => { setIsAnimating(true); if (onFlipStart && typeof onFlipStart === 'function') { onFlipStart(id); } rotateYAsDeg.value = withTiming(id < 0 ? -180 : 180, timingConfig, () => { runOnJS(onPageFlip)(id, false); }); }, [onFlipStart, onPageFlip, rotateYAsDeg, setIsAnimating]); React.useImperativeHandle(ref, () => ({ turnPage }), [turnPage]); useEffect(() => { isMounted.current = true; return () => { isMounted.current = false; }; }, []); const getDegreesForX = x => { 'worklet'; const val = interpolate(x, [-containerSize.width, 0, containerSize.width], [180, 0, -180], Extrapolate.CLAMP); return val; }; const containerStyle = useAnimatedStyle(() => { return { flex: 1 }; }); const onPanGestureHandler = useAnimatedGestureHandler({ // @ts-ignore onStart: (event, ctx) => { ctx.x = x.value; if (onPageDragStart && typeof onPageDragStart === 'function') { runOnJS(onPageDragStart)(); } }, onActive: (event, ctx) => { const newX = ctx.x + event.translationX; const degrees = getDegreesForX(newX); x.value = newX; rotateYAsDeg.value = degrees; if (onPageDrag && typeof onPageDrag === 'function') { runOnJS(onPageDrag)(); } }, onEnd: event => { if (onPageDragEnd && typeof onPageDragEnd === 'function') { runOnJS(onPageDragEnd)(); } const snapTo = snapPoint(x.value, event.velocityX, pSnapPoints); const id = snapTo > 0 ? -1 : snapTo < 0 ? 1 : 0; if (!next && id > 0) { // reset x.value = withTiming(0); rotateYAsDeg.value = withTiming(0); // runOnJS(onDrag)(false); return; } const degrees = getDegreesForX(snapTo); x.value = snapTo; if (rotateYAsDeg.value === degrees) { // already same value // debugValue('already there'); runOnJS(onPageFlip)(id, false); } else { runOnJS(setIsAnimating)(true); const progress = Math.abs(rotateYAsDeg.value - degrees) / 100; const duration = clamp(800 * progress - Math.abs(0.1 * event.velocityX), 350, 1000); rotateYAsDeg.value = withTiming(degrees, { ...timingConfig, duration: duration }, () => { if (snapTo === 0) {// } runOnJS(onPageFlip)(id, false); }); } } }); const gesturesEnabled = enabled && !isAnimating; const iPageProps = { containerSize, containerWidth, getPageStyle, rotateYAsDeg, renderPage }; return /*#__PURE__*/React.createElement(Animated.View, { style: containerStyle }, /*#__PURE__*/React.createElement(PanGestureHandler, { onGestureEvent: onPanGestureHandler, enabled: gesturesEnabled }, /*#__PURE__*/React.createElement(Animated.View, { style: containerStyle }, isPressable && prev && /*#__PURE__*/React.createElement(Pressable, { disabled: isAnimating, onPress: () => { if (!isAnimatingRef.current) turnPage(-1); }, style: { position: 'absolute', height: '100%', width: '25%', zIndex: 10000, left: 0 // backgroundColor: 'red', // opacity: 0.2, } }), isPressable && next && /*#__PURE__*/React.createElement(Pressable, { disabled: isAnimating, onPress: () => { if (!isAnimatingRef.current) turnPage(1); }, style: { position: 'absolute', height: '100%', width: '30%', zIndex: 10000, right: 0 // backgroundColor: 'blue', // opacity: 0.2, } }), current && next ? /*#__PURE__*/React.createElement(IPage, _extends({ page: current, right: true }, iPageProps)) : /*#__PURE__*/React.createElement(View, { style: { height: '100%', width: '100%' } }, renderPage && /*#__PURE__*/React.createElement(View, { style: getPageStyle(true, true) }, renderPage(current.right))), prev && /*#__PURE__*/React.createElement(IPage, _extends({ page: prev, right: false }, iPageProps))))); }); const IPage = _ref2 => { let { right, page, rotateYAsDeg, containerWidth, containerSize, getPageStyle, renderPage } = _ref2; const [loaded, setLoaded] = useState(right); useEffect(() => { // hack fix setTimeout(() => { setLoaded(true); }, 50); }, []); const rotationVal = useDerivedValue(() => { const val = right ? rotateYAsDeg.value : interpolate(rotateYAsDeg.value, [-180, 0], [0, 180]); return val; }); const portraitBackStyle = useAnimatedStyle(() => { const x = interpolate(rotationVal.value, [0, 180], [containerWidth, -containerWidth / 2]); const w = interpolate(rotationVal.value, [0, 180], [0, containerWidth / 2]); return { width: Math.ceil(w), zIndex: 2, opacity: 1, transform: [{ translateX: x }] }; }); const portraitFrontStyle = useAnimatedStyle(() => { const w = interpolate(rotationVal.value, [0, 160], [containerWidth, -20], Extrapolate.CLAMP); const style = { zIndex: 1, width: Math.floor(w) }; if (!right) { style['left'] = 0; } else {// style['right'] = 0; } return style; }); const frontPageStyle = getPageStyle(right, true); const backPageStyle = getPageStyle(right, false); if (!loaded) { // hack fix return null; } const shadowProps = { right: true, degrees: rotationVal, width: containerSize.width, viewHeight: containerSize.height }; return /*#__PURE__*/React.createElement(View, { style: { ...StyleSheet.absoluteFillObject, zIndex: !right ? 5 : 0 } }, /*#__PURE__*/React.createElement(Animated.View, { style: [styles.pageContainer, portraitBackStyle, { overflow: 'visible' }] }, /*#__PURE__*/React.createElement(View, { style: styles.pageContainer }, renderPage && /*#__PURE__*/React.createElement(Animated.View, { style: [backPageStyle, { opacity: 0.2, transform: [{ rotateX: '180deg' }, { rotateZ: '180deg' }] }] }, renderPage(page.left))), /*#__PURE__*/React.createElement(BackShadow, { degrees: rotationVal, right: true }), /*#__PURE__*/React.createElement(FrontShadow, shadowProps), /*#__PURE__*/React.createElement(PageShadow, _extends({}, shadowProps, { containerSize: containerSize }))), /*#__PURE__*/React.createElement(Animated.View, { style: [styles.pageContainer, portraitFrontStyle] }, renderPage && /*#__PURE__*/React.createElement(Animated.View, { style: [frontPageStyle] }, renderPage(page.left)))); }; export { BookPagePortrait }; const styles = StyleSheet.create({ container: { flex: 1 }, pageContainer: { height: '100%', width: '100%', position: 'absolute', backfaceVisibility: 'hidden', overflow: 'hidden', backgroundColor: 'white' } }); //# sourceMappingURL=BookPagePortrait.js.map