Skip to content

Instantly share code, notes, and snippets.

@guschnwg
Created May 13, 2020 02:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guschnwg/1e02f73b534149855c138fe94a6ce98e to your computer and use it in GitHub Desktop.
Save guschnwg/1e02f73b534149855c138fe94a6ce98e to your computer and use it in GitHub Desktop.
import React from 'react'
import {StyleSheet, TouchableOpacity} from 'react-native'
import {useSafeArea} from 'react-native-safe-area-context'
import Icon from 'react-native-vector-icons/FontAwesome'
import {WIDTH} from '../../config/constants'
import Items from './Items'
const Menu: React.FC<Props> = ({open = false, setOpen}) => {
const insets = useSafeArea()
return (
<>
<TouchableOpacity
activeOpacity={1}
style={{
...styles.menu,
bottom: insets.bottom + 15,
}}
onPress={() => setOpen(!open)}
>
<Icon name="home" size={30} color="black" />
</TouchableOpacity>
<Items open={open} onPress={() => setOpen(false)} />
</>
)
}
const styles = StyleSheet.create({
menu: {
position: 'absolute',
bottom: 0,
left: WIDTH / 2 - 30,
height: 60,
width: 60,
borderRadius: 30,
backgroundColor: 'yellow',
alignItems: 'center',
justifyContent: 'center',
zIndex: 2,
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.5,
shadowRadius: 5,
elevation: 5,
},
})
interface Props {
open: boolean
setOpen: (open: boolean) => void
}
export default Menu
--
import React from 'react'
import Item from './Item'
const ITEMS = [
{
title: '1',
icon: 'home',
route: 'PollinationCreate',
},
{
title: '2',
icon: 'home',
route: 'PollinationCreate',
},
{
title: '3',
icon: 'home',
route: 'PollinationCreate',
},
{
title: '4',
icon: 'home',
route: 'PollinationCreate',
},
{
title: '5',
icon: 'home',
route: 'PollinationCreate',
},
]
const getAngle = (position: number, angle: number) =>
((90 + position * angle) * Math.PI) / 180
const getPosition = (position: number, radius: number, angle: number) => {
const itemAngle = getAngle(position, angle)
return {
x: Math.cos(itemAngle) * radius,
y: Math.sin(itemAngle) * radius,
}
}
const Items: React.FC<Props> = ({open, onPress}) => {
const median = (ITEMS.length - 1) / 2
return (
<>
{ITEMS.map((item, i) => (
<Item
key={i}
index={i}
items={ITEMS.length}
open={open}
title={item.title}
route={item.route}
position={getPosition(i - median, -80, 45)}
onPress={onPress}
/>
))}
</>
)
}
interface Props {
open: boolean
onPress: () => void
}
export default Items
--
import React, {useEffect, useState} from 'react'
import {Animated, TouchableOpacity, Text, StyleSheet} from 'react-native'
import {useNavigation} from '@react-navigation/native'
import {useSafeArea} from 'react-native-safe-area-context'
import {WIDTH} from '../../config/constants'
const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity)
const getDelay = (open: boolean, items: number, index: number) =>
open ? index * 50 : (items - index) * 50
const getToValue = (open: boolean, position: {x: number; y: number}) =>
open ? {scale: 1, transform: position} : {scale: 0, transform: {x: 0, y: 0}}
const Item: React.FC<Props> = ({
index,
items,
open,
title,
route,
position,
onPress,
}) => {
const inset = useSafeArea()
const [translateValue] = useState(new Animated.ValueXY({x: 0, y: 0}))
const [scaleValue] = useState(new Animated.Value(0))
const {navigate} = useNavigation()
useEffect(() => {
const toValue = getToValue(open, position)
const delay = getDelay(open, items, index)
Animated.parallel([
Animated.timing(translateValue, {
toValue: toValue.transform,
duration: 150,
delay,
useNativeDriver: true,
}),
Animated.timing(scaleValue, {
toValue: toValue.scale,
duration: 300,
delay,
useNativeDriver: true,
}),
]).start()
}, [translateValue, scaleValue, index, items, open, position])
return (
<AnimatedTouchable
activeOpacity={1}
style={{
...styles.item,
bottom: inset.bottom + 20,
transform: [
{
translateX: translateValue.x,
},
{
translateY: translateValue.y,
},
{
scaleX: scaleValue,
},
{
scaleY: scaleValue,
},
],
}}
onPress={() => {
navigate(route)
onPress()
}}
>
<Text>{title}</Text>
</AnimatedTouchable>
)
}
const styles = StyleSheet.create({
item: {
borderRadius: 20,
position: 'absolute',
backgroundColor: 'yellow',
height: 40,
width: 40,
left: WIDTH / 2 - 20,
zIndex: 1,
alignItems: 'center',
justifyContent: 'center',
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.3,
shadowRadius: 5,
elevation: 3,
},
})
interface Props {
index: number
items: number
open: boolean
title: string
route: string
position: {x: number; y: number}
onPress: () => void
}
export default Item
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment