代码
拖拽小按钮
import React from "react";
import { Animated, PanResponder } from "react-native";
import { FontAwesome5 } from "@expo/vector-icons";
import styled from "styled-components";
let iconTranslate = new Animated.ValueXY({ x: 0, y: 0 }); // 打开菜单的图标的位移
let prevY = 0;
const DragIcon = ({ setOpenMenu }) => {
const _panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderGrant: () => {
console.log('grant')
},
onPanResponderMove:(evt, gestureState) => {
iconTranslate.setValue({
x:gestureState.dx,
y:gestureState.dy + prevY
})
},
// onPanResponderMove: Animated.event(
// [
// null, // 忽略原始事件
// { dx: iconTranslate.x },
// { dy: iconTranslate.y },
// ], // 手势状态参数
// { useNativeDriver: false } // 可选的异步监听函数
// ),
onPanResponderRelease:(evt, gestureState) => {
prevY = gestureState.dy;
Animated.spring(iconTranslate,{
toValue:{
x:0,
y:gestureState.dy
},
useNativeDriver: false
}).start()
},
});
return (
<AnimatedRadiusBorder
{..._panResponder.panHandlers}
style={{
transform: [
{ translateX: iconTranslate.x },
{ translateY: iconTranslate.y },
],
}}
>
<FontAwesome5
onPress={() => setOpenMenu((x) => true)}
name="grip-horizontal"
size={28}
color="white"
/>
</AnimatedRadiusBorder>
);
};
export default DragIcon;
const RadiusBorder = styled.View`
width: 50px;
height: 50px;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
overflow: hidden;
position: absolute;
top: 100px;
background-color: #51f;
padding: 10px;
`;
const AnimatedRadiusBorder = Animated.createAnimatedComponent(RadiusBorder);
展开菜单
import React, { useEffect } from "react";
import { Text, Animated, TouchableWithoutFeedback } from "react-native";
import styled from "styled-components";
import { width, height, model } from "../../../constants/Layout";
import { AntDesign } from "@expo/vector-icons";
import { setStatusBarHidden } from "expo-status-bar";
import { Avatar } from 'react-native-paper'
let layout = new Animated.ValueXY({ x: 0, y: 0 });// 整体的宽高
let opacity = new Animated.Value(0);// 整体的透明度
let smallRound = new Animated.Value(height * 0.6);// 小的扇形宽度
let bigRound = new Animated.Value(height * 0.8); // 大的扇形宽度
let RoundHeight = new Animated.Value(height * 0.75);// 两个扇形一致的高度
const AwesomeAnimated = ({ openMenu, setOpenMenu }) => {
useEffect(() => {
open(openMenu);
}, [openMenu]);
function open(isOpen) {
if (isOpen) {
Animated.spring(layout, {
toValue: { x: height, y: height + 100 },
useNativeDriver: false,
}).start();
Animated.spring(opacity, {
toValue: 1,
useNativeDriver: false,
}).start();
Animated.timing(smallRound, {
toValue: height * 0.6,
duration: 300,
useNativeDriver: false,
}).start();
Animated.timing(bigRound, {
toValue: height * 0.85,
duration: 400,
useNativeDriver: false,
}).start();
} else {
Animated.spring(layout, {
toValue: { x: 0, y: 0 },
useNativeDriver: false,
}).start();
Animated.spring(opacity, {
toValue: 0,
useNativeDriver: false,
}).start();
Animated.spring(smallRound, {
toValue: 0,
useNativeDriver: false,
}).start();
Animated.spring(bigRound, {
toValue: 0,
useNativeDriver: false,
}).start();
}
if (model === "ios") {
setStatusBarHidden(openMenu);
}
}
return (
<AnimatedContainer
style={{
width: layout.y,
height: layout.y,
opacity,
}}
>
<AnimatedRoundView
style={{
width: bigRound,
height: RoundHeight,
}}
/>
<AnimatedRoundView
style={{
width: smallRound,
height: RoundHeight,
}}
bgc="#00009f"
/>
<ContentContainer>
<Avatar.Image style={{
position: 'absolute',
top: 40,
right: 30
}} size={50} source={require('../../../assets/0.jpg')} />
<Headline>Home</Headline>
<Headline>My Trips</Headline>
<Headline>Trip Summary</Headline>
<Headline>Wallet</Headline>
<Headline>Setting</Headline>
<Headline>Feedback</Headline>
<Headline>Layout</Headline>
<View>
<TouchableWithoutFeedback onPress={() => setOpenMenu((x) => false)}>
<AntDesign
name="closecircle"
size={50}
color="#51f"
/>
</TouchableWithoutFeedback>
</View>
</ContentContainer>
</AnimatedContainer>
);
};
export default AwesomeAnimated;
const View = styled.View`
width: 100%;
height: 30%;
position: absolute;
bottom: 0;
justify-content: center;
align-items: center;
`;
const Headline = styled.Text`
color: #fff;
padding: 10px;
font-size: 28px;
padding-left: 40px;
`;
const RoundView = styled.View`
background-color: ${(props) => props.bgc || "#04f"};
border-bottom-right-radius: ${(props) => props.radius || height}px;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
`;
const AnimatedRoundView = Animated.createAnimatedComponent(RoundView);
const ContentContainer = styled.View`
width: ${width}px;
height: ${height}px;
position: absolute;
top: 0;
left: 0%;
z-index: 3;
padding-top: 20px;
`;
const Container = styled.View`
position: absolute;
left: 0px;
background-color: #42f;
z-index: 9;
/* justify-content: center;
align-items: center; */
border-bottom-right-radius: ${height}px;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.8);
`;
const AnimatedContainer = Animated.createAnimatedComponent(Container);
父容器
import React, { useEffect, useState } from "react";
import { View, Text } from "react-native";
import AwesomeAnimatedMenu from "./components/AwesomeAnimatedMenu";
import DragIcon from './components/DragIcon'
const UITest = (props) => {
const [openMenu, setOpenMenu] = useState(false);
useEffect(() => {
},[]);
return (
<View
style={{
position: "relative",
}}
>
<DragIcon setOpenMenu={setOpenMenu} />
<AwesomeAnimatedMenu openMenu={openMenu} setOpenMenu={setOpenMenu} />
</View>
);
};
export default UITest;