|
@@ -0,0 +1,267 @@
|
|
|
+import React, { useState, useEffect } from 'react';
|
|
|
+import {
|
|
|
+ View,
|
|
|
+ Text,
|
|
|
+ StyleSheet,
|
|
|
+ Dimensions,
|
|
|
+ PanResponder,
|
|
|
+ ScrollView,
|
|
|
+ Image,
|
|
|
+ Platform,
|
|
|
+ StatusBar
|
|
|
+} from 'react-native';
|
|
|
+
|
|
|
+import { Slider, Icon } from 'react-native-elements';
|
|
|
+import Animated, { Easing } from 'react-native-reanimated';
|
|
|
+import { State, PanGestureHandler } from 'react-native-gesture-handler';
|
|
|
+import { getStatusBarHeight } from '../utils/StatusBarHeight';
|
|
|
+import C from 'rn-class';
|
|
|
+import PlayerControls from '../components/PlayerControls';
|
|
|
+import PlayerContents from '../components/PlayerContents';
|
|
|
+
|
|
|
+const { width, height } = Dimensions.get('window');
|
|
|
+const statusBarHeight = getStatusBarHeight();
|
|
|
+const minHeight = 64;
|
|
|
+const midBound = height - 100 * 3;
|
|
|
+const upperBound = midBound + minHeight;
|
|
|
+
|
|
|
+const {
|
|
|
+ Value,
|
|
|
+ event,
|
|
|
+ Extrapolate,
|
|
|
+ Clock,
|
|
|
+ cond,
|
|
|
+ eq,
|
|
|
+ set,
|
|
|
+ add,
|
|
|
+ sub,
|
|
|
+ multiply,
|
|
|
+ lessThan,
|
|
|
+ clockRunning,
|
|
|
+ startClock,
|
|
|
+ spring,
|
|
|
+ stopClock,
|
|
|
+ interpolate,
|
|
|
+ timing,
|
|
|
+ neq
|
|
|
+} = Animated;
|
|
|
+
|
|
|
+const shadow = {
|
|
|
+ alignItems: 'center',
|
|
|
+ shadowColor: 'black',
|
|
|
+ shadowOffset: { width: 0, height: 0 },
|
|
|
+ shadowOpacity: 0.18,
|
|
|
+ shadowRadius: 2
|
|
|
+};
|
|
|
+const shadow2 = {
|
|
|
+ alignItems: 'center',
|
|
|
+ shadowColor: '#000',
|
|
|
+ shadowOffset: {
|
|
|
+ width: 0,
|
|
|
+ height: 7
|
|
|
+ },
|
|
|
+ shadowOpacity: 0.41,
|
|
|
+ shadowRadius: 9.11
|
|
|
+
|
|
|
+ //elevation: 14
|
|
|
+};
|
|
|
+
|
|
|
+function runSpring(clock, value, dest) {
|
|
|
+ const state = {
|
|
|
+ finished: new Value(0),
|
|
|
+ velocity: new Value(0),
|
|
|
+ position: new Value(0),
|
|
|
+ time: new Value(0)
|
|
|
+ };
|
|
|
+
|
|
|
+ const config = {
|
|
|
+ damping: 20,
|
|
|
+ mass: 1,
|
|
|
+ stiffness: 100,
|
|
|
+ overshootClamping: false,
|
|
|
+ restSpeedThreshold: 1,
|
|
|
+ restDisplacementThreshold: 0.5,
|
|
|
+ toValue: new Value(0)
|
|
|
+ };
|
|
|
+
|
|
|
+ return [
|
|
|
+ cond(clockRunning(clock), 0, [
|
|
|
+ set(state.finished, 0),
|
|
|
+ set(state.velocity, 0),
|
|
|
+ set(state.position, value),
|
|
|
+ set(config.toValue, dest),
|
|
|
+ startClock(clock)
|
|
|
+ ]),
|
|
|
+ spring(clock, state, config),
|
|
|
+ cond(state.finished, stopClock(clock)),
|
|
|
+ state.position
|
|
|
+ ];
|
|
|
+}
|
|
|
+
|
|
|
+const Player = props => {
|
|
|
+ const translationY = new Value(0);
|
|
|
+ const velocityY = new Value(0);
|
|
|
+ const offsetY = new Value(0);
|
|
|
+ const offsetY2 = new Value(0);
|
|
|
+ const gestureState = new Value(State.UNDETERMINED);
|
|
|
+
|
|
|
+ const onGestureEvent = event(
|
|
|
+ [
|
|
|
+ {
|
|
|
+ nativeEvent: {
|
|
|
+ translationY,
|
|
|
+ velocityY,
|
|
|
+ state: gestureState
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ { useNativeDriver: true }
|
|
|
+ );
|
|
|
+ const clockY = new Clock();
|
|
|
+ const finalTranslateY = add(translationY, multiply(0.2, velocityY));
|
|
|
+ const snapPoint = cond(
|
|
|
+ lessThan(finalTranslateY, sub(offsetY, height / 4)),
|
|
|
+ 0,
|
|
|
+ upperBound
|
|
|
+ );
|
|
|
+ const ty = cond(
|
|
|
+ eq(gestureState, State.END),
|
|
|
+ [
|
|
|
+ set(
|
|
|
+ translationY,
|
|
|
+ runSpring(clockY, add(offsetY, translationY), snapPoint)
|
|
|
+ ),
|
|
|
+ set(offsetY, translationY),
|
|
|
+ translationY
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ cond(eq(gestureState, State.BEGAN), stopClock(clockY)),
|
|
|
+ add(offsetY, translationY)
|
|
|
+ ]
|
|
|
+ );
|
|
|
+
|
|
|
+ const translateY = add(ty, offsetY2);
|
|
|
+ const opacity = interpolate(translateY, {
|
|
|
+ inputRange: [0, midBound - 100],
|
|
|
+ outputRange: [1, 0],
|
|
|
+ extrapolate: Extrapolate.CLAMP
|
|
|
+ });
|
|
|
+ const statusBarOpacity = interpolate(translateY, {
|
|
|
+ inputRange: [0, statusBarHeight],
|
|
|
+ outputRange: [1, 0],
|
|
|
+ extrapolateLeft: Extrapolate.CLAMP
|
|
|
+ });
|
|
|
+ const playContainerWidth = interpolate(translateY, {
|
|
|
+ inputRange: [0, midBound],
|
|
|
+ outputRange: [width, width - 16],
|
|
|
+ extrapolate: Extrapolate.CLAMP
|
|
|
+ });
|
|
|
+ const playerContainerHeight = interpolate(translateY, {
|
|
|
+ inputRange: [0, midBound],
|
|
|
+ outputRange: [height, 0],
|
|
|
+ extrapolate: Extrapolate.CLAMP
|
|
|
+ });
|
|
|
+ const videoWidth = interpolate(translateY, {
|
|
|
+ inputRange: [0, midBound, upperBound],
|
|
|
+ outputRange: [width, width - 16, width / 4],
|
|
|
+ extrapolate: Extrapolate.CLAMP
|
|
|
+ });
|
|
|
+ const videoHeight = interpolate(translateY, {
|
|
|
+ inputRange: [0, midBound, upperBound],
|
|
|
+ outputRange: [width / 1.78, minHeight * 1.3, minHeight],
|
|
|
+ extrapolate: Extrapolate.CLAMP
|
|
|
+ });
|
|
|
+ const playerControlOpaciy = interpolate(translateY, {
|
|
|
+ inputRange: [midBound, upperBound],
|
|
|
+ outputRange: [0, 1],
|
|
|
+ extrapolate: Extrapolate.CLAMP
|
|
|
+ });
|
|
|
+
|
|
|
+ const playerBoderRadius = interpolate(translateY, {
|
|
|
+ inputRange: [midBound, upperBound],
|
|
|
+ outputRange: [0, 15],
|
|
|
+ extrapolate: Extrapolate.CLAMP
|
|
|
+ });
|
|
|
+ const mainPlayerContainerHeight = interpolate(translateY, {
|
|
|
+ inputRange: [0, midBound, upperBound],
|
|
|
+ outputRange: [height, minHeight * 1.3, minHeight],
|
|
|
+ extrapolate: Extrapolate.CLAMP
|
|
|
+ });
|
|
|
+ useEffect(() => {
|
|
|
+ return () => {};
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ const slideUp = () => {};
|
|
|
+
|
|
|
+ return (
|
|
|
+ <PanGestureHandler
|
|
|
+ onHandlerStateChange={onGestureEvent}
|
|
|
+ activeOffsetY={[-10, 10]}
|
|
|
+ {...{ onGestureEvent }}
|
|
|
+ >
|
|
|
+ <Animated.View
|
|
|
+ style={{
|
|
|
+ ...StyleSheet.absoluteFillObject,
|
|
|
+ zIndex: 10,
|
|
|
+ marginTop: statusBarHeight,
|
|
|
+ transform: [{ translateY }],
|
|
|
+ ...shadow,
|
|
|
+ height: mainPlayerContainerHeight,
|
|
|
+ backgroundColor: 'white'
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Animated.View
|
|
|
+ style={{
|
|
|
+ padding: 5,
|
|
|
+ backgroundColor: 'white',
|
|
|
+ width: playContainerWidth,
|
|
|
+ borderRadius: playerBoderRadius
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Animated.View
|
|
|
+ style={[
|
|
|
+ Platform.OS === 'android'
|
|
|
+ ? { borderColor: '#eee' }
|
|
|
+ : { borderColor: 'white' },
|
|
|
+ {
|
|
|
+ ...StyleSheet.absoluteFillObject,
|
|
|
+ borderWidth: 1,
|
|
|
+ borderRadius: 15,
|
|
|
+ opacity: playerControlOpaciy,
|
|
|
+ backgroundColor: 'white'
|
|
|
+ },
|
|
|
+ shadow2
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <PlayerControls
|
|
|
+ {...props}
|
|
|
+ title={props.Pages.subtitle}
|
|
|
+ slideup={slideUp}
|
|
|
+ closePlayer={props.closePlayer}
|
|
|
+ />
|
|
|
+ </Animated.View>
|
|
|
+ <Animated.View style={{ width: videoWidth, height: videoHeight }}>
|
|
|
+ <Image
|
|
|
+ resizeMode="contain"
|
|
|
+ style={{ flex: 1, width: null, height: null }}
|
|
|
+ source={props.Pages.img}
|
|
|
+ />
|
|
|
+ </Animated.View>
|
|
|
+ </Animated.View>
|
|
|
+ <Animated.View
|
|
|
+ style={{
|
|
|
+ backgroundColor: 'white',
|
|
|
+ width: playContainerWidth,
|
|
|
+ height: playerContainerHeight
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Animated.View style={{ opacity }}>
|
|
|
+ <PlayerContents {...props} />
|
|
|
+ </Animated.View>
|
|
|
+ </Animated.View>
|
|
|
+ </Animated.View>
|
|
|
+ </PanGestureHandler>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default Player;
|