-
-
Save wcandillon/288e994cda3216d1dbef9482a9322804 to your computer and use it in GitHub Desktop.
import React from "react"; | |
import { | |
StyleSheet, | |
Text, | |
View, | |
ScrollView, | |
Animated, | |
SafeAreaView, | |
Dimensions | |
} from "react-native"; | |
const cardHeight = 250; | |
const cardTitle = 45; | |
const cardPadding = 10; | |
const { height } = Dimensions.get("window"); | |
const cards = [ | |
{ | |
name: "Shot", | |
color: "#a9d0b6", | |
price: "30 CHF" | |
}, | |
{ | |
name: "Juice", | |
color: "#e9bbd1", | |
price: "64 CHF" | |
}, | |
{ | |
name: "Mighty Juice", | |
color: "#eba65c", | |
price: "80 CHF" | |
}, | |
{ | |
name: "Sandwich", | |
color: "#95c3e4", | |
price: "85 CHF" | |
}, | |
{ | |
name: "Combi", | |
color: "#1c1c1c", | |
price: "145 CHF" | |
}, | |
{ | |
name: "Signature", | |
color: "#a390bc", | |
price: "92 CHF" | |
}, | |
{ | |
name: "Coffee", | |
color: "#fef2a0", | |
price: "47 CHF" | |
} | |
]; | |
export default class App extends React.Component { | |
state = { | |
y: new Animated.Value(0) | |
}; | |
render() { | |
const { y } = this.state; | |
return ( | |
<SafeAreaView style={styles.root}> | |
<View style={styles.container}> | |
<View style={StyleSheet.absoluteFill}> | |
{cards.map((card, i) => { | |
const inputRange = [-cardHeight, 0]; | |
const outputRange = [ | |
cardHeight * i, | |
(cardHeight - cardTitle) * -i | |
]; | |
if (i > 0) { | |
inputRange.push(cardPadding * i); | |
outputRange.push((cardHeight - cardPadding) * -i); | |
} | |
const translateY = y.interpolate({ | |
inputRange, | |
outputRange, | |
extrapolateRight: "clamp" | |
}); | |
return ( | |
<Animated.View | |
key={card.name} | |
style={{ transform: [{ translateY }] }} | |
> | |
<View | |
style={[styles.card, { backgroundColor: card.color }]} | |
/> | |
</Animated.View> | |
); | |
})} | |
</View> | |
<Animated.ScrollView | |
scrollEventThrottle={16} | |
contentContainerStyle={styles.content} | |
showsVerticalScrollIndicator={false} | |
onScroll={Animated.event( | |
[ | |
{ | |
nativeEvent: { | |
contentOffset: { y } | |
} | |
} | |
], | |
{ useNativeDriver: true } | |
)} | |
/> | |
</View> | |
</SafeAreaView> | |
); | |
} | |
} | |
const styles = StyleSheet.create({ | |
root: { | |
flex: 1, | |
margin: 16 | |
}, | |
container: { | |
flex: 1 | |
}, | |
content: { | |
height: height * 2 | |
}, | |
card: { | |
height: cardHeight, | |
borderRadius: 10 | |
} | |
}); |
Can anyone help me please, i trying add onpress function in each card
is there any sample code for implementing the on click event on the cards using touchable opacity
This is kinda useless because theres noway to click the cards, if you do an overlay touchable opacity and map the coordinates, it works only when they are stacked in the original state, if theres too many cards and you scroll up to minimise the stack then your coordinates change. Just seems very hacky to make clickable items work this way
anyway
with this hack, i have it working, see video and rough code below
https://user-images.githubusercontent.com/1423413/132573698-20f7f5c5-65bf-44d4-b24e-dd263fcc7fab.mov
import React, { useState, useEffect } from 'react'
import {
StyleSheet,
Dimensions,
TouchableOpacity,
View,
StatusBar,
Animated,
SafeAreaView,
Text,
} from 'react-native'
const cardHeight = 250
const cardTitle = 45
const cardPadding = 10
const cards = [
{
name: 'Shot',
color: '#EFF6FF',
price: '30 CHF',
},
{
name: 'Juice',
color: '#DBEAFE',
price: '64 CHF',
},
{
name: 'Mighty Juice',
color: '#BFDBFE',
price: '80 CHF',
},
{
name: 'Sandwich',
color: '#93C5FD',
price: '85 CHF',
},
{
name: 'Combi',
color: '#60A5FA',
price: '145 CHF',
},
{
name: 'Signature',
color: '#3B82F6',
price: '92 CHF',
},
{
name: 'Coffee',
color: '#2563EB',
price: '47 CHF',
},
]
const { height } = Dimensions.get('window')
const Page = ({ navigation }) => {
const [selected, setSelected] = useState(null)
const [space, setSpace] = useState([])
const [coor, setCoor] = useState([])
const [y] = useState(new Animated.Value(0))
useEffect(() => {
const cardVals = cards.map((card, i, arr) => {
const start = cardPadding * i
const end = (cardHeight - cardPadding) * -i
return {
start,
end,
}
})
const coord = cardVals.map((item, i, arr) => {
const start = cardTitle * i
let end = start + cardTitle
if (i === arr.length - 1) {
end += cardHeight - cardTitle
}
return {
y1: start,
y2: end,
anim: new Animated.Value((cardHeight - cardTitle) * -i),
}
})
setCoor(coord)
setSpace(cardVals)
}, [])
return (
<View style={styles.wrapper}>
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<TouchableOpacity
onPress={() => {
coor.forEach(
(item, i) => {
Animated.spring(coor[i].anim, {
bounciness: 4,
toValue: (cardHeight - cardTitle) * -i,
duration: 500,
useNativeDriver: true,
}).start()
},
() => {
setSelected(null)
}
)
}}
>
{selected && <Text>Done</Text>}
</TouchableOpacity>
<View style={styles.container}>
<View style={StyleSheet.absoluteFill}>
{!!space.length &&
cards.map((card, i) => {
const inputRange = [-cardHeight, 0]
const outputRange = [
cardHeight * i,
(cardHeight - cardTitle) * -i,
]
if (i > 0) {
inputRange.push(space[i].start)
outputRange.push(space[i].end)
}
const translateY = y.interpolate({
inputRange,
outputRange,
extrapolateRight: 'clamp',
})
if (selected) {
if (selected === i) {
return (
<Animated.View
key={card.name}
style={{
...styles.card,
backgroundColor: card.color,
transform: [
{
translateY: coor[i].anim,
},
],
}}
>
<Text>card {i}</Text>
</Animated.View>
)
} else {
return (
<Animated.View
key={card.name}
style={{
...styles.card,
backgroundColor: card.color,
transform: [
{
translateY: coor[i].anim,
},
],
}}
>
<Text>card {i}</Text>
</Animated.View>
)
}
}
return (
<Animated.View
key={card.name}
style={{
...styles.card,
opacity: 1,
backgroundColor: card.color,
transform: [{ translateY }],
}}
>
<Text>card {i}</Text>
</Animated.View>
)
})}
</View>
<Animated.ScrollView
scrollEventThrottle={16}
contentContainerStyle={styles.content}
showsVerticalScrollIndicator={false}
onScroll={Animated.event(
[
{
nativeEvent: {
contentOffset: { y },
},
},
],
{ useNativeDriver: true }
)}
>
<TouchableOpacity
onPress={(evt) => {
const posY = evt.nativeEvent.locationY
coor.forEach(({ y1, y2 }, i) => {
if (posY >= y1 && posY <= y2) {
setSelected(i)
Animated.spring(coor[i].anim, {
toValue: (cardHeight - cardTitle) * -i - i * cardTitle,
duration: 300,
useNativeDriver: true,
}).start()
} else {
Animated.spring(coor[i].anim, {
toValue: 800,
duration: 500,
useNativeDriver: true,
}).start()
}
})
}}
style={[
{
height: cards.length * 75,
},
]}
/>
</Animated.ScrollView>
</View>
</View>
</SafeAreaView>
</View>
)
}
const styles = StyleSheet.create({
wrapper: {
flex: 1,
},
container: {
flex: 1,
paddingTop: StatusBar.currentHeight,
marginHorizontal: 16,
},
Text: {
color: '#fff',
fontSize: 30,
textAlign: 'center',
letterSpacing: 2,
textShadowColor: 'rgba(0, 0, 0, 1)',
textShadowOffset: { width: 1, height: 1 },
textShadowRadius: 2,
marginBottom: 16,
},
content: {
height,
},
card: {
height: cardHeight,
borderRadius: 10,
padding: 8,
},
})
export default Page
how can i make the items inside the card clickable, the TouchableOpacity is not working inside the card
I did like this and it worked
<SafeAreaView style={styles.root}>
<View style={styles.container}>
<Animated.ScrollView
scrollEventThrottle={16}
contentContainerStyle={styles.content}
showsVerticalScrollIndicator={false}
onScroll={Animated.event(
[
{
nativeEvent: {
contentOffset: { y }
}
}
],
{ useNativeDriver: true }
)}
>
<View style={StyleSheet.absoluteFill}>
{cards.map((card, i) => {
const inputRange = [-cardHeight, 0];
const outputRange = [
cardHeight * i,
(cardHeight - cardTitle) * -i
];
if (i > 0) {
inputRange.push(cardPadding * i);
outputRange.push((cardHeight - cardPadding) * -i);
}
const translateY = y.interpolate({
inputRange,
outputRange,
extrapolateRight: "clamp"
});
return (
<Animated.View
key={card.name}
style={{ transform: [{ translateY }] }}
>
<View
style={[styles.card, { backgroundColor: card.color }]}
/>
</Animated.View>
);
})}
</View>
</ Animated.ScrollView>
</View>
</SafeAreaView>
@1ik, To make items clickable you need to know the position of each card and emulate a click event by handling a click on the overlay.