here is my code.
const actionButtonsAnimated = new Animated.Value(0);
const animated = new Animated.Value(255);
const animateTrendingCardSheet = () => {
Animated.timing(animated, {
toValue: 0,
duration: 1500,
useNativeDriver: true,
}).start();
Animated.timing(actionButtonsAnimated, {
toValue: -180,
duration: 1000,
useNativeDriver: true,
}).start();
};
<Animated.View
style={[
{
transform: [{ translateY: actionButtonsAnimated }],
},
]}
>
<MainActionButtons />
</Animated.View>
<Animated.View
style={[
{
transform: [{ translateY: animated }],
width: "100%",
position: "absolute",
bottom: 0,
},
]}
>
<View style={styles.trendingCards}>
<Text h5 center color={colors.trendingText}>
Trending in your area...
</Text>
<View style={styles.flatlistWrapper}>
<FlatList
horizontal={true}
data={trendingCards}
renderItem={({ item }) => <TrendingCardComponent card={item} />}
/>
</View>
</View>
</Animated.View>
so if i call the animateTrendingCardSheet function inside useEffect like this.
useEffect(() => {
animateTrendingCardSheet()
}, [])
it works as expected but once i put it in a condition that it should be called after the API call has been finished it does not work at all if i again save the file it hot reload animation works
useEffect(() => {
if (loadTrendingCard) {
animateTrendingCardSheet();
}
}, [loadTrendingCard]);
Your issue is that after the first call to animateTrendingCardSheet the toValue that you are animating to is the current value of your Animated variables; so it looks like nothing is happening. You can counteract this by resetting your animation variables before calling your animation function:
import * as React from 'react';
import {
Text,
View,
StyleSheet,
Animated,
TouchableOpacity,
FlatList,
Button
} from 'react-native';
import Constants from 'expo-constants';
const MainActionButtons = () => {
return (
<View
style={{
flexDirection: 'row',
width: '100%',
justifyContent: 'space-between',
}}>
<TouchableOpacity>Btn 1</TouchableOpacity>
<TouchableOpacity>Btn 2</TouchableOpacity>
<TouchableOpacity>Btn 3</TouchableOpacity>
<TouchableOpacity>Btn 4</TouchableOpacity>
</View>
);
};
const TrendingCardComponent = ({ card }) => {
return (
<View style={{ width: '100%' }}>
<Text>{card.title}</Text>
<Text>{card.message}</Text>
</View>
);
};
const trendingCards = [
{ title: 'A Cool Card', message: 'A cool message' },
{ title: 'Card 1', message: 'A cool message' },
{ title: 'A Cool Card', message: 'A cool message' },
{ title: 'A Cool Card', message: 'A cool message' },
];
const initialActionButton = 0;
const initialAnimated = 255;
export default function App() {
const actionButtonsAnimated = new Animated.Value(initialActionButton);
const animated = new Animated.Value(initialAnimated);
const opacity = new Animated.Value(1)
const onApiCall = ()=>{
// set opacity to 0
Animated.timing(opacity,{toValue:0,duration:500}).start(()=>{
// when view is invisible do resets
animated.setValue(initialAnimated)
actionButtonsAnimated.setValue(initialActionButton)
Animated.timing(opacity,{toValue:1,duration:500}).start()
animateTrendingCardSheet()
})
}
const animateTrendingCardSheet = () => {
Animated.timing(animated, {
toValue: 0,
duration: 1500,
useNativeDriver: true,
}).start();
Animated.timing(actionButtonsAnimated, {
toValue: -180 ,
duration: 1000,
useNativeDriver: true,
}).start();
};
// React.useEffect(() => {
// animateTrendingCardSheet();
// }, []);
return (
<View style={styles.container}>
<Button title="Simulate API call" onPress={onApiCall}/>
<Animated.View
style={[
{
transform: [{ translateY: actionButtonsAnimated }],
opacity
},
]}>
<MainActionButtons />
</Animated.View>
<Animated.View
style={[
{
transform: [{ translateY: animated }],
width: '100%',
position: 'absolute',
bottom: 0,
opacity
},
]}>
<View style={styles.trendingCards}>
<Text>Trending in your area...</Text>
<View style={styles.flatlistWrapper}>
<FlatList
style={{ flex: 1 }}
horizontal={true}
data={trendingCards}
renderItem={({ item }) => <TrendingCardComponent card={item} />}
/>
</View>
</View>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
flatlistWrapper: {
width: '100%',
height: 200,
},
});
Demo
The above answer is good but it does not solve my problem, its all due to a stupid mistake, the issue was i was setting the state before calling the animation call function, as the state changes and it interfere the animation i had to use useRef like this.
const actionButtonsAnimated = useRef(new Animated.Value(initialActionButton)).current;
const animated = useRef(new Animated.Value(initialAnimated)).current;
it reference the state and animation works as expected.
Related
I'm having trouble getting an AnimatedTextInput to work properly where a normal TextInput works properly. When I press the TextInput to start typing, there are no problems, and I can start typing immediately. However, on an AnimatedTextInput, the software keyboard closes immediately.
So, this works just fine:
return (
<View>
<TextInput style={[style, styles.input, { paddingTop: 20, paddingBottom: 20 }]} {...restOfProps} onFocus={() => handleFocus()} onBlur={handleBlur} ref={refInput} />
<Animated.View style={[styles.labelContainer, { opacity: opacity}]}>
<Text style={styles.label}>{label}</Text>
</Animated.View>
</View>
)
However this:
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
return (
<View>
<AnimatedTextInput style={[style, styles.input, { paddingTop: paddingTop, paddingBottom: paddingBottom }]} {...restOfProps} onFocus={() => handleFocus()} onBlur={handleBlur} ref={refInput} />
<Animated.View style={[styles.labelContainer, { opacity: opacity}]}>
<Text style={styles.label}>{label}</Text>
</Animated.View>
</View>
)
Immediately causes the software keyboard to quickly open then close when touching the input to gain focus/to start typing. On searching, answers I've seen say this is related to being part of a scrollview, but this happens whether in a scrollview or not.
For reference, here's the entire file:
import React, { useRef } from 'react';
import { Animated, StyleSheet, Text, TextInput, View } from 'react-native';
const styles = StyleSheet.create({
input: {
padding: 24,
borderColor: 'transparent',
borderWidth: 1,
borderRadius: 4,
fontFamily: 'Avenir-Medium',
fontSize: 16,
backgroundColor: '#F8F8F8',
},
labelContainer: {
position: 'absolute',
left: 16,
top: 9,
paddingHorizontal: 8,
},
label: {
color: '#ABB4BD',
fontFamily: 'Avenir-Heavy',
fontSize: 12,
},
})
// extend from native TextInput props
const TextField = (props) => {
const { label, style, ...restOfProps } = props;
const [isFocused, setIsFocused] = React.useState(false);
const opacity = useRef(new Animated.Value(0)).current;
const paddingTop = useRef(new Animated.Value(20)).current;
const paddingBottom = useRef(new Animated.Value(20)).current;
const refInput = useRef();
React.useEffect(() => {
Animated.timing(
paddingBottom,
{
useNativeDriver: false,
toValue: isFocused ? 10 : 20,
duration: 200,
}
).start();
Animated.timing(
paddingTop,
{
useNativeDriver: false,
toValue: isFocused ? 30 : 20,
duration: 200,
}
).start();
Animated.timing(
opacity,
{
useNativeDriver: false,
toValue: isFocused ? 1 : 0,
duration: 200,
}
).start();
}, [isFocused])
const handleFocus = (input) => {
console.log('here')
setIsFocused(true);
}
const handleBlur = () => {
console.log('there')
setIsFocused(false);
}
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
return (
<View>
<AnimatedTextInput style={[style, styles.input, { paddingTop: paddingTop, paddingBottom: paddingBottom }]} {...restOfProps} onFocus={() => handleFocus()} onBlur={handleBlur} ref={refInput} />
<Animated.View style={[styles.labelContainer, { opacity: opacity}]}>
<Text style={styles.label}>{label}</Text>
</Animated.View>
</View>
)
};
export default TextField;
I'm working on React Native.
Actually I want to draw a line between two points in react native.
The point is where I start touch and where I release touch.
I'm doing this using penResponder.
Using penResponder I get those point. Where i start touch and where I release touch.
Here is my code:
import React, {Component} from 'react';
import {
StyleSheet,
View,
Platform,
Text,
PanResponder,
Image,
} from 'react-native';
export default class App extends Component {
constructor() {
super();
//initialize state
this.panResponder;
this.state = {
locationX: 0,
locationY: 0,
locationSX: 0,
locationSY: 0,
};
//panResponder initialization
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: (event, gestureState) => true,
onStartShouldSetPanResponderCapture: (event, gestureState) => {
this.setState({
locationX: event.nativeEvent.locationX.toFixed(2),
locationY: event.nativeEvent.locationY.toFixed(2),
});
},
onMoveShouldSetPanResponder: (event, gestureState) => false,
onMoveShouldSetPanResponderCapture: (event, gestureState) => false,
onPanResponderGrant: (event, gestureState) => false,
onPanResponderMove: (event, gestureState) => {},
onPanResponderRelease: (event, gestureState) => {
this.setState({
locationSX: event.nativeEvent.locationX.toFixed(2),
locationSY: event.nativeEvent.locationY.toFixed(2),
});
},
});
this.setState({
locationX: 0,
locationY: 0,
locationSX: 0,
locationSY: 0,
});
}
render() {
return (
<View style={styles.MainContainer}>
<View style={styles.childView}>
<View
style={[
{
height: 22,
width: 22,
marginTop: 5,
position: 'absolute',
borderRadius: 14,
backgroundColor: '#afeeee',
},
{
top: parseFloat(this.state.locationY - 5),
left: parseFloat(this.state.locationX - 15),
},
]}
/>
<View
style={[
{
height: 22,
width: 22,
marginTop: 5,
position: 'absolute',
borderRadius: 14,
backgroundColor: '#afeeee',
},
{
top: parseFloat(this.state.locationSY - 2),
left: parseFloat(this.state.locationSX - 11),
},
]}
/>
<View
style={{flex: 1, backgroundColor: 'transparent'}}
{...this.panResponder.panHandlers}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer: {
flex: 1,
},
childView: {
flex: 1,
overflow: 'hidden',
},
point: {
height: 22,
width: 22,
marginTop: 5,
position: 'absolute',
borderRadius: 14,
backgroundColor: '#afeeee',
},
});
But how to draw line between the two points?
Actually I want this:
Please help!
Thanks in advance.
You can use svg (https://github.com/react-native-community/react-native-svg) to do this. I recommend you to put your PanResponder on top of your svg to handle touches.
Svg example:
<Svg height={windowHeight} width={windowWidth}>
<Line x1={startTouch.x} y1={startTouch.y} x2={endTouch.x} y2={endTouch.y} stroke="red" strokeWidth="2" />
</Svg>
I've implemented an app based on react native. This app shows me a list via swipelistview. But I have a Problem. when list add new items after scroll down, swipelistview won't work properly. In fact it works by Accident. I don't know where i've made mistake.
this is my code:
import React , {Component} from 'react';
import { StyleSheet, Text, View, FlatList ,ActivityIndicator,Animated , TouchableOpacity, Alert } from 'react-native';
import { SwipeListView } from 'react-native-swipe-list-view';
import { Container, Header, Content, Accordion ,Icon ,Item, Input , Button ,ActionSheet , Card, CardItem, Body} from "native-base";
var DESTRUCTIVE_INDEX = 3;
var CANCEL_INDEX = 4;
class App extends Component {
constructor(props) {
super(props);
this.actionSheet = null;
}
state = {
data: [],
filteredData: [],
page: 1,
loading: false,
error: null,
spinner: false,
searchText:'',
lety:false,
refreshing: false
};
componentDidMount() {
console.disableYellowBox = true;
this.fetchData();
}
handleLoadMore = () => {
this.setState(
{
page: this.state.page + 1
},
() => {
this.fetchData();
}
);
};
fetchData = () => {
const page = this.state.page;
this.setState({ loading: true });
let response = fetch(
'http://webgispro.ir/home/GetWords/' + page ,
{
method: "POST"
}
).then((response) => response.json())
.then((responseJson) => {
this.setState(
{
data : page === 1 ? Array.from(responseJson): [...this.state.data, ...responseJson] ,
loading: false,
refreshing: false
})
})
.catch((error) => {
this.setState({ error, loading: false });
});
};
searchData = (searchText) => {
this.setState({searchText: searchText.toLowerCase()});
if (searchText =='') {
this.setState({filteredData: []});
this.setState({lety:false})
return false;
}
let filteredData = this.state.data.filter(function (item) {
return item.Word.toLowerCase().includes(searchText.toLowerCase());
});
this.setState({lety : filteredData.length == 0 ? true : false})
this.setState({filteredData: filteredData});
};
renderHeader = () => {
return <Searchbar
style={{ width:'100%' }}
inputStyle={{borderWidth: 0}}
round={false}
lightTheme={true}
placeholder="Search..."
autoCapitalize='none'
autoCorrect={false}
onChangeText={this.searchData}
value={this.state.searchText}
/>
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating ={true} color="blue" style={{height: 80, marginTop: 10, opacity: 1 }} size="large" />
</View>
);
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#CED0CE",
//marginLeft: "14%"
}}
/>
);
};
handleRefresh = () => {
this.rowHeightAnimatedValues = {};
this.setState(
{
page: 1,
refreshing: true
},
() => {
this.fetchData();
}
);
};
editItem = key => () => {
var example = this.state.data.find(o=>o.Id == key).Example
if ( this.actionSheet !== null ) {
const title = <Text style={{ color: 'black', fontSize: 23 }}>{example}</Text>
this.actionSheet._root.showActionSheet(
{
options: [] ,
cancelButtonIndex: CANCEL_INDEX,
destructiveButtonIndex: DESTRUCTIVE_INDEX,
title:title
}, (i) => console.log(i));
}
}
deleteItem = key => () => {
Alert.alert(
"???",
"??? ???? ??? ????? ????? ??",
[
{
text: "???",
onPress: () => console.log("Cancel Pressed"),
style: "cancel"
},
{ text: "???", onPress: () => console.log("OK Pressed") }
],
{ cancelable: false }
);
}
onRowOpen =() => {
console.log('cfdfdf')
}
render() {
return (
<View style={styles.container}>
<Header searchBar rounded>
<Item>
<Icon name="ios-search" />
<Input placeholder="Search" onChangeText={this.searchData} />
<Icon name="ios-people" />
</Item>
<Button transparent>
<Text>Search</Text>
</Button>
</Header>
<SwipeListView
useFlatList
extraData={this.state}
data={this.state.filteredData && this.state.filteredData.length > 0 || this.state.lety == true ? this.state.filteredData : this.state.data}
keyExtractor={item => item.Id.toString()}
renderItem={(data, rowMap) => (
<Animated.View
style={[
styles.rowFront,
data.index % 2 === 0 && { backgroundColor: 'white' },
{ height: 100 }
]}
>
<Text style={{position: 'absolute', top: 35 , fontSize:20}}>
{data.item.Word}
</Text>
</Animated.View>
)}
renderHiddenItem={ (data, rowMap) => (
<View style={styles.rowBack}>
<Text style={{fontSize:20}}>{data.item.Meaning}</Text>
<TouchableOpacity style={[styles.backRightBtn, styles.backRightBtnLeft]} onPress={this.editItem(data.item.Id)}>
<Icon name='md-information-circle-outline' />
</TouchableOpacity>
<TouchableOpacity style={[styles.backRightBtn, styles.backRightBtnRight]} onPress={this.deleteItem(data.item.Id)}>
<Icon name='trash-outline' />
</TouchableOpacity>
</View>
)}
leftOpenValue={90}
rightOpenValue={-150}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
initialNumToRender={8}
ItemSeparatorComponent={this.renderSeparator}
ListFooterComponent={this.renderFooter}
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
/>
<ActionSheet ref={(c) => { this.actionSheet = c; }} />
</View>
);
}
}
export default App
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
flex: 1,
//paddingTop: 50,
},
backTextWhite: {
color: '#FFF'
},
rowFront: {
alignItems: 'center',
backgroundColor: '#CCC',
justifyContent: 'center',
},
rowBack: {
alignItems: 'center',
backgroundColor: 'green',
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
paddingLeft: 15,
},
backRightBtn: {
alignItems: 'center',
bottom: 0,
justifyContent: 'center',
position: 'absolute',
top: 0,
width: 75
},
backRightBtnLeft: {
backgroundColor: 'blue',
right: 75
},
backRightBtnRight: {
backgroundColor: 'red',
right: 0
},
});
The list works properly and comes data but whenever I scroll down or up it doesn't work anymore. Please help me
If you are in mindset to change your package, then you can try react-native-snap-carousel, perfect for your requirement. Thanks
I would like to blur part of an Image wherever user drags his finger, I don't want to blur the whole image. Here is my hacky way of doing blur
I am using #react-native-community/blur for blurring the view
const MyView = () => {
const [locations, setLocation] = useState([]);
const [top, setTop] = useState();
const [left, setLeft] = useState();
return (
<>
<View
style={styles.container}
onTouchEnd={(event) => {
console.log(event.nativeEvent.locationX);
console.log(event.nativeEvent.locationY);
setLocation([
...locations,
{
right: event.nativeEvent.locationX,
bottom: event.nativeEvent.locationY,
top,
left,
},
]);
}}
onTouchStart={(event) => {
console.log('S' + event.nativeEvent.locationX);
console.log('S' + event.nativeEvent.locationY);
setLeft(event.nativeEvent.locationX);
setTop(event.nativeEvent.locationY);
}}>
<ImageBackground
style={{width, height}}
source={{
uri:
'https://images.unsplash.com/photo-1593642634315-48f5414c3ad9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80',
}}>
<Text>Hi there </Text>
</ImageBackground>
</View>
{locations.map((l, index) => {
return (
<BlurView
key={index.toString()}
style={{
width: l.right - l.left,
height: l.bottom - l.top,
backgroundColor: 'rgba(0,0,0,0.1)',
position: 'absolute',
top: parseFloat(l.top),
left: parseFloat(l.left),
right: parseFloat(l.right),
bottom: parseFloat(l.bottom),
// blurR,
// opacity: 0.4,
}}
blurType="light"
blurAmount={4}
reducedTransparencyFallbackColor="white"
/>
);
})}
</>);
};
This is a really hacky way of smudging part of an image where user drags his finger. On Android , the application just stops responding as soon as I move my finger on more than two places. Does anyone know a better way to smudge part of view instead of blurring the whole image view where user drags his finger
You can use PanResponder. I developed an example application for you. There is no blurview on expo snack but i guess you can easily convert that sample to what you want. Here is example code:
import React, { useState, useRef } from 'react'
import { StyleSheet, View, ImageBackground, PanResponder, Animated } from 'react-native'
const CIRCLE_RADIUS = 60
const IAmTheBlurComponent = () => {
return (
<View style={[StyleSheet.absoluteFill, { backgroundColor: 'pink', opacity: 0.5 }]}></View>
)
}
const Component = () => {
const [pointers, setPointers] = useState([])
const refs: any = useRef({}).current;
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderStart: (evt) => {
setPointers(evt.nativeEvent.touches.map(touch => touch.identifier))
evt.nativeEvent.touches.forEach((event) => {
refs[event.identifier] = { x: new Animated.Value(event.pageX), y: new Animated.Value(event.pageY) }
})
},
onPanResponderMove: (evt) => {
evt.nativeEvent.touches.forEach((event) => {
refs[event.identifier].x.setValue(event.pageX)
refs[event.identifier].y.setValue(event.pageY)
})
},
onPanResponderEnd: (evt) => {
setPointers(evt.nativeEvent.touches.map(touch => touch.identifier))
},
})
).current
return (
<ImageBackground
style={{
flex: 1,
position: 'relative'
}}
source={{
uri:
'https://images.unsplash.com/photo-1593642634315-48f5414c3ad9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80',
}}
{...panResponder.panHandlers}
>
{pointers.map((pnt) => {
return (
<Animated.View
key={pnt}
style={{
overflow: 'hidden',
height: CIRCLE_RADIUS * 2,
width: CIRCLE_RADIUS * 2,
borderRadius: CIRCLE_RADIUS,
position: 'absolute',
transform: [
{
translateX: Animated.subtract(refs[pnt].x, CIRCLE_RADIUS)
},
{
translateY: Animated.subtract(refs[pnt].y, CIRCLE_RADIUS)
}
]
}}
>
<IAmTheBlurComponent />
</Animated.View>
)
})}
</ImageBackground>
)
}
export default function App() {
return (
<View style={styles.container}>
<Component />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
})
For smooth animations, you can chose Reanimated library. I didnt use ValueXY or similar things, so you can easily convert that sample to reanimated.
This is the sample expo snack: https://snack.expo.io/#kubilaysali/suspicious-ice-cream
I am creating an application for android and ios in react-native(0.57.7) and using react-native-video to play videos uploaded into vimeo. After integrate react video plugin, tested in both device. In ios it works perfectly but in android, I am not able to play video in full-screen mode. Here is my code for Android:
import React, { PureComponent } from 'react';
import {
View,
Text,
Image,
ImageBackground,
StyleSheet,
SafeAreaView,
TouchableOpacity,
ActivityIndicator
} from 'react-native';
import PropTypes from 'prop-types'
import Video from "react-native-video";
import Orientation from 'react-native-orientation-locker';
import { widthPercentageToDP as wp, heightPercentageToDP as hp } from '../components/Resposive';
import RF from "react-native-responsive-fontsize"
export default class Videoplayer extends PureComponent {
constructor(props){
super(props);
this.state = {
loading: true,
videoFileArr:[],
showPlayer:false,
playing:false
}
}
componentDidMount() {
Orientation.lockToLandscape();
this.fetchVimeoVideo();
}
componentWillUnmount() {
Orientation.lockToPortrait();
}
goToHome(){
Orientation.lockToPortrait();
this.props.navigation.goBack();
}
fetchVimeoVideo = async () => {
await this.setState({ loading: true });
const { navigation } = this.props;
const jsonReceived = navigation.getParam('item', {})
const url = "https://api.vimeo.com/me/videos/" + jsonReceived.video_link
console.log(url)
const response = await fetch(
url, {
method: "get",
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization:"Bearer f9e937d64037e657addcf088f28e6cb5"
}
});
const jsonResponse = await response.json();
const { files} = jsonResponse;
if (response.status != 200) {
alert(response.status)
}
console.log(files)
await this.setState({ videoFileArr:files, loading: false });
};
renderOptions = () => {
if (this.state.loading === true) {
return (
<View style={{
flex: 1,
alignItems: "center",
justifyContent: "center"
}}>
<ActivityIndicator size="large" color="#00ff00" />
<Text style={{ fontFamily: "Futura Std", fontSize: RF(3.0), fontWeight: "900", color: "#244E25", textAlign: "center" }}>Please wait while we are loading questions for you</Text>
</View>
)
}else if (this.state.videoFileArr.length> 0 && this.state.playing === false) {
const { navigation } = this.props;
const jsonReceived = navigation.getParam('item', {})
return(
<ImageBackground style={{width:"100%",height:"100%",alignItems:"center",justifyContent:"center"}} source={{uri:jsonReceived.video_banner}}>
<TouchableOpacity
onPress={() => {
this.setState({playing:true})
}}
>
<Image source={require("../assets/Common/Play/playIcon.png")}/>
</TouchableOpacity>
</ImageBackground>
)
} else if (this.state.videoFileArr.length > 0 && this.state.playing === true) {
return (
<View style={styles.container}>
<Video source={{ uri:this.state.videoFileArr[0].link}} // Can be a URL or a local file.
ref={ ref =>
this.player = ref
} // Store reference
onBuffer={this.onBuffer} // Callback when remote video is buffering
onError={this.videoError} // Callback when video cannot be loaded
style={styles.backgroundVideo}
controls={true}
paused={false}
fullscreen={true}
/>
</View>
)
}
}
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1, overflow: "hidden" }}>
<View style={{ flex: 1, backgroundColor: "green" }}>
{this.renderOptions()}
</View>
{/* top navigationBar */}
<View
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
width: "100%",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
height: 80,
backgroundColor: null
}}
>
<TouchableOpacity onPress={
()=>this.goToHome()
}>
<Image style={{ margin: 8 }} source={require("../assets/Common/goBack/goBack.png")} />
</TouchableOpacity>
<TouchableOpacity>
<Image style={{ margin: 8 }} source={require("../assets/Common/Star/starOff.png")} />
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container:{ flex: 1, justifyContent: "center"},
backgroundVideo: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});
and this is the output screen where I can not play video in full screen:
Please help, What I'm doing wrong ?
I have solved fullscreen of video frame just adding resizeMode on Video component:
<Video source={{ uri:this.state.videoFileArr[0].link}} // Can be a URL or a local file.
ref={ ref =>
this.player = ref
} // Store reference
onBuffer={this.onBuffer} // Callback when remote video is buffering
onError={this.videoError} // Callback when video cannot be loaded
style={styles.backgroundVideo}
controls={true}
paused={false}
fullscreen={true}
resizeMode="cover"
/>
I was struggling with the same problem couple of days ago but I made it work for me in android. I hope this will help you also.
You will need to install some other package as well that is
1. react-native-video-controls
2. react-native-orientation
now In your Screen where the video will be played.
import React, { Component } from 'react'
import {
Text,
StyleSheet,
StatusBar,
Dimensions,
Alert,
Modal,
BackHandler,
TouchableOpacity,
ToastAndroid,
} from 'react-native'
import { Container, View, Button, Icon, List, ListItem } from 'native-base';
import Video from 'react-native-video';
import Orientation from 'react-native-orientation';
const sample = require('../assets/demo.mp4');
export default class Player extends Component {
constructor(props) {
super(props);
this.onLoad = this.onLoad.bind(this);
this.onProgress = this.onProgress.bind(this);
}
state = {
rate: 1,
volume: 1,
muted: false,
resizeMode: 'contain',
duration: 0.0,
currentTime: 0.0,
active: false,
modalVisible: false,
fullScreen: true,
};
onLoad(data) {
this.setState({ duration: data.duration });
}
onProgress(data) {
this.setState({ currentTime: data.currentTime });
}
getCurrentTimePercentage() {
if (this.state.currentTime > 0) {
return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
} else {
return 0;
}
}
renderRateControl(rate) {
const isSelected = (this.state.rate == rate);
return (
<ListItem>
<TouchableOpacity onPress={() => { this.setState({ rate: rate }) }}>
<Text style={{ fontWeight: isSelected ? "bold" : "normal" }}>
{rate}x
</Text>
</TouchableOpacity>
</ListItem>
)
}
renderResizeModeControl(resizeMode) {
const isSelected = (this.state.resizeMode == resizeMode);
return (
<TouchableOpacity onPress={() => { this.setState({ resizeMode: resizeMode })
}}>
<Text style={[styles.controlOption, { fontWeight: isSelected ? "bold" :
"normal" }]}>
{resizeMode}
</Text>
</TouchableOpacity>
)
}
setModalVisible = (visible) => {
this.setState({ modalVisible: visible });
}
fullScreen = () => {
Orientation.getOrientation((err, orientation) => {
if (orientation == 'LANDSCAPE') {
Orientation.lockToPortrait();
} else {
Orientation.lockToLandscape();
}
});
}
backAction = () => {
Orientation.getOrientation((err, orientation) => {
if (orientation == 'LANDSCAPE') {
Orientation.lockToPortrait();
}
});
};
componentDidMount() {
this.backHandler = BackHandler.addEventListener(
"hardwareBackPress",
this.backAction
);
}
componentWillUnmount() {
this.backHandler.remove();
}
render() {
const { modalVisible, paused } = this.state
const url = `https://www.sample-
videos.com/video/mp4/720/big_buck_bunny_720p_10mb.mp4`;
const flexCompleted = this.getCurrentTimePercentage() * 100;
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
return (
<View style={styles.container}>
<StatusBar hidden={true} />
<Video source={sample}
style={styles.fullScreen}
rate={this.state.rate}
paused={this.state.paused}
volume={this.state.volume}
muted={this.state.muted}
resizeMode={this.state.resizeMode}
onLoad={this.onLoad}
onProgress={this.onProgress}
onEnd={() => { alert('Done!') }}
controls
repeat={true} />
<View style={[{ left: 0 }, styles.rateControl]}>
<Button
transparent
onPress={() => {
this.fullScreen();
}}
>
<Icon type="FontAwesome5" name="compress" style={{ color: "#fff",
fontSize: 15 }} />
</Button>
</View>
<View style={styles.rateControl}>
<Button
transparent
onPress={() => {
this.setModalVisible(true);
}}
>
<Icon type="FontAwesome5" name="ellipsis-v" style={{ color:
"#fff", fontSize: 15 }} />
</Button>
</View>
{/* <View style={styles.controls}>
<View style={styles.generalControls}>
<View style={styles.resizeModeControl}>
{this.renderResizeModeControl('cover')}
{this.renderResizeModeControl('contain')}
{this.renderResizeModeControl('stretch')}
</View>
</View>
<View style={styles.trackingControls}>
<View style={styles.progress}>
<View style={[styles.innerProgressCompleted, { flex: flexCompleted }]} />
<View style={[styles.innerProgressRemaining, { flex: flexRemaining }]} />
</View>
</View>
</View> */}
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<View style={styles.closeModal}>
<Button
transparent
onPress={() => { this.setModalVisible(!modalVisible);
}}
>
<Icon name="close" />
</Button>
</View>
<View>
<Text style={{ textAlign: 'center', fontWeight: 'bold'
}}>Play Rate</Text>
<List style={{ flexDirection: 'row', justifyContent:
'space-between', alignItems: 'center' }}>
{this.renderRateControl(0.25)}
{this.renderRateControl(0.5)}
{this.renderRateControl(1.0)}
{this.renderRateControl(1.5)}
{this.renderRateControl(2.0)}
</List>
</View>
</View>
</View>
</Modal>
</View >
)
}
}
const styles = StyleSheet.create({
backgroundVideo: {
// position: 'absolute',
// top: 0,
// left: 0,
// bottom: 0,
// right: 0,
width: Dimensions.get('window').width,
height: Dimensions.get('window').width * .6,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'black',
},
fullScreen: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
controls: {
backgroundColor: "transparent",
borderRadius: 5,
position: 'absolute',
bottom: 20,
left: 20,
right: 20,
},
progress: {
flex: 1,
flexDirection: 'row',
borderRadius: 3,
overflow: 'hidden',
},
innerProgressCompleted: {
height: 20,
backgroundColor: '#cccccc',
},
innerProgressRemaining: {
height: 20,
backgroundColor: '#2C2C2C',
},
generalControls: {
flex: 1,
// flexDirection: 'row',
borderRadius: 4,
overflow: 'hidden',
paddingBottom: 10,
},
rateControl: {
flexDirection: 'row',
position: 'absolute',
top: 10,
right: 10
},
volumeControl: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
resizeModeControl: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
controlOption: {
alignSelf: 'center',
fontSize: 11,
color: "white",
paddingLeft: 2,
paddingRight: 2,
lineHeight: 12,
},
centeredView: {
flex: 1,
marginTop: '22%'
},
modalView: {
width: '100%',
padding: '5%',
backgroundColor: "white",
position: 'absolute',
bottom: 10,
},
openButton: {
backgroundColor: "#F194FF",
borderRadius: 20,
padding: 10,
elevation: 2
},
closeModal: {
alignItems: 'flex-end',
margin: -10
},
textStyle: {
color: "white",
fontWeight: "bold",
textAlign: "center"
},
modalText: {
marginBottom: 15,
textAlign: "center"
}
});
I hope this will help you.
Most of suggestion involves adding additional libraries to achieve fullscreen in react-native-vide for android. Its not really needed.
What we need is set the height and width of to full screen to achieve this. we need to use variable instead of fixed value then setState to refresh the view.
Below is the sample typescript code that works for react-native-video fullscreen:
import React, {Component} from 'react';
import {
View,
StyleSheet,
TouchableWithoutFeedback,
Dimensions,
} from 'react-native';
import Video from 'react-native-video';
import Icon from 'react-native-vector-icons/FontAwesome';
import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons';
interface Props {}
interface States {
paused: boolean;
inFullScreen: boolean;
}
export default class Sandbox extends Component<Props, States> {
player: any;
videoStyle: {minWidth: number; minHeight: number};
constructor(props: Props) {
super(props);
this.state = {
paused: true,
inFullScreen: false,
};
this.videoStyle = {minWidth: 400, minHeight: 400};
}
render() {
return (
<View style={{height: 400, width: 400}}>
<Video
source={{
uri:
'https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4',
}} // Can be a URL or a local file.
ref={(ref: any) => {
this.player = ref;
}} // Store reference
controls={false}
paused={this.state.paused}
resizeMode={'cover'}
style={this.videoStyle}
/>
<View style={styles.controls}>
<TouchableWithoutFeedback
onPress={() => {
this.setState({paused: !this.state.paused});
}}>
<Icon name={'play'} size={30} color="#FFF" />
</TouchableWithoutFeedback>
<TouchableWithoutFeedback
onPress={() => {
if (!this.state.inFullScreen) {
//Orientation.lockToLandscape();
this.videoStyle = {
minHeight: Dimensions.get('window').height,
minWidth: Dimensions.get('window').width,
};
} else {
this.videoStyle = {
minHeight: 400,
minWidth: 400,
};
}
this.setState({inFullScreen: !this.state.inFullScreen});
}}>
<MaterialIcon name={'fullscreen'} size={30} color="#FFF" />
</TouchableWithoutFeedback>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
controls: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
height: 48,
top: 20,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
paddingHorizontal: 10,
},
});
As #Selva answered we can use variables for video sizes i.e width & height and make it occupy full screen and use a stack to place the video in a stack screen above the current screen and pop it when needed. Even if it's in a flat list.
Had same issue fix it by removing the wrapping and setting just the height to the player, no other library needed and no need to manage the orientation.
<Video source={{uri: this.props.videoData.uri}}
onEnd={this.onVideoEnd}
style={styles.player}
controls={true} />
<View
onStartShouldSetResponder={() => setPaused(!paused)}
style={{
marginHorizontal: 10,
backgroundColor: "black",
position: "relative",
transform: [{ rotate: "90deg" }],
// justifyContent:'center',
// alignItems:'center'
}}
>
<Video
onEnd={onEnd}
onLoad={onLoad}
onLoadStart={onLoadStart}
posterResizeMode={"cover"}
onProgress={onProgress}
paused={paused}
ref={(ref) => (videoPlayer.current = ref)}
resizeMode={'stretch'}
source={{
uri: "url",
}}
style={{
...styles.backgroundVideo,
height: width,
aspectRatio:2
// width: "100%"
// alignSelf:'center'
// transform: [{ rotate: '90deg'}]
}}
/>
just give it a width of
width:'100%'