Reputation:
I'm building an app on RN and I made the mistake to put my values in pixels instead of percentages so it fits with all screen sizes... Can someone help me convert the values in my const styles into percentage in my javascript code? Just tell me what to add or modify to keep the exact same values, but in percentages.
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image, Modal, Pressable, Dimensions, TextInput } from 'react-native';
import axios from 'axios';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useTheme } from './ThemeContext';
import Animated, { useSharedValue, useAnimatedStyle, withSpring, runOnJS } from 'react-native-reanimated';
import ImageCropPicker from 'react-native-image-crop-picker';
import Sound from 'react-native-sound'; // Importez la bibliothèque pour les sons
const { width, height } = Dimensions.get('window');
const ProfileScreen = () => {
const [profileImage, setProfileImage] = useState(null);
const [profileImageUrls, setProfileImageUrls] = useState([]);
const [username, setUsername] = useState('');
const { isDarkTheme } = useTheme();
const [modalVisible, setModalVisible] = useState(false);
const [currentIndex, setCurrentIndex] = useState(0);
const translateX = useSharedValue(0);
// Créez une instance du son
const sound = new Sound(require('./assets/sounds/omg-username.mp3'), (error) => {
if (error) {
console.log('Failed to load sound', error);
}
});
useEffect(() => {
const fetchProfileImages = async () => {
try {
const response = await axios.get('https://raw.githubusercontent.com/RaylinkX/swipememe-homepage/refs/heads/main/profileImages.json');
setProfileImageUrls(response.data.images);
} catch (error) {
console.error('Error fetching profile images:', error);
}
};
const loadUserData = async () => {
const savedImageUrl = await AsyncStorage.getItem('selectedProfileImage');
const savedUsername = await AsyncStorage.getItem('username');
setProfileImage(savedImageUrl ? { uri: savedImageUrl } : null);
setUsername(savedUsername || '');
};
fetchProfileImages();
loadUserData();
// Nettoyez le son lors du démontage du composant
return () => {
sound.release();
};
}, []);
const handleUsernameChange = async (newUsername) => {
setUsername(newUsername);
await AsyncStorage.setItem('username', newUsername);
};
const handleSelectImage = async (url) => {
try {
const croppedImage = await ImageCropPicker.openCropper({
path: url,
width: 300,
height: 300,
cropping: true,
cropperCircleOverlay: true,
});
setProfileImage({ uri: croppedImage.path });
await AsyncStorage.setItem('selectedProfileImage', croppedImage.path);
setModalVisible(false);
} catch (error) {
console.error('Image cropping error:', error);
}
};
const handleSwipe = (direction) => {
const nextIndex = direction === 'left'
? (currentIndex + 1) % profileImageUrls.length
: (currentIndex - 1 + profileImageUrls.length) % profileImageUrls.length;
runOnJS(setCurrentIndex)(nextIndex);
translateX.value = withSpring(direction === 'left' ? -width : width);
};
const onSwipeComplete = (direction) => {
handleSwipe(direction);
translateX.value = withSpring(0);
};
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateX: translateX.value }],
}));
const buttonFontSize = (height * 3) / 100;
const handleBlur = () => {
// Joue le son lorsque l'utilisateur quitte le champ de texte
sound.play((success) => {
if (!success) {
console.log('Sound did not play');
}
});
};
return (
<View style={[styles.container, { backgroundColor: isDarkTheme ? '#001F3F' : '#BF2A2A' }]}>
<View style={styles.circleContainer}>
{profileImage
? <Image source={profileImage} style={styles.profileImage} />
: <View style={styles.circle} />}
</View>
<TouchableOpacity
style={[styles.button, { backgroundColor: isDarkTheme ? '#FFD700' : '#FF5733' }]}
onPress={() => setModalVisible(true)}
>
<Text style={styles.buttonText}>Change profile picture</Text>
</TouchableOpacity>
<TextInput
style={styles.usernameInput}
placeholder="Enter your username"
value={username}
onChangeText={handleUsernameChange}
onBlur={handleBlur} // Ajoutez l'événement onBlur ici
/>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}
>
<View style={styles.modalContainer}>
<TouchableOpacity style={styles.closeButton} onPress={() => setModalVisible(false)}>
<Text style={styles.closeButtonText}>Close</Text>
</TouchableOpacity>
<View style={styles.imageContainer}>
<Animated.View style={[styles.swipeImageContainer, animatedStyle]}>
<Image source={{ uri: profileImageUrls[currentIndex] }} style={styles.swipeImage} />
</Animated.View>
<View style={styles.buttonContainer}>
<Pressable onPress={() => onSwipeComplete('right')} style={styles.arrowButton}>
<Image source={require('./assets/arrow-left.png')} style={styles.arrowImage} />
</Pressable>
<Pressable onPress={() => onSwipeComplete('left')} style={styles.arrowButton}>
<Image source={require('./assets/arrow-right.png')} style={styles.arrowImage} />
</Pressable>
</View>
<Pressable onPress={() => handleSelectImage(profileImageUrls[currentIndex])} style={styles.selectButton}>
<Text style={[styles.selectButtonText, { fontSize: buttonFontSize }]}>Select</Text>
</Pressable>
</View>
</View>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'flex-start',
paddingTop: '10%',
},
circleContainer: {
alignItems: 'center',
marginBottom: '5%',
},
usernameInput: {
marginTop: '3%',
width: '80%',
paddingVertical: '2%',
paddingHorizontal: '5%',
borderRadius: 8,
borderColor: '#ccc',
borderWidth: 1,
backgroundColor: '#FFF',
fontSize: 16,
textAlign: 'center',
},
circle: {
width: '90%',
aspectRatio: 1,
borderRadius: 9999,
backgroundColor: '#FFF',
justifyContent: 'center',
alignItems: 'center',
marginTop: '5%',
},
profileImage: {
width: '90%',
aspectRatio: 1,
borderRadius: 9999,
resizeMode: 'cover',
},
button: {
marginTop: '3%',
width: '60%',
paddingVertical: '2%',
borderRadius: 8,
alignItems: 'center',
},
modalContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.7)',
},
closeButton: {
alignSelf: 'flex-end',
marginBottom: 20,
padding: 10,
},
closeButtonText: {
fontSize: 16,
color: '#FFF',
},
imageContainer: {
width: '80%',
height: '50%',
backgroundColor: '#FFF',
borderRadius: 10,
justifyContent: 'center',
alignItems: 'center',
overflow: 'hidden',
},
swipeImageContainer: {
width: '100%',
height: '80%',
},
swipeImage: {
width: '100%',
height: '100%',
top: '15%',
borderRadius: 10,
resizeMode: 'contain',
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '80%',
marginTop: 10,
},
arrowButton: {
padding: 10,
marginHorizontal: '50%',
marginLeft: '0%',
},
arrowImage: {
width: 60, // Adjust the width as needed
height: 40, // Adjust the height as needed
right: '30%',
marginHorizontal: '10%',
top: '150%',
},
selectButton: {
marginBottom: '10%',
paddingVertical: 10,
paddingHorizontal: 20,
backgroundColor: '#007BFF',
borderRadius: 8,
left: '1%',
},
selectButtonText: {
color: '#FFF',
// Remove the fontSize from here; it will be applied inline
},
});
export default ProfileScreen;
Upvotes: 0
Views: 98
Reputation: 51
Don't know exactly what it should look like, but I would try to give the images a height of 80% and let the browser/engine handle the width calculation of the Image.
If that's not what you want to achieve, I need more detailed information on that matter.
By the way: Using inset properties (top, right, bottom, left, ...) without a position attribute can lead to unexpected results in different browsers/engines and should not be rendered as per specification.
Upvotes: 1