goodboy před 6 roky
rodič
revize
d1bdb97d2e

+ 36 - 0
src/components/PlayerContents.js

@@ -0,0 +1,36 @@
+import React, { useState } from 'react';
+import { ScrollView } from 'react-native';
+import { Icon, Slider } from 'react-native-elements';
+import C from 'rn-class';
+
+const PlayerContents = props => {
+  const { Pages } = props;
+  const [curPlayIdx, setCurPlayIdx] = useState(0);
+
+  return (
+    <ScrollView>
+      <C.Text cls="flx1 ta-c">{Pages.subtitle}</C.Text>
+      <C.View cls="flx2 ph5 mt5">
+        <C.Text cls="fw-b">{Pages.subtitle}</C.Text>
+        <C.Text cls="mt2" numberOfLines={10}>
+          {Pages.contens}
+        </C.Text>
+      </C.View>
+      <C.View cls="flx1 flx-row jc-sa ai-c mt5">
+        <C.EL.Icon type="ionicon" name="md-rewind" />
+        <C.EL.Icon type="ionicon" name="md-pause" />
+        <C.EL.Icon type="ionicon" name="md-fastforward" />
+      </C.View>
+      <C.View cls="flx1 ai-c mt2">
+        <Slider
+          style={{ width: '80%' }}
+          thumbTintColor="#333"
+          value={curPlayIdx}
+          onValueChange={value => setCurPlayIdx(value)}
+        />
+      </C.View>
+    </ScrollView>
+  );
+};
+
+export default PlayerContents;

+ 65 - 0
src/components/PlayerControls.js

@@ -0,0 +1,65 @@
+import React, { useState } from 'react';
+import {
+  View,
+  StyleSheet,
+  Text,
+  Dimensions,
+  TouchableWithoutFeedback
+} from 'react-native';
+import { Icon, Slider } from 'react-native-elements';
+import C from 'rn-class';
+
+const { width } = Dimensions.get('window');
+
+export const PLACEHOLDER_WIDTH = width / 4;
+
+const setPlayer = () => {};
+
+const PlayerControls = props => {
+  const { title, closePlayer, slideup } = props;
+  const [curPlayIdx, setCurPlayIdx] = useState(0);
+
+  return (
+    <TouchableWithoutFeedback onPress={slideup}>
+      <View style={styles.container}>
+        <View style={styles.placeholder} />
+        <C.View cls="flx2">
+          <Text numerOfLine={1}>{title}</Text>
+          <Slider
+            thumbTintColor="#333"
+            value={curPlayIdx}
+            onValueChange={value => setCurPlayIdx(value)}
+          />
+        </C.View>
+        <C.View cls="flx1 jc-sa ai-sa flx-row">
+          <C.EL.Icon type="entypo" name="controller-play" />
+          <C.EL.Icon type="entypo" name="cross" onPress={closePlayer} />
+        </C.View>
+      </View>
+    </TouchableWithoutFeedback>
+  );
+};
+
+const styles = StyleSheet.create({
+  container: {
+    ...StyleSheet.absoluteFillObject,
+    flexDirection: 'row',
+    justifyContent: 'space-around',
+    alignItems: 'center'
+  },
+  title: {
+    flex: 2,
+    flexWrap: 'wrap',
+    paddingLeft: 8
+  },
+  placeholder: {
+    width: PLACEHOLDER_WIDTH
+  },
+  icon: {
+    fontSize: 24,
+    color: 'gray',
+    padding: 8
+  }
+});
+
+export default PlayerControls;

+ 267 - 0
src/pages/PlayerGesture.js

@@ -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;

+ 2 - 1
src/pages/_5_Root.js

@@ -5,7 +5,7 @@ import Main from './_6_Main'
 import List from './_7_List'
 // import List from './_7_List' 
 import TabBar from '../components/TabBar'
-import Player from "./_8_Player"; 
+import Player from "./PlayerGesture"; 
 
 C.addColor("#d7d7d7", "color-listbg");
 
@@ -36,6 +36,7 @@ export default (props)=>{
     let onTabPress = (tIdx, i)=>{
         setsel_Idx(i);
         setTargetIdx(tIdx);
+        setbPlay(false);
     }
     let startPlay = (p)=>{
         setbPlay(true);

+ 198 - 0
src/sample/PlayerTest.js

@@ -0,0 +1,198 @@
+import React, { useState } from 'react';
+import {
+  View,
+  Text,
+  StyleSheet,
+  Dimensions,
+  Animated,
+  PanResponder,
+  ScrollView,
+  Image
+} from 'react-native';
+import C from 'rn-class';
+import { Slider, Icon} from 'react-native-elements';
+
+
+const Player = props => {
+  const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get(
+    'window'
+  );
+  console.log(SCREEN_HEIGHT, SCREEN_WIDTH);
+
+  const [isScrollEnabled, setScrollEnabled] = useState(false);
+  let animation = new Animated.ValueXY({x: 0, y: 0})
+  let panResponder = PanResponder.create({
+    onMoveShouldSetPanResponder: ()=> true,
+    onPanResponderGrant: (evt, getstureState) => {
+      animation.extractOffset()
+    },
+    onPanResponderMove: (evt, getstureState) => {
+      animation.setValue({x:0, y: getstureState.dy})
+    },
+    onPanResponderRelease: (evt, getstureState) => {
+      console.log(evt.nativeEvent)
+      if(getstureState.moveY > SCREEN_HEIGHT - 60 ) {
+        Animated.spring(animation.y, {
+          toValue: 0,
+          tension: 1
+        }).start()
+      }
+      else if(getstureState.moveY < 60) {
+        Animated.spring(animation.y, {
+          toValue: 0,
+          tension: 1
+        }).start()
+      }
+      else if(getstureState.dy < 0) {
+        Animated.spring(animation.y, {
+          toValue: -SCREEN_HEIGHT + 60,
+          tension: 1
+        }).start()
+      }
+      else if(getstureState.dy>0) {
+        Animated.spring(animation.y, {
+          toValue: SCREEN_HEIGHT - 60,
+          tension: 1
+        }).start()
+      }
+    }
+  })
+
+
+
+  const animatedHeight = {
+    transform: animation.getTranslateTransform()
+  }
+
+  const animatedImageHeight = animation.y.interpolate({
+    inputRange: [0, SCREEN_HEIGHT - 60],
+    outputRange: [200, 32],
+    extrapolate: 'clamp'
+  })
+
+  const animatedTitleOpacity =  animation.y.interpolate({
+    inputRange: [0, SCREEN_HEIGHT - 500, SCREEN_HEIGHT - 60],
+    outputRange: [0, 0, 1],
+    extrapolate: 'clamp'
+  })
+
+  const animatedImageMarginLeft = animation.y.interpolate({
+    inputRange: [0, SCREEN_HEIGHT - 60],
+    outputRange: [SCREEN_WIDTH/2-80, 0],
+    extrapolate: 'clamp'
+  })
+
+  const animatedImageFlex = animation.y.interpolate({
+    inputRange: [0, SCREEN_HEIGHT - 60],
+    outputRange: [4, 1],
+    extrapolate: 'clamp'
+  })
+
+  const animatedHeaderHeight = animation.y.interpolate({
+    inputRange: [0, SCREEN_HEIGHT - 60],
+    outputRange: [SCREEN_HEIGHT /2 + 100, 60],
+    extrapolate: 'clamp'
+  })
+
+  const animatedBigPlayContentHeight = animation.y.interpolate({
+    inputRange: [0, SCREEN_HEIGHT - 60],
+    outputRange: [SCREEN_HEIGHT /2, 60],
+    extrapolate: 'clamp'
+  }) 
+
+  const animatedBigPlayContentOpacity = animation.y.interpolate({
+    inputRange: [0, SCREEN_HEIGHT - 500, SCREEN_HEIGHT - 60],
+    outputRange: [1, 0, 0],
+    extrapolate: 'clamp'
+  })  
+
+  const animatedPlayerBottom = animation.y.interpolate({
+    inputRange: [0, SCREEN_HEIGHT - 500],
+    outputRange: [0, 60],
+    extrapolate: 'clamp'
+  })
+  console.log(props)
+  
+  
+  return (
+      // <Animated.View style={s.container}>
+        <Animated.View 
+        {...panResponder.panHandlers}
+        style={[animatedHeight,s.playerContainer,{height:SCREEN_HEIGHT}]}>
+
+          <Animated.View 
+          style={{height:animatedHeaderHeight, alignItems:'center', borderTopWidth:1, flexDirection:'row', borderTopColor: '#eee'}}>           
+            
+            <Animated.View style={{ flex:animatedImageFlex, height: animatedImageHeight, widht: animatedImageHeight, marginLeft: animatedImageMarginLeft}}>
+              <Image resizeMode='contain' style={{width:null, height:null, flex:4}} source={props.Pages.img} />
+              <Animated.Text style={{opacity:animatedBigPlayContentOpacity, flex:1}}>{props.Pages.subtitle}</Animated.Text>
+            </Animated.View>
+            <View style={{flex:3}}>
+              <Animated.Text numberOfLines={1} style={{ opacity:animatedTitleOpacity}}>
+                {props.Pages.subtitle}  
+              </Animated.Text>
+            </View>
+            <Animated.View style={{opacity:animatedTitleOpacity, flex:2, flexDirection: 'row', justifyContent: 'space-around'}}>
+              <Icon type='ionicon' name='md-pause' />
+              <Icon type='ionicon' name='md-play' />
+              <Icon type="ionicon" name="md-close"  onPress={props.closePlayer}/>  
+            </Animated.View>
+
+          </Animated.View>
+
+          <Animated.View style={{height:animatedBigPlayContentHeight, opacity:animatedBigPlayContentOpacity}}>
+            
+            <View style={{flex:1, alignItems:'center', justifyContent:'flex-end'}}>
+              <Text style={{fontSize:18, fontWeight: 'bold'}}>{props.Pages.title}</Text>
+              <Text style={{marginTop:10}}>{props.Pages.contens}</Text>
+            </View>
+            
+            <View style={{flex:1, flexDirection:'row', alignItems: 'center', justifyContent: 'space-around'}}>
+              <Icon type='ionicon' name='md-rewind'></Icon>
+              <Icon type='ionicon' name='md-pause'></Icon>
+              <Icon type='ionicon' name='md-fastforward'></Icon>
+            </View>
+
+            <View style={{flex:1, width: SCREEN_WIDTH, alignItems: 'center'}}>
+              <Slider style={{width:300}} />
+            </View>
+
+          </Animated.View>
+
+        </Animated.View>
+      // </Animated.View>
+    );
+}
+
+const s = StyleSheet.create({
+  container: {
+    ...StyleSheet.absoluteFill,
+    backgroundColor: 'skyblue',
+    zIndex: 10
+  },
+  playerContainer: {
+    position: 'absolute',
+    left: 0,
+    right: 0,
+    bottom: 0,
+    zIndex: 20,
+    backgroundColor: 'orange',
+    
+
+  },
+  playerContentContainer: {
+    height: 80,
+    borderTopWidth: 1,
+    borderTopColor: '#ebe5e5',
+    flexDirection: 'row',
+    alignItems: 'center',
+    
+  },
+  example: {
+    alignContent: 'center',
+    justifyContent: 'center'
+  }
+
+});
+
+export default Player;

+ 23 - 0
src/utils/StatusBarHeight.js

@@ -0,0 +1,23 @@
+import { Dimensions, Platform, StatusBar } from 'react-native';
+
+const X_WIDTH = 375;
+const X_HEIGHT = 812;
+
+const XSMAX_WIDTH = 414;
+const XSMAX_HEIGHT = 896;
+
+const { height: W_HEIGHT, width: W_WIDTH } = Dimensions.get('window');
+
+let isIPhoneX = false;
+
+if (Platform.OS === 'ios' && !Platform.isPad && !Platform.isTVOS) {
+    isIPhoneX = W_WIDTH === X_WIDTH && W_HEIGHT === X_HEIGHT || W_WIDTH === XSMAX_WIDTH && W_HEIGHT === XSMAX_HEIGHT;
+}
+
+export function getStatusBarHeight(skipAndroid) {
+    return Platform.select({
+        ios: isIPhoneX ? 44 : 20,
+        android: skipAndroid ? 0 : StatusBar.currentHeight,
+        default: 0
+    })
+}