React native animation progress bar with image - react-native

Today i want to make a progress bar with a image on the left. Unfortunately I can't position the image in the right place. For now it looks like that
I want to looks something like that -
My code so far -
<View
style={{
flexDirection: "row",
alignSelf: "center",
marginBottom: "20%",
marginTop: "10%",
}}
>
<View
style={{
width: "90%",
height: 30,
padding: 2.5,
backgroundColor: "#00000020",
borderRadius: 30,
}}
>
{/* <Animated.View
style={[
{
width: "100%",
height: 25,
borderRadius: 15,
backgroundColor: "#f5de41",
},
{
width: progressAnim,
},
]}
></Animated.View> */}
<Image
source={require("./assets/lion.png")}
style={{
height: 44,
height: 44,
position: "absolute",
}}
resizeMode="contain"
/>
</View>
</View>
I tried to add left: '-62%' in style of iamge but it not works. I am not sure how to move the lion to the left?

One approach would be to remove the absolute position and use flexbox to align the the image to end of the row:
const ProgressBar = ({imgSource,imgStyle,imgSize,style,progress,color})=>{
let loadingAnim = useRef(new Animated.Value(progress)).current;
const [{width,height},setViewDimensions] = useState({});
// get parent view size
const onLayout=({nativeEvent})=>{
setViewDimensions({
width:nativeEvent.layout.width,
height:nativeEvent.layout.height
})
}
const animatedWidth =loadingAnim.interpolate({
inputRange:[0,1],
outputRange:[0,width-imgSize],
extrapolate:'clamp'
})
const containerAnimation = {
margin:0,
padding:0,
width:Animated.add(animatedWidth,imgSize),
backgroundColor:color,
height:'100%',
justifyContent:'center',
overflow:'hidden'
}
useEffect(()=>{
Animated.timing(loadingAnim,{
toValue:progress,
duration:100
}).start()
},[progress])
return(
<View style={[styles.loadingContainer,{height:imgSize*1.5||null},style]} onLayout={onLayout}>
<Animated.View style={[styles.loadingContainer,containerAnimation]}>
<Animated.Image
source={imgSource}
style={[{height:imgSize,width:imgSize,alignSelf:'flex-end'},imgStyle,{}]}
/>
</Animated.View>
</View>
)
}
I found this approach to be slightly un-smooth. I think this is because the width of image's parent view was being animated, and not the actual position of the image.
Another approach would be to animate the image:
const ProgressBar = ({imgSource,imgStyle,imgSize,style,progress,color})=>{
let widthAnim = useRef(new Animated.Value(progress)).current;
const [{width,height},setViewDimensions] = useState({});
// get parent view width to determine progress view size
const onLayout=({nativeEvent})=>{
setViewDimensions({
width:nativeEvent.layout.width,
height:nativeEvent.layout.height
})
}
const animatedWidth = widthAnim.interpolate({
inputRange:[0,1],
outputRange:[0,width-imgSize],
extrapolate:'clamp'
})
const containerAnimation = {
// min width will be imgSize
width:Animated.add(animatedWidth,imgSize),
backgroundColor:color,
}
const imgAnimation = {
left:animatedWidth
}
// animate progress changess
useEffect(()=>{
Animated.timing(widthAnim,{
toValue:progress,
duration:100
}).start()
},[progress])
return(
<View>
<View style={[styles.loadingContainer,{height:imgSize*1.25||null},style]} onLayout={onLayout}>
<Animated.View style={[styles.progressBar,containerAnimation]}/>
</View>
<Animated.Image
source={imgSource}
style={[styles.image,{height:imgSize,width:imgSize},imgStyle,imgAnimation]}
resizeMode='contain'
/>
</View>
)
}

Related

White background in react-native-view-shot

I am using react-native-view-shot to save a screenshot of a view, whose height is not initially defined, as I am using padding and setting the height of the view using the onLayout method.
The problem is that, when the view has an initial fixed height, the screenshot taken does not have a white background, which is what I want. However, when I set the height when the onLayout is invoked, the screenshot has a white background.
Here's my code:
const [height, setHeight] = useState();
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
ref={contentRef}
style={{
height,
width: width - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={styles.text}>This is a test using padding</Text>
</View>
https://snack.expo.dev/#pietroputelli/react-native-view-shot
=========== EDIT ===========
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<View ref={shotRef} style={{ backgroundColor: "transparent" }}>
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
style={{
height,
width: width / 2 - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={{ color: "white" }}>This is a test using padding</Text>
</View>
</View>
<Button
onPress={() => {
captureRef(shotRef, {
format: "png",
quality: 0.8,
}).then(
async (uri) => {
await MediaLibrary.saveToLibraryAsync(uri);
},
(error) => console.error("Oops, snapshot failed", error)
);
}}
title="Take screenshot"
/>
</View>
I can able to generate the same from viewshot:
take another view and give a reference to that view and generate a screenshot from that. might be its issue of reference. Please check the below screenshot.
<View ref={viewshotRef}
style={{
// whatever you want to add as per your requirement
}} >
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
style={{
height,
width: DEVICE_WIDTH / 2 - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={{ color: 'white' }}>This is a test using padding</Text>
</View>
</View>
For base64:
import * as MediaLibrary from "expo-media-library";
import React, { useRef, useState } from "react";
import {
Button,
Dimensions,
StyleSheet,
Text,
View,
} from "react-native";
import ViewShot, { captureRef } from "react-native-view-shot";
const { width } = Dimensions.get("window");
export default function ViewCapture() {
const contentRef = useRef();
const [height, setHeight] = useState(undefined);
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "transparent" }}>
<ViewShot ref={contentRef} options={{ result: 'base64' }}>
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
style={{
height,
width: width - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={{ color: "white" }}>This is a test using padding</Text>
</View>
</ViewShot>
{/* </View> */}
<Button
onPress={() => {
contentRef.current.capture().then((url) => {
console.log("on success", "data:image/jpeg;base64,", url)
// await MediaLibrary.saveToLibraryAsync(uri);
},
(error) => console.error("Oops, snapshot failed", error)
);
}}
title="Take screenshot"
/>
</View>
);
}
For File:
You need to set the result as tmpfile so you will get file uri in the callback
<ViewShot ref={contentRef} options={{ result: 'tmpfile' }}>
I hope it will work!

React Native Button onPressIn animation request

I want to achieve an animation each time a user taps on a button this shrinks in a smaller button.
GIF HERE
For this use TouchableHighlight component from react-native. It has onPressIn and onPressOut on which you can change buttons width and height.
e.g.
export const TouchableHighlightExample = () => {
const [BtnSize, setBtnSize ] = useState({ height: 40, width: "100%" });
const zoomIn=()=>{
setBtnSize({ height: 35, width: "90%",marginHorizontal:"5%" })
}
const zoomOut=()=>{
setBtnSize({ height: 40, width: "100%" })
}
return (
<View style={styles.container}>
<TouchableHighlight underlayColor="#ffffff00" onPressIn={zoomIn} onPressOut={zoomOut}>
<View style={[styles.button,BtnSize]}>
<Text style={{color: "white"}}>Touch Here</Text>
</View>
</TouchableHighlight>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 10,
},
button: {
alignItems: 'center',
backgroundColor: 'red',
padding: 10,
borderRadius:40
},
});

Animating Views from top-bottom to side-by-side in React Native

I am trying to create a custom header for my app and I want to animate it in a certain way. I will describe the way it is supposed to be animated.
If you have a look at the image you will see one red View that contains one green View and a blue View. I would like the Views be arranged side-by-side from their current positions while animating.
I have tried to create the code for a collapsing header and the red View that contains everything, is shrinking based on a ScrollView but I cant get the green and blue Views to come side-by-side.
Home.js
const HEADER_EXPANDED_VIEW = 200
const HEADER_COLLAPSED_VIEW = 80
export default class HomeActivity extends Component {
constructor(props) {
super(props)
this.state = {
scrollY: new Animated.Value(0)
}
}
static navigationOptions = {
title: "HomeActivity",
header: null
}
render() {
const headerHeight = this.state.scrollY.interpolate({
inputRange: [0, HEADER_EXPANDED_VIEW - HEADER_COLLAPSED_VIEW],
outputRange: [HEADER_EXPANDED_VIEW, HEADER_COLLAPSED_VIEW],
extrapolate: "clamp"
})
// console.log(headerHeight)
return (
<View style={styles.container}>
<ScrollView
contentContainerStyle={{
padding: 16,
paddingTop: HEADER_EXPANDED_VIEW,
color: "#FFFFFF"
}}
onScroll={Animated.event([
{
nativeEvent: {
contentOffset: {
y: this.state.scrollY
}
}
}
])}
>
<Text style={styles.title}>This is Title</Text>
<Text style={styles.content}>
.....
</Text>
</ScrollView>
<CollapsingHeader headerHeight={headerHeight} />
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
scrollContainer: {
padding: 16
},
title: {
fontSize: 24,
marginVertical: 16
}
})
CollapsingHeader.js
export default class CollapsingHeader extends Component {
render() {
return (
<Animated.View
style={{
height: this.props.headerHeight,
width: Dimensions.get("screen").width,
position: "absolute",
top: 0,
left: 0,
borderWidth: 2,
borderColor: "#FF0000",
backgroundColor: "#212121"
}}
>
<View
style={{
borderWidth: 2,
borderColor: "#00FF00"
}}
>
<MenuButton />
</View>
<View
style={{
flexDirection: "row",
borderWidth: 2,
borderColor: "#0000FF"
}}
>
<View
style={{
flexGrow: 1
}}
>
<Text
style={{
fontFamily: "NunitoSans-Bold",
fontSize: 40,
color: "#FFFFFF"
}}
>
Home
</Text>
</View>
<SortButton />
<SearchButton />
</View>
</Animated.View>
)
}
}
I am relatively new to React Native, please assume I know very little about it.
If you know the headerHeight at which it's going to collapse, then you could add a dynamic flexDirection to the Animated.View style.
style={{
/* ...your Animated.View styles */
flexDirection: this.props.headerHeight < /* collapse height */ ? 'row' : 'column'
}}

React-Native: how to fill fullsize screen without explicit Width and Height?

I want to create a fullsize-screen with the child-view (a video player) that is rendered over the full size of the screen. I only get it work when i pass explicitly width and height to the component.
But i know that there is a property called "flex". In many tutorials they do something like "flex: 1", but for me it nowhere does what it is supposed to.
(For the sake of completeness, the video-player is not part of the question. I can also replace the <video> tag with <Image> or each other kind of view and get the same results)
render() {
const uri = this.props.uri
return (
<KeyboardAwareScrollView keyboardShouldPersistTaps="always" >
<TouchableWithoutFeedback onPress={RouterActions.pop}>
<Video source={{uri: uri}}
ref={(ref) => {
this.player = ref
}}
style={s.fullsize}
/>
</TouchableWithoutFeedback>
<TouchableOpacity onPress={RouterActions.pop} style={s.closeBtn}>
<Icon name="times-circle-o" size={20} color="white" />
</TouchableOpacity>
</KeyboardAwareScrollView>
)
}
My styles:
This is only working when i pass the width and height:
const s = StyleSheet.create({
fullsize: {
backgroundColor: 'black',
//flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
closeBtn: {
position: 'absolute',
left: 20,
top: 25,
},
});
I only tried out this one, but then the screen will be empty because of the -Component has a width and height of 0 each.
const s = StyleSheet.create({
fullsize: {
backgroundColor: 'black',
flex: 1, // this is not working
left: 0,
right: 0,
top: 0,
bottom: 0
},
});
I believe doing flex: 1 will make it take up all the space provided by it's parent element. In your case, none of your parent elements have any styling so try this out:
render() {
const uri = this.props.uri
return (
<View style={{ flex: 1 }}>
<TouchableWithoutFeedback style={{ flex: 1 }} onPress={RouterActions.pop}>
<Video source={{uri: uri}}
ref={(ref) => {
this.player = ref
}}
style={{ flex: 1 }}
/>
</TouchableWithoutFeedback>
<TouchableOpacity onPress={RouterActions.pop} style={s.closeBtn}>
<Icon name="times-circle-o" size={20} color="white" />
</TouchableOpacity>
</View>
)
}

FlatList numColumns doesn't appear to be working correctly?

I'm trying to use a FlatList to show a bunch of user avatars to someone in a grid format, but it ends up looking very strange and I can't seem to figure out how to fix it.
Here's what it looks like
Here's what my FlatList code looks like:
<FlatList
style={{flex: 1}}
data={this.props.usersList}
horizontal={false}
numColumns={3}
columnWrapperStyle={{ marginTop: 10 }}
renderItem={({ item }) => this.renderItem(item)}
keyExtractor={this._keyExtractor}/>
and here's what the component looks like for renderItem:
class UserButton extends React.Component {
render() {
const { item, onPress } = this.props;
return (
<TouchableOpacity style={styles.button} onPress={onPress}>
<Image
source={(item.avatar) ? { uri: item.avatar } : require('../assets/images/userplaceholder.png')}
resizeMode='cover'
style={styles.imageStyle}
/>
</TouchableOpacity>
)
}
const styles = {
button: {
height: 100,
width: 100,
borderColor: '#aaa',
backgroundColor: '#aaa',
borderWidth: 2,
borderRadius: 50,
justifyContent: 'center',
alignItems: 'center',
marginHorizontal: 5,
},
imageStyle: {
height: 96,
width: 96,
alignSelf: 'center',
borderRadius: 48,
marginTop: (Platform.OS == 'android') ? 0 : 0.4
}
}
export default UserButton;
Anyone have any ideas?
You can take width from Dimensions and set that width for items of your flatlist.
const {width} = Dimensions.get('window');
const itemWidth = (width) / 4;
I was in kind of similar situation, but now I have proper grid using flatList, you can take a look at my code attached below.
<FlatList
contentContainerStyle={{margin:4}}
horizontal={false}
numColumns={4}
data={this.state.categoryDataSource}
renderItem={(categoryItem) =>
<CategoryListItem category={categoryItem.item} mode="small"/>
}
keyExtractor={category => category.id}
/>