I am working with react native reanimated 2
To make animations with opacity
This example works as well
const width = useSharedValue(50);
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: width.value,
};
});
return <Animated.View style={[styles.box, animatedStyle]} />;
But when I want to calculate any value that does not work
I get an error
const width = useSharedValue(50);
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: cond(greaterThan(width.value, 10), 1, 0),
};
});
return <Animated.View style={[styles.box, animatedStyle]} />;
I get errors like that
cannot read property 'onFrame' on null
How can I calculate the value in 'useAmimationStyle'?
You are using reanimated v1 functions on the v2. You could do:
const width = useSharedValue(50);
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: width.value > 10 ? 1 :0
};
});
return <Animated.View style={[styles.box, animatedStyle]} />;
But the issue with this is that opacity wont really be animated; it would just instantaneously go to 1 or 0 when width.value got around 10. To get animation like opacity changes use interpolate:
const width = useSharedValue(50);
const animatedStyle = useAnimatedStyle(() => {
return {
width:width.value,
opacity: interpolate(
width.value,
//input range
[0, 10, 50],
// output ramge
[0, 0, 1]
),
};
});
return <Animated.View style={[styles.box, animatedStyle]} />;
Demo
Related
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
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.
I build slider for my project, the slider works fine but every time that I adjusting the dragging (with the Circle element) the circle jumps to his initial state let translateX = sub(x, 50 / 2);
Please help me understand what I need to add in order that the circle will continue from his last place.
I tried to overwrite translateX in onDrop function but it's not working ->
() =>
cond(
eq(state, State.ACTIVE),
call([translateX], ([v]) => {
onDrop(v);
}),
),
[state, value],
);
const onDrop = (e) => {
const diff = e - -firstNumber;
const lastOption = diff * offSet;
setTextValue((lastOption / 10).toFixed(2));
};
my code:
const width = Dimensions.get('window').width
const SLIDER_WIDTH = width - 200;
let offSet = 1000 / SLIDER_WIDTH;
let firstNumber = SLIDER_WIDTH - 25;
const [state, translationX] = useValues(
State.UNDETERMINED,
(-SLIDER_WIDTH + 100) / 2,
);
const gestureHandler = onGestureEvent({state, translationX});
const x = diffClamp(withOffset(translationX, state), -SLIDER_WIDTH + 50, 50);
let translateX = sub(x, 50 / 2);
const [textValue, setTextValue] = useState(50);
const value = round(multiply(divide(x, SLIDER_WIDTH), 100));
useCode(
() =>
cond(
eq(state, State.ACTIVE),
call([translateX], ([v]) => {
onDrop(v);
}),
),
[state, value],
);
const onDrop = (e) => {
const diff = e - -firstNumber;
const lastOption = diff * offSet;
setTextValue((lastOption / 10).toFixed(2));
};
return (
<View>
<PanGestureHandler
minDist={0}
{...gestureHandler}
onHandlerStateChange={chosenValue}>
<Animated.View
style={{
position: 'absolute',
top: 0,
left: 0,
width: CIRCLE_SIZE,
height: CIRCLE_SIZE,
transform: [{translateX}],
}}>
<Animated.View
style={{
...StyleSheet.absoluteFillObject,
}}>
<Circle {...{state}} />
</Animated.View>
</Animated.View>
</PanGestureHandler>
</View>
);
Thanks in advance.
I have an autoscrolling carousel in React Native and everything is working fine with images scrolling through both automatically every X seconds and manually.
The problem is when I move away from that screen that I get the following error:
Here's is my full code...
const { width, height } = Dimensions.get("window");
let flatList;
function infiniteScroll(dataList) {
const numberOfData = dataList.length;
let scrollValue = 0,
scrolled = 0;
setInterval(function () {
scrolled++;
if (scrolled < numberOfData) scrollValue = scrollValue + width;
else {
scrollValue = 0;
scrolled = 0;
}
this.flatList.scrollToOffset({ animated: true, offset: scrollValue });
}, 3000);
}
const Carousel = (props) => {
const topTenVideos = useSelector(getTopTenVideos);
const dispatch = useDispatch();
const scrollX = new Animated.Value(0);
let position = Animated.divide(scrollX, width);
const [dataList, setDataList] = useState(topTenVideos);
const isFocused = useIsFocused();
useEffect(() => {
if (isFocused) {
setDataList(topTenVideos);
infiniteScroll(dataList);
}
}, [isFocused]);
const renderRow = (itemData) => {
return (
<CarouselItem
id={itemData.item.id}
img={itemData.item.poster}
title={itemData.item.title}
/>
);
};
return (
<View style={styles.screen}>
<FlatList
ref={(flatList) => {
this.flatList = flatList;
}}
horizontal
data={dataList}
pagingEnabled
scrollEnabled
snapToAlignment="center"
scrollEventThrottle={16}
decelerationRate={"fast"}
showsHorizontalScrollIndicator={false}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { x: scrollX } } },
])}
keyExtractor={(item, index) => "key" + index}
renderItem={renderRow}
/>
<View style={styles.dotView}>
{dataList.map((_, i) => {
let opacity = position.interpolate({
inputRange: [i - 1, i, i + 1],
outputRange: [0.3, 1, 0.3],
extrapolate: "clamp",
});
return (
<Animated.View
key={i}
style={{
opacity,
height: 8,
width: 8,
borderRadius: 6,
backgroundColor: "white",
margin: 8,
}}
/>
);
})}
</View>
</View>
);
};
It's complaining about this line this.flatList.scrollToOffset({ animated: true, offset: scrollValue });
}, 3000); which is inside my infiniteScroll function.
It looks like that when the screen loses focus, it is still searching for this.flatList.scrollToOffset.
You are not creating your ref properly, You have to use useRef hook if you are using functional component or createRef in the case of class component for making refs in your component
Have a look at this.
https://reactjs.org/docs/hooks-reference.html#useref
Create your ref like below.
const flatListRef = useRef(null)
<FlatList
ref={flatListRef}
// other props
/>
flatListRef.current.scrollToOffset({ animated: true, offset: scrollValue }) // access like this.
I am trying to auto-scroll between four images using the useRef hook which identifies the target using a state value called 'selected'.
I am getting unexpected behaviour in that the auto-scroll is erratic, usually leaving out the third image it should be scrolling to.
const [selected, setSelected] = useState(0);
const scrollRef = useRef(null);
const setSelectedIndex = e => {
const viewSize = e.nativeEvent.layoutMeasurement.width;
const contentOffset = e.nativeEvent.contentOffset.x;
const selectedIndex = Math.floor(contentOffset / viewSize);
setSelected(selectedIndex);
}
useEffect(() => {
setInterval(() => {
setSelected(prev => prev + 1);
scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * selected
});
}, 10000);
}, [selected]);
return (
<View style={{ height: '100%', width: '100%' }}>
<ScrollView
horizontal
pagingEnabled
onMomentumScrollEnd={setSelectedIndex}
ref={scrollRef}
>
{images.map(image => (
<Image
key={image}
source={image}
style={styles.backgroundImage}
/>
))}
</ScrollView>
<View style={styles.circleDiv}>
{
images.map((img, i) => (
<View
key={img}
style={[styles.whiteCircle, { opacity: i === selected ? 0.5 : 1 }]}
/>
))
}
</View>
</View>
);
If it helps, I have built it using componentDidMount which works as expected.
scrollRef = createRef();
componentDidMount = () => {
setInterval(() => {
this.setState({
prev => ({ selected: prev.selected + 1 }),
() => {
this.scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * this.state.selected
});
}
});
}, 5000);
}
try like this,
use useRef() instead of createRef()
return new state from setSelected arrow function
use setTimeout instead of setInterval
const scrollRef = useRef()
useEffect(() => {
setTimeout(() => {
setSelected((prev) => (
prev == slidesArray.length - 1 ? 0 : prev + 1
))
if (scrollRef.current) {
scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * (selected +1)
});
}
}, 5000);
}, [selected])