How to make image draggable within the view parent in react-native? - javascript

I have a FlatList carousel and I'm using react-native-gesture-handler to create a draggable zoom in effect for the image. The zoom in and dragging works, however I'm having problems setting a boundary for the image as it is overlapped by the next image once I zoom in and the dragging will go all the way to the end of the slider. I found a couple of examples where you can achieve this, but all of them were with react-native-reanimated. This is my code so far:
const { height, width } = Dimensions.get('window');
console.log("height", height, width)
const ITEM_HEIGHT = height * 0.7
const ITEM_WIDTH = height * 0.46
const Preview = ({ navigation, route }) => {
const { item, goBack } = route.params;
const buttonRef = React.useRef();
const ref = useRef()
const selectedPhotoIndex = data.findIndex((i) => i.id === item.id)
const scrollX = useRef(new Animated.Value(selectedPhotoIndex)).current
const scale = useRef(new Animated.Value(1)).current
const panRef = useRef()
const pinchRef = useRef()
const translationX = useRef(new Animated.Value(0)).current
const translationY = useRef(new Animated.Value(0)).current
const [scrollEnabled, setSCrollEnabled] = useState(true)
const goToIndex = useCallback((info) => {
const wait = new Promise(resolve => setTimeout(resolve, 200));
wait.then(() => {
ref.current?.scrollToIndex({ index: info.index, animated: true });
})
})
const pinchEvent = Animated.event([{ nativeEvent: { scale: scale } }], { useNativeDriver: true })
const onStateChange = (event) => {
if (event.nativeEvent.state === State.ACTIVE) {
setSCrollEnabled(false)
}
const nScale = event.nativeEvent.scale
if (event.nativeEvent.state === State.END || event.nativeEvent.oldState === State.ACTIVE) {
if (nScale < 1) {
Animated.spring(scale, {
toValue: 1,
useNativeDriver: true
}).start()
translationX.setOffset(0)
translationY.setOffset(0)
setSCrollEnabled(true)
}
} else if (event.nativeEvent.state === State.ACTIVE) {
setSCrollEnabled(false)
}
}
const panEvent = Animated.event([{
nativeEvent: {
translationX,
translationY
}
}], { useNativeDriver: true })
const onChange = (event) => {
translationX.extractOffset()
translationY.extractOffset()
if (event.nativeEvent.oldState === State.ACTIVE) {
}
}
const RenderItem = ({ item, translateX, viewLimit }) => {
return (
<View style={{ height, justifyContent: 'center', alignItems: 'center', alignContent: 'center' }}>
<PanGestureHandler failOffsetY={[-500, 500]}
failOffsetX={[-500, 500]}
enabled={!scrollEnabled}
ref={panRef}
simultaneousHandlers={[pinchRef]}
onHandlerStateChange={onChange}
onGestureEvent={panEvent}>
<Animated.View style={{
height, transform: [
{ translateX },
{ scale }
],
}}>
<SharedElement id={`item.${item.id}.image_url`}>
<PinchGestureHandler ref={pinchRef}
simultaneousHandlers={[panRef]}
onGestureEvent={pinchEvent}
onHandlerStateChange={onStateChange}>
<Animated.Image
source={{ uri: item.image_url }}
style={{
width: ITEM_WIDTH,
height: ITEM_HEIGHT,
transform: [
{ translateX: translationX },
{ translateY: translationY }
]
}}
resizeMode='contain'
/>
</PinchGestureHandler>
</SharedElement>
</Animated.View>
</PanGestureHandler>
<View
style={{ flexDirection: 'row', marginTop: 10, paddingHorizontal: 20 }}
>
<View style={{ flexDirection: 'column', paddingLeft: 6 }}>
<SharedElement id={`item.${item.id}.title`}>
<Text
style={{
color: 'white',
fontSize: 24,
fontWeight: 'bold',
lineHeight: 28
}}
>
{item.title}
</Text>
</SharedElement>
<SharedElement id={`item.${item.id}.description`}>
<Text
style={{
color: 'white',
fontSize: 16,
fontWeight: 'bold',
lineHeight: 18
}}
>
{item.description}
</Text>
</SharedElement>
</View>
</View>
</View>
)
}
return (
<View style={{ flex: 1, backgroundColor: '#0f0f0f', overflow: 'hidden' }}>
<BackButton navigation={navigation} />
<Animated.FlatList
onScroll={Animated.event([{ nativeEvent: { contentOffset: { x: scrollX } } }],
{ useNativeDriver: true })}
pagingEnabled
nestedScrollEnabled={true}
snapToInterval={ITEM_WIDTH}
decelerationRate="normal"
scrollEnabled={scrollEnabled}
horizontal
ref={ref}
data={data}
keyExtractor={(item) => item.id}
initialScrollIndex={selectedPhotoIndex}
onScrollToIndexFailed={info => goToIndex(info)}
renderItem={({ item: details, index }) => {
const inputRange = [
(index - 1) * ITEM_WIDTH,
index * ITEM_WIDTH,
(index + 1) * ITEM_WIDTH
]
const swipeX = scrollX.interpolate({
inputRange,
outputRange: [ITEM_WIDTH, 0, -ITEM_WIDTH]
})
const viewLimit = translationX.interpolate({
inputRange,
outputRange: [ITEM_WIDTH, 0, -ITEM_WIDTH / 2]
})
return (
<RenderItem viewLimit={viewLimit} translateX={swipeX} item={details} />
)
}} />
</View>
);
};
How I can zoom in and contain the image dragging within the view, without the image being overlapped by the others?I've tried to change the gesture handler based on this topic https://github.com/software-mansion/react-native-gesture-handler/issues/110 but it didn't work for me. Any help would be appreciated!

Related

Issue with updating an array in React Native

I am trying to work through a problem with my array state in React Native and correctly updating the array of objects when I click a button on one of the components.
Here is a video of the problem occurring, which is probably a better way to explain the issue: https://cdn.discordapp.com/attachments/935693286566416417/953045147397001276/RPReplay_Final1647294055.MP4
Here's the relevant code that might offer some insight:
MealPlanner.Js
const MealPlannerScreen = props => {
const minDate = new Date();
const [mealPlannerData, setMealPlannerData] = useState([]);
const [loading, setLoading] = useState(true)
const [selectedDate, setSelectedDate] = useState("03/13/2022")
const [breakfast, setBreakfast] = useState([])
const getMealPlannerData = async(date = '03/13/2022') => {
const isDataAvailable = await AsyncStorage.getItem("meal_planner")
if (isDataAvailable) {
const parsedData = JSON.parse(isDataAvailable)
let breakfastData = parsedData.filter(function (e) {
return ((e.date = date) && (e.meal == 'Breakfast'));
});
let lunchData = parsedData.filter(function (e) {
return ((e.date = date) && (e.meal == 'Lunch'));
});
let dinnerData = parsedData.filter(function (e) {
return ((e.date = date) && (e.meal == 'Dinner'));
});
let snacksData = parsedData.filter(function (e) {
return ((e.date = date) && (e.meal == 'Snacks'));
});
setMealPlannerData(JSON.parse(isDataAvailable))
setBreakfast(breakfastData)
setLunch(breakfastData)
setDinner(dinnerData)
setSnacks(snacksData)
} else {
setMealPlannerData([])
}
}
const childToParent = async(unique_id, meal, date) => {
const mealFiltered = mealPlannerData.filter(function (e) {
return e.unique_id != unique_id;
});
console.log(unique_id)
console.log(date)
if (meal === 'Breakfast') {
let breakfastData = mealFiltered.filter(function (e) {
return ((e.date = date) && (e.meal == 'Breakfast'));
});
console.log(breakfastData);
setBreakfast(breakfastData)
}
return (
<Container style={styles.background_general}>
<View>
{breakfast.map((p, i) => {
console.log(breakfast)
return (<>
<FoodItem meal={"Breakfast"} date={p.date} food={p.recipe_name} calories={p.calories} servings={p.servings} unique_id={p.unique_id} mealData={mealPlannerData} childToParent={childToParent}/>
<View style={{
borderBottomColor: 'rgba(0,0,0,0.1)',
borderBottomWidth: 1,
}}>
</View>
</>
)
})}
</View>
</Container>
}
}
And the FoodItem.js
const FoodItem = props => {
removeMeal = (unique_id, meal) => {
props.childToParent(unique_id, meal, props.date)
}
const renderRightActions = useCallback((progress, dragX) => {
const trans = dragX.interpolate({
inputRange: [-300, -160, 0],
outputRange: [0, 0, 160],
});
return (
<Animated.View
style={{
transform: [{ translateX: trans }],
}}
>
<LinearGradient
colors={["#6e269b", "#35184e"]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={{
flexDirection: "row",
width: 160,
height: "100%",
borderRadius: 20
}}
>
<RectButton style={{
justifyContent: "center",
alignItems: "center",
paddingHorizontal: 16,
flex: 1
}} onPress={() => {removeMeal(props.unique_id, props.meal) }}>
<View style={{ flexDirection: 'row' }}>
<Icon type="FontAwesome" name="trash" style={{ fontSize: 18, color: 'white', paddingRight: 10, marginTop: 2 }} />
<Text style={{ color: '#fff', fontWeight: 'bold', fontSize: 18 }}>
Remove
</Text>
</View>
</RectButton>
</LinearGradient>
</Animated.View>);
}, []);
return (
<View style={{
padding: 20
}}>
<Swipeable renderRightActions={renderRightActions}>
<View padding-6 row>
<View style={{ alignItems: "center" }}>
</View>
<View >
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>
{props.food}
</Text>
<View row marginT-8>
<View row>
<Text R14 color6D marginL-4>
{props.calories} cal
</Text>
</View>
<View row marginL-24>
<Text R14 color6D marginL-4>
{props.servings} servings
</Text>
</View>
</View>
</View>
</View>
</Swipeable>
</View>
)
}
Any help would be greatly appreciated!

I want to restart a circular progress bar after 30 seconds

const useProgress = (maxTimeInSeconds = 30) => {
const [elapsedTime, setElapsedTime] = useState(0);
const [progress, setProgress] = useState(0);
useEffect(() => {
const intervalId = setInterval((callback) => {
if (progress < 1) {
setElapsedTime((t) => t + 1);
}
}, 1000);
return () => clearInterval(intervalId);
}, []);
useEffect(() => {
setProgress(elapsedTime / maxTimeInSeconds);
console.log(elapsedTime);
}, [elapsedTime]);
return progress;
};
const progress = useProgress();
Here I have implemented logic to move the circular progress bar for 30 seconds. What I want to do is restart the progress after 30 seconds and keep it in the loop for 30 seconds.
here I have pasted the whole code
import { Observer } from "mobx-react";
import React, { Component, useEffect, useState } from "react";
import {
StyleSheet,
Text,
View,
ImageBackground,
TouchableOpacity,
Image,
Platform,
Dimensions,
Clipboard,
} from "react-native";
import * as Progress from "react-native-progress";
import AppStore from "../../stores/AppStore";
import Iconpack from "../../utils/Iconpack";
import Theme from "../../utils/Theme";
import DeviceInfo from "react-native-device-info";
import CircularProgressBar from "./CircularProgressBar";
import { Shadow } from "react-native-shadow-2";
import Toast from "react-native-simple-toast";
import { BoxShadow } from "react-native-shadow";
import FastImage from "react-native-fast-image";
// import Clipboard from "#react-native-community/clipboard";
const hRem = AppStore.screenHeight / 812;
const wRem = AppStore.screenWidth / 375;
const OtpDetails = ({ route, params, navigation }) => {
const { id, title } = route.params;
const useProgress = (maxTimeInSeconds = 30) => {
const [elapsedTime, setElapsedTime] = useState(0);
const [progress, setProgress] = useState(0);
useEffect(() => {
const intervalId = setInterval((callback) => {
if (progress < 1) {
setElapsedTime((t) => t + 1);
}
}, 1000);
return () => clearInterval(intervalId);
}, []);
useEffect(() => {
console.log(elapsedTime);
setProgress(elapsedTime / maxTimeInSeconds);
if (elapsedTime == 30) {
setProgress(elapsedTime / maxTimeInSeconds);
}
}, [elapsedTime / maxTimeInSeconds]);
return progress;
};
const progress = useProgress();
const setTextIntoClipboard = async () => {
await Clipboard.setString(title);
Toast.show("OTP copied successfully!", 200);
};
const deviceName = DeviceInfo.getModel();
return (
<Observer>
{() => (
<View style={{ flex: 1 }}>
<FastImage
style={styles.container}
source={Iconpack.GLOBAL_BG}
resizeMode={FastImage.resizeMode.cover}
/>
<View style={styles.headerStyle}>
<TouchableOpacity
onPress={() => {
navigation.navigate("OtpScreen");
}}
>
<Image
style={styles.backButton}
source={Iconpack.GLOBAL_BACK_BUTTON}
/>
</TouchableOpacity>
<Text style={styles.headerTitle}>{title}</Text>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity>
<Image
style={[styles.editTrashButton, { marginRight: 19 }]}
source={Iconpack.EDIT_ICON}
/>
</TouchableOpacity>
<TouchableOpacity>
<Image
style={styles.editTrashButton}
source={Iconpack.DELETE_ICON}
/>
</TouchableOpacity>
</View>
</View>
<View style={styles.circleOuter}>
{Platform.OS === "ios" ? (
<View style={styles.circleContainer}>
<Progress.Circle
style={{ alignItems: "center" }}
progress={progress}
// indeterminate={this.state.indeterminate}
size={Dimensions.get("window").width * 0.561}
thickness={10}
borderWidth={0}
endAngle={10}
strokeCap="round"
color={"#354659"}
// indeterminateAnimationDuration={1}
unfilledColor={"#031830"}
/>
</View>
) : (
<Shadow
distance={20}
startColor={"rgba(27, 100, 206, 0.5)"}
radius={210}
safeRender={true}
>
<View style={styles.circleContainer}>
<Progress.Circle
style={{ alignItems: "center", overflow: "hidden" }}
progress={progress}
// indeterminate={this.state.indeterminate}
size={Dimensions.get("window").width * 0.561}
thickness={10}
borderWidth={0}
endAngle={10}
strokeCap="round"
color={"#354659"}
indeterminateAnimationDuration={2000}
unfilledColor={"#031830"}
/>
</View>
</Shadow>
)}
<Text style={[styles.circleItem]}>{title}</Text>
<TouchableOpacity
onPress={() => {
setTextIntoClipboard();
}}
style={styles.copyItem}
>
<Text style={styles.copyText}>TAP TO COPY</Text>
</TouchableOpacity>
</View>
{/* <View style={styles.circleOuter}>
<View style={styles.circleContainer}>
<Text style={styles.circleItem}>title</Text>
</View>
<TouchableOpacity
onPress={() => {
copyToClipboard();
}}
style={styles.copyItem}
>
<Text style={styles.copyText}>TAP TO COPY</Text>
</TouchableOpacity> */}
{/* </View> */}
</View>
)}
</Observer>
);
};
export default OtpDetails;
const styles = StyleSheet.create({
container: {
flex: 1,
position: "absolute",
top: 0,
right: 0,
left: 0,
bottom: 0,
backgroundColor: "#021831",
},
headerStyle: {
flexDirection: "row",
marginTop: Platform.OS === "ios" ? 56 : 40,
marginHorizontal: wRem * 26.66,
justifyContent: "space-between",
alignItems: "center",
},
backButton: {
width: wRem * 12,
height: hRem * 21,
},
editTrashButton: {
width: wRem * 23,
height: hRem * 23,
},
headerTitle: {
...Theme.encodeSansMed2,
color: "#FFFFFF",
fontWeight: "600",
marginLeft: wRem * 50,
},
circleOuter: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
circleContainer: {
borderRadius:
Math.round(
Dimensions.get("window").width + Dimensions.get("window").height
) / 2,
width: Dimensions.get("window").width * 0.56,
height: Dimensions.get("window").width * 0.56,
justifyContent: "center",
backgroundColor: "black",
shadowColor: "rgba(27, 100, 206, 0.5)",
shadowOffset: {
width: 0,
height: 0,
},
shadowOpacity: 30,
shadowRadius: 30,
},
circleItem: {
color: "white",
...Theme.encodeSansMed7,
textAlign: "center",
position: "absolute",
width: "100%",
paddingBottom: 100,
},
copyItem: {
justifyContent: "center",
alignItems: "center",
marginTop: hRem * 64,
},
copyText: {
color: "#3D4756",
...Theme.encodeSansMed4,
},
});
with all styling and the most important thing is I am using third party party library react-native-progress thanks
progress does not need to be a state atom since it's always unambiguously calculable from the elapsed time and max time.
Using the modulo (remainder) operator you can have the observed progress always loop back to 0 after the maximum time.
const useProgress = (maxTimeInSeconds = 30) => {
const [elapsedTime, setElapsedTime] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setElapsedTime((t) => t + 1);
}, 1000);
return () => clearInterval(intervalId);
}, []);
return (elapsedTime % maxTimeInSeconds) / maxTimeInSeconds;
};
const progress = useProgress();
Alternately, if you want slightly more accurate, wallclock-based timing:
const useProgress = (maxTimeInSeconds = 30) => {
const [initialTime] = useState(() => +new Date());
const [counter, setCounter] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setCounter((t) => t + 1);
}, 1000);
return () => clearInterval(intervalId);
}, []);
const elapsedTime = Math.floor(((+new Date()) - initialTime) / 1000);
if (elapsedTime == 30) {
return maxTimeInSeconds;
}
return (elapsedTime % maxTimeInSeconds) / maxTimeInSeconds;
};
This pins the progress start to the component's mount time, and should work better even when setInterval isn't accurate.

Using react-native-track-player to play selected song from flatlist

I can not figure out how I can play selected track/song from flatlist, I already had a playerscreen.js where I can play songs from my array of songs. I want to achieve users searching for songs and play the song from the selected list from the flatlist. Below is my songlist.js
const Songlist = () => {
const [loading, setLoading] = useState(false);
const navigation = useNavigation();
renderFooter = () => {
if (!loading) return null
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: '#CED0CE',
}}
>
<ActivityIndicator animating size='large' />
</View>
)
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '5%',
}}
/>
)
}
return (
<View>
<FlatList
data={songs}
keyExtractor={item => item.id}
renderItem={({ item, index }) => (
<TouchableOpacity
onPress={() => navigation.navigate('Player')}
>
<View
style={{
flexDirection: 'row',
padding: 16,
alignItems: 'center',
}}
>
<Avatar
source={{ uri: item.artwork.thumbnail }}
size='giant'
style={{ marginRight: 16 }}
/>
<Text
category='s1'
style={{color: '#000'}}
>{`${item.title}`}</Text>
</View>
<Text
category='s2'
style={{color: '#999', marginHorizontal: 80, bottom: 20}}
>Genre: {`${item.genre}`}</Text>
</TouchableOpacity>
)}
ItemSeparatorComponent={renderSeparator}
ListFooterComponent={renderFooter}
/>
</View>
);
}
export default Songlist;
This is my initial playerscreen.js
export default function PlayerScreen() {
const navigation = useNavigation();
const scrollX = useRef(new Animated.Value(0)).current;
const slider = useRef(null);
const isPlayerReady = useRef(false);
const index = useRef(0);
const [songIndex, setSongIndex] = useState(0);
const isItFromUser = useRef(true);
// for tranlating the album art
const position = useRef(Animated.divide(scrollX, width)).current;
const playbackState = usePlaybackState();
useEffect(() => {
// position.addListener(({ value }) => {
// console.log(value);
// });
scrollX.addListener(({value}) => {
const val = Math.round(value / width);
setSongIndex(val);
});
TrackPlayer.setupPlayer().then(async () => {
// The player is ready to be used
console.log('Player ready');
// add the array of songs in the playlist
await TrackPlayer.reset();
await TrackPlayer.add(songs);
TrackPlayer.play();
isPlayerReady.current = true;
await TrackPlayer.updateOptions({
stopWithApp: false,
alwaysPauseOnInterruption: true,
capabilities: [
Capability.Play,
Capability.Pause,
Capability.SkipToNext,
Capability.SkipToPrevious,
],
});
//add listener on track change
TrackPlayer.addEventListener(Event.PlaybackTrackChanged, async (e) => {
console.log('song ended', e);
const trackId = (await TrackPlayer.getCurrentTrack()) - 1; //get the current id
console.log('track id', trackId, 'index', index.current);
if (trackId !== index.current) {
setSongIndex(trackId);
isItFromUser.current = false;
if (trackId > index.current) {
goNext();
} else {
goPrv();
}
setTimeout(() => {
isItFromUser.current = true;
}, 200);
}
// isPlayerReady.current = true;
});
//monitor intterupt when other apps start playing music
TrackPlayer.addEventListener(Event.RemoteDuck, (e) => {
// console.log(e);
if (e.paused) {
// if pause true we need to pause the music
TrackPlayer.pause();
} else {
TrackPlayer.play();
}
});
});
return () => {
scrollX.removeAllListeners();
TrackPlayer.destroy();
// exitPlayer();
};
}, []);
// change the song when index changes
useEffect(() => {
if (isPlayerReady.current && isItFromUser.current) {
TrackPlayer.skip(songs[songIndex].id)
.then((_) => {
console.log('changed track');
})
.catch((e) => console.log('error in changing track ', e));
}
index.current = songIndex;
}, [songIndex]);
const exitPlayer = async () => {
try {
await TrackPlayer.stop();
} catch (error) {
console.error('exitPlayer', error);
}
};
const goNext = async () => {
slider.current.scrollToOffset({
offset: (index.current + 1) * width,
});
await TrackPlayer.play();
};
const goPrv = async () => {
slider.current.scrollToOffset({
offset: (index.current - 1) * width,
});
await TrackPlayer.play();
};
const renderItem = ({index, item}) => {
return (
<Animated.View
style={{
alignItems: 'center',
width: width,
transform: [
{
translateX: Animated.multiply(
Animated.add(position, -index),
-100,
),
},
],
}}>
<Animated.Image
source={item.artwork}
style={{width: 320, height: 320, borderRadius: 5}}
/>
</Animated.View>
);
};
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={{height: 320}}>
<Animated.FlatList
ref={slider}
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
data={songs}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: scrollX}}}],
{useNativeDriver: true},
)}
/>
</SafeAreaView>
<TouchableOpacity
style={styles.genreContainer}
onPress={() => navigation.navigate('Genre')}
>
<SimpleLineIcons name="playlist" size={20} color='#fff' />
<Text style={styles.genre}> Genre</Text>
</TouchableOpacity>
<View>
<Text style={styles.title}>{songs[songIndex].title}</Text>
<Text style={styles.artist}>{songs[songIndex].artist}</Text>
</View>
<SliderComp />
<Controller onNext={goNext} onPrv={goPrv} />
</SafeAreaView>
);
}
import React from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar, ScrollView, TouchableOpacity } from 'react-native';
import Sound from 'react-native-sound';
const MusicApp = () => {
const DATA = [
{
title: 'Advertising Local',
isRequire: true,
url: require('./resource/advertising.mp3'),
},
{
title: 'Advertising URL',
url:
'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/advertising.mp3',
},
{
title: 'Hosana',
isRequire: true,
url: require('./Hosana.mp3'),
},
{
title: 'Play aac sound from remote URL',
url:
'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/pew2.aac',
},
{
title: 'Frog wav sound from Local',
isRequire: true,
url: require('./resource/frog.wav'),
},
{
title: 'Frog wav sound from remote URL',
url:
'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/frog.wav',
},
];
let sound;
const playSound =(item, id)=>{
console.log("the url is "+ item.url);
if(item.url !=1){
sound = new Sound(item.url, '', (error, _sound) => {
if (error) {
alert('error' + error.message);
return;
}
stopSound
sound.play(() => {
sound.release();
});
});
}else{
sound = new Sound(item.url, (error, _sound) => {
if (error) {
alert('error' + error.message);
return;
}
stopSound
sound.play(() => {
sound.release();
});
});
}
}
const stopSound =(index)=>{
sound.stop(() => {
console.log('Stop');
});
}
const renderItem = ({ item, index }) => (
<ScrollView style={{flex: 1}}>
<View style={styles.item}>
<Text style={styles.title}> {item.title} </Text>
<TouchableOpacity onPress={()=>playSound(item, index)} style={{padding: 10, backgroundColor: 'blue'}}><Text>Play</Text></TouchableOpacity>
<TouchableOpacity onPress={()=>stopSound(index)} style={{padding: 10, backgroundColor: 'red'}}><Text>Stop</Text></TouchableOpacity>
</View>
</ScrollView>
);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 20,
},
});
export default MusicApp;
NB:
Do yarn add react-native-sound or npm install react-native-sound.
Secondly, there are two sources of music- Remote url and local file path, ensure you modify the local file path appropriately before running

How To Implement Auto Scrolling Video in React Native using JSX

import React, {useState, useEffect, useRef} from 'react';
import {
View,
Text,
StyleSheet,
FlatList,
ScrollView,
Image,
TouchableHighlight,
TextInput,
TouchableOpacity,
Modal,
Dimensions,
SafeAreaView,
} from 'react-native';
import Voice from 'react-native-voice';
import Video from 'react-native-video';
import SimpleLineIcons from 'react-native-vector-icons/SimpleLineIcons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import {videos} from './data';
// code started creating state on every event which will be used
enter center export default function App() {
const [pitch, setPitch] = useState('');
const [error, setError] = useState('');
const [end, setEnd] = useState('');
const [started, setStarted] = useState('');
const [results, setResults] = useState([]);
const [videoData, setVideos] = useState([...videos]);
const [showModal, setShowModal] = useState(false);
const [paused, setPaused] = useState(true);
const [position, setPosition] = useState({start: null, end: null});
const [muted, setMuted] = useState(true);
//Setting callbacks for the process status
useEffect(() => {
Voice.onSpeechStart = onSpeechStart;
Voice.onSpeechEnd = onSpeechEnd;
Voice.onSpeechError = onSpeechError;
Voice.onSpeechResults = onSpeechResults;
return () => {
//destroy the process after switching the screen
Voice.destroy().then(Voice.removeAllListeners);
};
}, []);
const onSpeechStart = (e) => {
//Invoked when .start() is called without error
console.log('onSpeechStart: ', e);
setStarted('√');
};
const onSpeechEnd = (e) => {
//Invoked when SpeechRecognizer stops recognition
console.log('onSpeechEnd: ', e);
setEnd('√');
};
const onSpeechError = (e) => {
//Invoked when an error occurs.
console.log('onSpeechError: ', e);
setError(JSON.stringify(e.error));
};
2. in this part we are matching the word sopken by the user and checking is if the same video is available in our data if it si then the video will come on the top
const onSpeechResults = (e) => {
//Invoked when SpeechRecognizer is finished recognizing
console.log('onSpeechResults: ', e.value[0]);
let vResult = e.value[0];
let small = vResult.toLowerCase();
setResults([small]);
setShowModal(false);
for (let i = 0; i < videoData.length - 1; i++) {
let newArray = [...videos];
if (small == newArray[i].name) {
newArray.splice(0, 0, newArray[i]);
const mainArray = newArray.filter((item, index) => {
if (item.name == small && index != 0) {
} else {
return item;
}
});
setVideos(mainArray);
}
}
};
const startRecognizing = async () => {
console.log('start listenind');
//Starts listening for speech for a specific locale
try {
await Voice.start('en-US');
setPitch('');
setError('');
setStarted('');
setResults([]);
setEnd('');
} catch (e) {
//eslint-disable-next-line
console.error(e);
}
};
const stopRecognizing = async () => {
//Stops listening for speech
try {
await Voice.stop();
} catch (e) {
//eslint-disable-next-line
console.error(e);
}
};
3. making video play n pause is out of axis
onScroll = (event) => {
const scrollPosition = event.nativeEvent.contentOffset.y;
const paused = paused;
const {start, end} = position;
if (scrollPosition < start && scrollPosition > end && paused) {
setPaused(false);
} else if (scrollPosition > start && scrollPosition < end && !paused) {
setPaused(true);
} else if (scrollPosition < end && !paused) {
setPaused(true);
}
};
4. getting the scroll y axis
const threshold = 180;
onVideoLayOut = (event) => {
const {height} = Dimensions.get('window');
const start = -(event.nativeEvent.layout.y - height + threshold);
const end =
event.nativeEvent.layout.y + event.nativeEvent.layout.height - threshold;
// console.log(start, end, 'position');
setPosition((state) => {
return {
...state,
start: start,
end: end,
};
});
console.log(position, 'position1');
};
5.this the render par using faltlist to render data
return (
<View
style={{
flex: 1,
backgroundColor: 'black',
}}>
<View
style={{
height: 70,
alignItems: 'center',
justifyContent: 'space-between',
marginHorizontal: 15,
flexDirection: 'row',
marginVertical: 15,
marginTop: 25,
}}>
<View
style={{
height: 40,
borderRadius: 20,
width: 300,
borderWidth: 1,
borderColor: '#F23C29',
}}>
<TextInput
placeholder="Search ..."
style={{flex: 1, paddingHorizontal: 30, color: 'white'}}
placeholderTextColor="grey"
/>
</View>
<View>
<TouchableOpacity
onPress={() => {
startRecognizing();
setShowModal(true);
}}>
<SimpleLineIcons color={'white'} name="microphone" size={30} />
</TouchableOpacity>
</View>
</View>
<SafeAreaView
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
borderColor: 'orange',
borderWidth: 1,
}}>
<FlatList
data={videoData}
// scrollEventThrottle={16}
onScroll={onScroll}
showsVerticalScrollIndicator={true}
renderItem={({item, index}) => {
return (
<View
key={index}
style={{
height: 250,
marginVertical: 20,
}}>
<Video
source={{uri: item.source}}
onLayout={onVideoLayOut}
muted={true}
resizeMode="cover"
paused={!paused}
style={{
width: '100%',
height: undefined,
aspectRatio: 2,
marginBottom: 200,
marginTop: 300,
}}
/>
<Text
style={{
fontSize: 20,
alignItems: 'center',
marginLeft: 20,
marginTop: 15,
color: 'white',
}}>
{item.title}
</Text>
</View>
);
}}
/>
</SafeAreaView>
{/* <ScrollView style={{flex: 2, width: '100%'}}>
{videoData.map((item, index) => {
return (
<View
key={index}
style={{
height: 250,
marginVertical: 20,
}}>
<Video
source={{uri: item.source}}
paused={false}
rate={1.0}
volume={1.0}
isMuted={false}
resizeMode="contain"
shouldPlay
isLooping
style={{width: '100%', height: undefined, aspectRatio: 2}}
onError={(e) => {
console.log(e, 'video data');
}}
/>
<Text
style={{
fontSize: 20,
alignItems: 'center',
marginLeft: 20,
marginTop: 15,
color: 'white',
}}>
{item.title}
</Text>
</View>
);
})}
</ScrollView> */}
<Modal
visible={showModal}
transparent={true}
onRequestClose={() => {
setShowModal(false);
}}>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.9)',
}}>
<MaterialCommunityIcons
name="microphone-settings"
size={45}
color="white"
/>
<Text
style={{
color: '#fff',
textAlign: 'center',
marginTop: 20,
fontSize: 17,
}}>
We are listening
</Text>
</View>
</Modal>
{/* <View style={{justifyContent: 'center', alignItems: 'center'}}>
<Text>Video</Text>
<View>
<TouchableHighlight
onPress={stopRecognizing}
style={styles.buttonStyle}>
<Text style={styles.buttonTextStyle}>Stop</Text>
</TouchableHighlight>
</View>
<View>
<TouchableHighlight onPress={startRecognizing}>
<SimpleLineIcons name="microphone" size={25} />
</TouchableHighlight>
</View>
{results.map((item, index) => {
return (
<Text key={`result-${index}`} style={styles.textStyle}>
{item}
</Text>
);`enter code here`
})}
<Text>Results</Text>
</View> */}
</View>
);
}
const styles = StyleSheet.create({
backgroundVideo: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});
The question is in my flat list at a time, there are three videos which are render .. I was trying to make the first video play and rest to be in paused state and after a 5 second of interval, the next video should be played
I tried demo which looks like instagram where the video which is in focused is played automatically and rest all are paused
I tried using scroll props and also video-autoscroll lib but coudlnot resolve
the videos are getting render but

How can i get back my data list after searching by words?

i use Flatlist and search by words on renderHeader() function. I can filter but when i delete letters, i couldn't get main list again. I think there is a logic problem but i couln't find after trying something...
i've got a new one when i fixed problem. I tried to fix but i couldn't do that, i should put the problem in front of experts :)
import React, {Component, useState} from 'react'
import {
Text,
StyleSheet,
View,
FlatList,
SafeAreaView,
ScrollView,
Image,
TextInput,
} from 'react-native'
import data from '../../data'
const Flatlistexample = () => {
//main list state
let [list, setList] = useState(data);
//search state
const [search, setSearch] = useState('');
//search filter
searchFilter = text => {
// onChangeText
const newData = list.filter(item => {
const listItem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return listItem.indexOf(text.toLowerCase()) > -1;
})
setList(newData)
}
//search function
renderHeader = () =>{
return (
<View style={styles.seachContainer}>
<TextInput
style={styles.textInput}
placeholder={'Search...'}
value={search}
onChangeText={text => {
//setStates
searchFilter(text)
setSearch(text)
}}></TextInput>
<Text
style={{
alignItems: 'flex-start',
color: 'black',
fontSize: 22,
}}>
{search}
</Text>
</View>
)
}
return (
<SafeAreaView
style={{
flex: 1,
}}>
<FlatList
data={list}
renderItem={({item, index}) => {
return (
<ScrollView>
<SafeAreaView
style={[
styles.container,
{backgroundColor: index % 2 === 0 ? '#fafafa' : '#bbb'},
]}>
<Image style={styles.profile} source={{uri: item.picture}} />
<View style={styles.rightside}>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.company}>{item.company}</Text>
</View>
</SafeAreaView>
</ScrollView>
)
}}
keyExtractor={item => item._id}
//called search function
ListHeaderComponent={renderHeader()}
/>
</SafeAreaView>
)
}
export default Flatlistexample
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
borderBottomWidth: 1,
borderColor: 'gray',
},
profile: {
width: 50,
height: 50,
borderRadius: 25,
marginLeft: 10,
},
rightside: {
marginLeft: 20,
justifyContent: 'space-between',
marginVertical: 5,
},
name: {
fontSize: 22,
marginBottom: 10,
},
searchContainer: {
padding: 10,
borderWidth: 2,
borderColor: 'gray',
},
textInput: {
fontSize: 16,
backgroundColor: '#f9f9f9',
padding: 10,
},
})
Thanks for your help
Filter Data
onSearchText = (value) => {
this.setState({searchText: value})
if(value.trim() == "" || value == null){
this.setState({list: this.state.list}
} else {
let filter = this.state.list.fillter(data => {
// data fillter logic //
return data;
})
this.setState({filterData: filter})
}
Render FlatList
<FlatList
extradata={this.state}
data={searchText ? filterData : list}
/>
I fixed...
How?
My main data state is constant, i'm filtering on data list with filter state. So my data list doesn't change anytime.
import React, {Component, useState} from 'react'
import {
Text,
StyleSheet,
View,
FlatList,
SafeAreaView,
ScrollView,
Image,
TextInput,
} from 'react-native'
import data from '../../data'
const Flatlistexample = () => {
//main list state
let [list, setList] = useState(data)
//search state
const [search, setSearch] = useState('')
//filter state
const [updated, setUpdated] = useState(data)
//search filter
searchFilter = text => {
// onChangeText
if (text) {
const newData = list.filter(item => {
const listItem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return listItem.indexOf(text.toLowerCase()) > -1
})
setUpdated(newData)
}
//search function
renderHeader = () => {
return (
<View style={styles.seachContainer}>
<TextInput
style={styles.textInput}
placeholder={'Search...'}
value={search}
onChangeText={text => {
searchFilter(text)
setSearch(text)
}}></TextInput>
<Text
style={{
alignItems: 'flex-start',
color: 'black',
fontSize: 22,
}}>
{search}
</Text>
</View>
)
}
return (
<SafeAreaView
style={{
flex: 1,
}}>
<FlatList
//i'm showing filter state
data={updated}
renderItem={({item, index}) => {
return (
<ScrollView>
<SafeAreaView
style={[
styles.container,
{backgroundColor: index % 2 === 0 ? '#fafafa' : '#bbb'},
]}>
<Image style={styles.profile} source={{uri: item.picture}} />
<View style={styles.rightside}>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.company}>{item.company}</Text>
</View>
</SafeAreaView>
</ScrollView>
)
}}
keyExtractor={item => item._id}
//called search function
ListHeaderComponent={renderHeader()}
/>
</SafeAreaView>
)
}
export default Flatlistexample
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
borderBottomWidth: 1,
borderColor: 'gray',
},
profile: {
width: 50,
height: 50,
borderRadius: 25,
marginLeft: 10,
},
rightside: {
marginLeft: 20,
justifyContent: 'space-between',
marginVertical: 5,
},
name: {
fontSize: 22,
marginBottom: 10,
},
searchContainer: {
padding: 10,
borderWidth: 2,
borderColor: 'gray',
},
textInput: {
fontSize: 16,
backgroundColor: '#f9f9f9',
padding: 10,
},
})
/*
else if(text.length > uzunluk){
setList(data)
const newData = list.filter(item => {
const listItem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return listItem.indexOf(text.toLowerCase()) > -1;
})
setList(newData)
}else if(text.length<uzunluk){
setList(data)
const newData = list.filter(item => {
const listItem = `${item.name.toLowerCase()} ${item.company.toLowerCase()}`
return listItem.indexOf(text.toLowerCase()) > -1;
})
setList(newData)
}
*/

Categories

Resources