react native reanimated three items but one bigger as the neighbor - react-native

I want to make a flatlist with three items and the selected/current Index item should be bigger as the two items next to.
Current Code:
const translateX = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler({
onScroll: (e) => {
translateX.value = e.contentOffset.x;
}
})
const animatedStyle = useAnimatedStyle(() => {
const scale = interpolate(translateX.value, [width / 3, 0, width / 3], [0.2, 1, 0.2], Extrapolate.CLAMP);
return {
transform: [{
scale
}]
}
}, []);
const renderI = ({ item }) => (
<Animated.View style={[animatedStyle]}>
<ProductCardAuto
{...item}
onPressProduct={() => handleNavigateToProduct(item)}
onPressReviews={handleOpenModalReviews}
/>
</Animated.View>
)
<Animated.FlatList
data={mockProducts}
renderItem={renderI}
keyExtractor={(item) => item.id}
snapToAlignment="start"
decelerationRate={"normal"}
onScroll={scrollHandler}
horizontal
snapToInterval={120}
/>
I am very thankful for your help. Idk what I am doing wrong. Maybe you can tell me what is wrong with it. it does not work its scale all items to 0.2 if I scroll

Even if the question has been already answered, I would suggest another approach.
What worked for me is react-native-reanimated-carousel, which not only that is very easy to use and very customizable, but is also using react-native-reanimated and that means native performance and precise animations because it runs the animations on the UI thread rather than the JS thread.
EDIT: I noticed that you're actually looking for a reanimated answer, which is great!
Based on the library I showed you, I believe you're looking for a parallax horizontal.
Here's a link to the example: https://github.com/dohooo/react-native-reanimated-carousel/blob/main/exampleExpo/src/pages/parallax/index.tsx

kindof tried to make it acc to what you needed.
You can check this expo : https://snack.expo.dev/d_myr1HdyN
import React,{useState,useEffect,useRef,useCallback} from 'react';
import { Text, View, StyleSheet ,Animated,FlatList,Dimensions,Easing } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
const data = ["hey there","indian","mango","whatsup","arsenal","jojoba"]
const screenWidth = Dimensions.get("window").width;
const EachItem = (props) => {
const {text = "",index=0,currentView={}} = props || {}
const {visible = false, index:visibleIndex = 0} = currentView;
console.log(visible,visibleIndex,"wuhyyyyy")
const animRef = useRef(new Animated.Value(0)).current
const startAnimation = useCallback(() => {
Animated.timing(animRef,{
toValue:1,
duration:1000,
useNativeDriver:true,
easing:Easing.ease,
}).start()
},[animRef])
const stopAnim = useCallback(() => {
Animated.timing(animRef,{
toValue:0,
duration:1000,
useNativeDriver:true,
easing:Easing.ease,
}).start()
},[animRef])
useEffect(() => {
if(visible === true && visibleIndex===index ){
startAnimation()
}
if(visible === false ){
setTimeout(() =>stopAnim(),1000)
}
},[startAnimation,visible,visibleIndex,index,stopAnim])
const scaleInter = animRef.interpolate({inputRange:[0,1],outputRange:[1,1.5]})
return(
<Animated.View style={{marginHorizontal:20 ,height:100,
backgroundColor:'rgba(102,116,255,0.6)',
width:screenWidth/3,
alignItems:'center',
justifyContent:'center',
transform:[{
scale:scaleInter
}]
}} >
<Text>{text}</Text>
</Animated.View>
)
}
export default function App() {
const [currentView,setCurrentView] = useState({visible:false,index:null})
const onViewRef = React.useRef((viewableItems) => {
console.log(viewableItems?.viewableItems,"viewableItems")
if(viewableItems?.viewableItems.length === 2){
setCurrentView({visible:false,index:null})
}
if(viewableItems?.viewableItems.length === 1){
const currentItemIndex = viewableItems?.viewableItems[0]?.index
setCurrentView({visible:true,index:currentItemIndex})
}
});
const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 30 });
const eachItem = ({item,index}) => {
return <EachItem text={item} currentView={currentView} index={index} />
}
return (
<View style={styles.container}>
<Animated.FlatList
data={data}
style={{flex:1}}
contentContainerStyle={{paddingTop:100}}
horizontal={true}
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
renderItem={eachItem}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
// justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
// alignItems:'center'
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
Hope it helps. feel free for doubts

Related

Changed from react native class to function and it doesnt work 100%

I am trying to change a class into a function. This is mainly because if I can get it working, I want to use it for learning different animations, I have had some success but not 100%. Originally it displayed an icon that when clicked it spun it one way and then when clicked again it spun the other way. What I have tried to do it get rid of the icon and replace it with an image. It works when clicked once but then does nothing.
I am struggling with toggled aspect of it and setting the state I think because I cant seem to set it up properly in a function.
I have tried several things but this is the best I can get. If I show the original code and then what I have managed to change, maybe someone can point me in the right direction as to what I am doing wrong.
All I want is the image to display and then when clicked spins right and then if clicked again it spins left.
I am doing this so I can mess around with the settings and hopefully learn animation a bit better.
Any help would be greatly appreciated.
The original code :
import React from 'react';
import { View, StyleSheet, Animated, Image, TouchableOpacity } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
const TabIcon = ({
onPress,
menuToggled
}) => {
const logoStyles = [styles.logoStyle];
if (menuToggled !== null) {
const animation = new Animated.Value(menuToggled ? 0 : 1);
Animated.timing(animation, {
toValue: menuToggled ? 1 : 0,
duration: 500,
useNativeDriver: true
}).start();
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
}
return (
<TouchableOpacity
style={styles.tabStyle}
onPress={onPress}
>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/></Animated.View>
</TouchableOpacity>
);
};
export default class App extends React.Component {
state = {
menuToggled: null
}
toggleMenu = () => {
this.setState(prevState => {
return { menuToggled: !prevState.menuToggled };
});
}
render () {
return (
<View style={styles.container}>
<TabIcon
onPress={this.toggleMenu}
menuToggled={this.state.menuToggled}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
and what I have changed so far :
import React, { useRef, useState } from "react";
import { View, StyleSheet, Animated, Image, TouchableOpacity, Easing } from 'react-native';
import Constants from 'expo-constants';
const App = () => {
const spinValue = useRef(new Animated.Value(0)).current;
const [menuToggled, setMenuToggled] = useState([null]);
toggleMenu = () => {
setMenuToggled(menuToggled === "null" ? "menuToggled" : "null");
}
const Spinner = ({
onPress,
menuToggled
}) => {
const logoStyles = [styles.logoStyle];
const animation = new Animated.Value(0);
const go = () => {
Animated.timing(animation, {
toValue: 1,
duration: 1500,
easing: Easing.elastic(1),
useNativeDriver: true
}).start();
}
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
return (
<TouchableOpacity
onPress={go}
>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/></Animated.View>
</TouchableOpacity>
);
};
return (
<View style={styles.container}>
<Spinner
onPress={toggleMenu}
menuToggled={menuToggled}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
export default App;
There are a few issues. You first had menuToggled initialized to [null] when it should have been null. You also had forgotten to use onPress in TabIcon. The most noteworthy thing was wrapping TabIcon in a useCallback to prevent it from being recreated all the time. Expo snack:
import React, { useRef, useState, useCallback } from 'react';
import {
View,
StyleSheet,
Animated,
Image,
TouchableOpacity,
} from 'react-native';
import Constants from 'expo-constants';
const App = () => {
const spinValue = useRef(new Animated.Value(0)).current;
const [menuToggled, setMenuToggled] = useState(null);
const TabIcon = useCallback(({ onPress, menuToggled }) => {
const logoStyles = [styles.logoStyle];
// initialized base on menuToggled
// if not done then it will take an additional button press to trigger
// the animation
const animation = useRef(new Animated.Value(menuToggled ? 0 : 1)).current;
const startAnimation = () => {
Animated.timing(animation, {
toValue: menuToggled ? 1 :0,
duration: 500,
useNativeDriver: true,
}).start();
};
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
return (
<TouchableOpacity
onPress={() => {
startAnimation();
onPress?.();
}}>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/>
</Animated.View>
</TouchableOpacity>
);
},[]);
return (
<View style={styles.container}>
<TabIcon
onPress={() => setMenuToggled((prev) => !prev)}
menuToggled={menuToggled}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
export default App;

I want to make a bottom bar

I want to make lower under the bar.But I don't know how to make. Press the lower bar to help me seyeo can get a page, I want I want to make.And I want to put the image that I want in the bottom bar Tell me what to do first I need your help I searched hard on Google, but there is no code similar to mine, so I keep getting it wrong or weird
import React, { useRef, useState, useCallback, useEffect } from 'react';
import {
View,
BackHandler,
Platform,
StyleSheet,
ActivityIndicator,
} from 'react-native';
import { WebView } from 'react-native-webview';
import { Image } from "react-native";
const DELAY_BEFORE_WEBVIEW = 10; // <--- seconds before webview load
export default function App() {
// ref
const webView = useRef();
const [canGoBack, setCanGoBack] = useState(false);
const handleBack = useCallback(() => {
if (canGoBack && webView.current) {
webView.current.goBack();
return true;
}
return false;
}, [canGoBack]);
// effects
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', handleBack);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleBack);
};
}, [handleBack]);
useEffect(() => {
setTimeout(() => {
setIsLoading(false);
}, 1000 * DELAY_BEFORE_WEBVIEW);
}, []);
// states
const [isLoading, setIsLoading] = useState(true);
return (
<View style={styles.container}>
<WebView
ref={webView}
source={{ uri: 'https://www.talesrunnerbestguild.co.kr/' }}
style={styles.webView}
onLoadProgress={(event) => setCanGoBack(event.nativeEvent.canGoBack)}
/>
{isLoading && <CenterLoader />}
</View>
);
}
const CenterLoader = () => (
<View style={styles.loaderContainer}>
<Image source={require('/workspace/talesrunner23/assets/js34.png/')}
style={{height:115,width:90}}/>
</View>
);
const styles = StyleSheet.create({
container: { flex: 1 },
loaderContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
width: '100%',
height: '100%',
backgroundColor:'white' // <-- comment this to show webview while loading
},
webView:
Platform.OS === 'ios'
? { marginTop: 30, marginBottom: 40 }
: { marginTop: 30 },
});

React Native Oval Scroll

How can I do a similar oval scroll?
What can I use for this?
Based on the assumption that you want something like this, I wrote a simple example
If someday the link turns out to be broken, below I attach the code additionally
import React, { useCallback, useState, useRef } from "react";
import {
FlatList,
Text,
View,
StyleSheet,
Dimensions,
Animated
} from "react-native";
const { height } = Dimensions.get("window");
const screenMiddle = height / 2;
const itemScaleOffset = height / 3;
const DATA = new Array(20).fill(0).map((...args) => ({
id: args[1],
title: args[1]
}));
// args[1] is an index, just I hate warnings
const Item = ({ title, offsetY }) => {
const [scrollEdges, setScrollEdges] = useState({
top: 0,
middle: 0,
bottom: 0
});
const onLayout = useCallback(
({
nativeEvent: {
layout: { top, height }
}
}) =>
setScrollEdges((edges) => ({
...edges,
top: top - itemScaleOffset - screenMiddle,
middle: top + height / 2 - screenMiddle,
bottom: top + height + itemScaleOffset - screenMiddle
})),
[]
);
const scale = offsetY.interpolate({
inputRange: [scrollEdges.top, scrollEdges.middle, scrollEdges.bottom],
outputRange: [0.66, 1, 0.66],
extrapolate: "clamp"
});
return (
<Animated.View
onLayout={onLayout}
style={[
{
transform: [
{
scale
}
]
},
styles.item
]}
>
<Text style={styles.title}>{title}</Text>
</Animated.View>
);
};
const keyExtractor = ({ id }) => id.toString();
const App = () => {
const offsetY = useRef(new Animated.Value(0)).current;
const renderItem = useCallback(
({ item: { title } }) => <Item title={title} offsetY={offsetY} />,
[offsetY]
);
return (
<View style={styles.app}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={keyExtractor}
onScroll={Animated.event(
[
{
nativeEvent: {
contentOffset: {
y: offsetY
}
}
}
],
{
useNativeDriver: false
}
)}
/>
</View>
);
};
const styles = StyleSheet.create({
app: {
flex: 1
},
item: {
backgroundColor: "#f9c2ff",
padding: 20,
marginVertical: 8,
marginHorizontal: 16
},
title: {
fontSize: 32
}
});
export default App;
I think you should use Reanimated 2 who has a very easy sintaxis and also very powerful. Maybe in combination with RNGestureHandler.

React-Native Animated Accordion/ Drawer/ Drop-down/ Collapsible-card

I want to implement an animated accordion list/ drawer / drop-down menu / collapsible card.
The animation should be performant and look like this:
After a lot of searching, I could find many libraries. But I wanted to implement it without any library. Also, some tutorials showed how to build one, but they were not performant.
Finally, this is how I implemented it. The complete snack code is here: https://snack.expo.dev/#vipulchandra04/a85348
I am storing isOpen (whether the menu is open or closed) in a state. Then changing that state on button press. I am using the LayoutAnimation API in React-Native to animate the opening and closing of the list. LayoutAnimation runs the animation natively, thus it is performant.
const Accordion = ({ title, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleOpen = () => {
setIsOpen(value => !value);
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}
return (
<>
<TouchableOpacity onPress={toggleOpen} activeOpacity={0.6}>
{title}
</TouchableOpacity>
<View style={[styles.list, !isOpen ? styles.hidden : undefined]}>
{children}
</View>
</>
);
};
const styles = StyleSheet.create({
hidden: {
height: 0,
},
list: {
overflow: 'hidden'
},
});
With this, it will fix the Vipul's demo's error: if click accordion so fast, children's opacity descending to 0. and add animation for icon
import {
Animated,
LayoutAnimation,
Platform,
StyleProp,
StyleSheet,
UIManager,
View,
ViewStyle,
} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons;
if (
Platform.OS === 'android' &&
UIManager.setLayoutAnimationEnabledExperimental
) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
const toggleAnimation = duration => {
return {
duration: duration,
update: {
property: LayoutAnimation.Properties.scaleXY,
type: LayoutAnimation.Types.easeInEaseOut,
},
delete: {
property: LayoutAnimation.Properties.opacity,
type: LayoutAnimation.Types.easeInEaseOut,
},
};
};
interface IAccordion {
title?: JSX.Element | JSX.Element[];
children?: JSX.Element | JSX.Element[];
style?: StyleProp<ViewStyle> | undefined;
}
const Accordion = ({title, children, style}: IAccordion) => {
const [isOpen, setIsOpen] = useState(false);
const animationController = useRef(new Animated.Value(0)).current;
const arrowTransform = animationController.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '90deg'],
});
const onToggle = () => {
setIsOpen(prevState => !prevState);
const duration = 300;
const config = {
duration: duration,
toValue: isOpen ? 0 : 1,
useNativeDriver: true,
};
Animated.timing(animationController, config).start();
LayoutAnimation.configureNext(toggleAnimation(duration));
};
return (
<View style={style ? style : styles.accordion}>
<TouchableOpacity onPress={onToggle} style={styles.heading}>
{title}
<Animated.View style={{transform: [{rotateZ: arrowTransform}]}}>
<Ionicons name={'chevron-forward-outline'} size={18} />
</Animated.View>
</TouchableOpacity>
<View style={[styles.list, !isOpen ? styles.hidden : undefined]}>
{children}
</View>
</View>
);
};
export default Accordion;
I had difficulty using the native API, so I go to third parties. The only thing I couldn't do was make the accordion size automatic.
import { useEffect } from 'react';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
Easing,
} from 'react-native-reanimated';
import styled from 'styled-components';
const Accordion = ({ children, open, height }) => {
const heightAnimation = useSharedValue(0);
useEffect(() => {
if (open === true) heightAnimation.value = height;
if (open === false) heightAnimation.value = 0;
}, [open]);
const animatedStyle = useAnimatedStyle(() => {
return {
height: withTiming(heightAnimation.value, {
duration: 500,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
}),
};
});
return (
<>
<Main style={animatedStyle}>{children}</Main>
</>
);
};
const Main = styled(Animated.View)`
width: 100%;
align-items: center;
justify-content: center;
overflow: hidden;
`;
export default Accordion;
Using:
<Accordion height={height} open={open}>
{children}
</Accordion>
As asked here for an example of what I managed to do with it, I tried to get as much out of it as possible.
You can see a deploy here: https://snack.expo.dev/#francisco.ossian/accordion
Libs used, react-native-reanimated

How to play list of videos sequentially by default without clicking next button accessing from local files of the device using react native cli

const filePath = `${dirs.DownloadDir}/samplevideos/1.mp4`;
const filePath1 = `${dirs.DownloadDir}/samplevideos/2.mp4`;
const paths = [{path:filePath},{path:filePath1}]
{paths&&paths.map((data,inx)=>{
return <Video key={inx} source={{uri:data.path }}
volume={50}
resizeMode="cover"
style={styles.videoStyle}
/>
})}
I have tried this but playing only last video. Any help will be appreciated.
I found solution for this question, So posting here it may help someone in the future.
there is a onEnd callback available in react-native-video based on that increasing index of next video
import React,{useEffect,useState} from 'react';
import {
StyleSheet,
View,
Image,
Text,
Dimensions,
PermissionsAndroid
} from 'react-native';
import Video from 'react-native-video';
import RNFetchBlob from 'rn-fetch-blob';
const FILE_PATHS = `${RNFetchBlob.fs.dirs.DownloadDir}/samplevideos/`;
const App = () => {
const [videoPaths,setVideosPath] = useState([]);
const [inxofCurrentVideo,setVideoIndex] = useState(0);
useEffect(() => {
PermissionsAndroid.RESULTS.GRANTED;
getVideoPaths();
},[])
const getVideoPaths = ()=>{
RNFetchBlob.fs.ls(FILE_PATHS).then(files => {
setVideosPath(files);
}).catch(error => console.log(error))
};
const onEnd =()=> {
if((inxofCurrentVideo < videoPaths.length) || (videoPaths.length === inxofCurrentVideo)){
setVideoIndex(inxofCurrentVideo+1);
}
}
return (
<View style={styles.videoContainer}>
<View style={styles.row}>
{videoPaths.length>0&&<Video onEnd = {onEnd}
source={{uri:(FILE_PATHS + videoPaths[inxofCurrentVideo])}}
volume={50}
resizeMode="cover"
style={styles.videoStyle}
/> }
</View>
</View>
);
};
const styles = StyleSheet.create({
videoContainer: {
flex: 1,
// backgroundColor: 'black',
},
row: {
flex: 1,
flexDirection: "row",
width: '100%'
},
col6: {
width: "50%"
},
videoStyle: {
position: 'absolute',
top: 0,
bottom: 5,
left: 0,
right: 0,
},
});
export default App;