gpt4 book ai didi

My event in useAnimatedGestureHandler don't reload in function of layout, I don't know why?(我的事件在useAnimatedGestureHandler中没有重新加载布局函数,我不知道为什么?)

转载 作者:bug小助手 更新时间:2023-10-24 21:13:52 26 4
gpt4 key购买 nike



I want to create a system of widgets that are draggable and interchangeable with each other

我想创建一个相互可拖动和互换的小部件系统


I managed to make them draggable and interchangeable unfortunately when the view that I am dragging is interchanged, the view shifts relative to the pointer because the react native "css" updates well and therefore changes the point origin of the translation is at the new place of the view but not the translation of the event in onActive of useAnimatedGestureHandler and therefore this creates this offset

我设法将它们设置为可拖动和可互换的。不幸的是,当我正在拖动的视图互换时,视图相对于指针移动,因为反应本机“css”更新良好,因此更改了转换的点原点是在视图的新位置,而不是事件的转换在onActive的新位置,因此这会创建此偏移量


Home.js

Home.js


import React from 'react';
import {Dimensions, StyleSheet, Text, TouchableHighlight, View} from 'react-native';
import {DraggableView} from "./components/DraggableView";
import {Icon} from "@rneui/themed";
import DraggableContainer from "./components/DraggableContainer";

export default function Home({navigation}) {
return (
<>
<View style={styles.container}>
<Text style={styles.title}>Home</Text>
<TouchableHighlight>
<Icon name="plus" type='ant-design' size={24}/>
</TouchableHighlight>
</View>
<DraggableContainer onDrag={(isNotDragging) => {
navigation.setOptions({swipeEnabled: isNotDragging});
}}>
<DraggableView>
<View style={[styles.box2, {backgroundColor: "#b5cbb7"}]}/>
</DraggableView>
<DraggableView>
<View style={[styles.box, {backgroundColor: "#d2e4c4"}]}/>
</DraggableView>
<DraggableView>
<View style={[styles.box, {backgroundColor: "#818479"}]}/>
</DraggableView>
<DraggableView>
<View style={[styles.box3, {backgroundColor: "#9ba898"}]}/>
</DraggableView>
<DraggableView>
<View style={[styles.box, {backgroundColor: "#e4e9b2"}]}/>
</DraggableView>
<DraggableView>
<View style={[styles.box, {backgroundColor: "#dbe7bb"}]}/>
</DraggableView>
<DraggableView>
<View style={[styles.box, {backgroundColor: "#c4d8be"}]}/>
</DraggableView>
</DraggableContainer>
</>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 10,
minHeight: 50,
},
title: {
fontSize: 26,
fontFamily: 'Inter-Regular'
},
box: {
width: Dimensions.get('window').width / 2.2,
height: Dimensions.get('window').width / 2.2,
borderRadius: 10,
zIndex: 0,
},
box2: {
width: Dimensions.get('window').width / 1.05,
height: Dimensions.get('window').width / 2.2,
borderRadius: 10,
zIndex: 0,
},
box3: {
width: Dimensions.get('window').width / 1.05,
height: Dimensions.get('window').width / 1.05,
borderRadius: 10,
zIndex: 0,
}
})

DraggableContainer.js

DraggableContainer.js


import React, {useRef, useState} from 'react';
import {Dimensions, ScrollView, StyleSheet, View} from "react-native";
import {GestureHandlerRootView} from "react-native-gesture-handler";

export default function DraggableContainer({children, onDrag}) {
const [scrollEnabled, setScrollEnabled] = useState(0);
const [childrenIds, setChildrenIds] = useState(children.map((_, index) => index));
const childrenLayouts = useRef(children.map((_) => ({x: 0, y: 0, height: 0, width: 0})));

const DraggableManagement = {
onLongPress: () => {
console.log("onLongPress")
if (onDrag) onDrag(scrollEnabled !== 0)
setScrollEnabled((prev) => ++prev)
},
onRelease: () => {
console.log("onRelease")
if (onDrag) onDrag(scrollEnabled !== 0)
setScrollEnabled((prev) => --prev)
},
onChangePosition: (id, position, afterOrBefore) => {
setChildrenIds((prev) => {
prev = prev.filter((item) => item !== id);
const index = prev.indexOf(position);
if (afterOrBefore === 'before') {
prev = [...prev.slice(0, index), id, ...prev.slice(index)];
} else {
prev = [...prev.slice(0, index + 1), id, ...prev.slice(index + 1)];
}
return prev;
})
},
onLayout: (id, layout) => {
childrenLayouts.current[id] = layout;
},
}



return (
<>
<ScrollView
scrollEnabled={scrollEnabled === 0}
>
<GestureHandlerRootView style={styles.gestureHandler}>
<View style={styles.container}>

{childrenIds.map((index) => {
return (React.cloneElement(children[index], {
...DraggableManagement,
id: index,
key: index,
childrenLayouts: childrenLayouts.current,
}))
})
}
</View>
</GestureHandlerRootView>
</ScrollView>
</>
)
}

const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
padding: 10,
gap: 10,
},
gestureHandler: {
flex: 1,
minHeight: Dimensions.get('window').height
},
absoluteFill: {}
})

DraggableView.js

DraggableView.js


import React, {useEffect, useState} from 'react';
import {PanGestureHandler, TapGestureHandler} from 'react-native-gesture-handler';
import Animated, {
Easing, Layout,
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withDelay,
withRepeat,
withSequence,
withSpring,
withTiming,
} from 'react-native-reanimated';
import * as Haptics from 'expo-haptics';
import {Dimensions, Text, TouchableHighlight, View} from 'react-native';
import {BlurView} from "expo-blur";


const ANGLE = 1.5;
const TIME = 100;
const EASING = Easing.elastic(0.5);
const ELASTIC = Easing.elastic(1.9);

function DraggableView({children, style, onLongPress, onRelease, onChangePosition, onLayout, childrenLayouts, id}) {
const x = useSharedValue(0);
const y = useSharedValue(0);
const [longPress, setLongPress] = useState([]);
const [draggable, setDraggable] = useState(false);
const [position, setPosition] = useState(0);
const [menu, setMenu] = useState(false);
const rotate = useSharedValue(0);
const scale = useSharedValue(1);

const openMenu = () => {
setMenu(true);
}

const closeMenu = () => {
setMenu(false);
}

const onPressGesture = (event) => {
if (event.nativeEvent.state === 2) {
setLongPress([event.nativeEvent.absoluteX, event.nativeEvent.absoluteY]);
}
if (longPress.length === 2 && event.nativeEvent.state === 1 &&
event.nativeEvent.absoluteX === longPress[0] &&
event.nativeEvent.absoluteY === longPress[1] && !menu && !draggable) {
if (onLongPress) onLongPress()
openMenu();
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
const y = event.nativeEvent.absoluteY;
if (y > Dimensions.get('window').height / 2) {
setPosition(1);
} else {
setPosition(0);
}
}
if (event.nativeEvent.state === 1
|| event.nativeEvent.state === 4
|| event.nativeEvent.state === 5) {
setLongPress([]);
}
}

const scaleRotateStyles = useAnimatedStyle(() => ({
transform: [{
scale: scale.value,
},
{
rotateZ: `${rotate.value}deg`
}],
}));

useEffect(() => {
rotate.value = 0;
if (draggable) {
scale.value = 1;
rotate.value =
withSequence(
withTiming(-ANGLE, {duration: TIME / 2, easing: EASING}),
withRepeat(
withTiming(ANGLE, {
duration: TIME,
easing: EASING,
}),
-1,
true
),
withTiming(0, {duration: TIME / 2, easing: EASING})
);
}
}, [draggable]);

useEffect(() => {
if (draggable) {
scale.value = 1;
return
}
if (longPress.length !== 2 && !menu) {
scale.value = 1;
return;
}
scale.value =
withDelay(150, withTiming(1.02, {duration: 350, easing: ELASTIC}))
}, [longPress, menu]);

const collision = (id) => {
const XLeft = x.value + childrenLayouts[id].x;
const YTop = y.value + childrenLayouts[id].y;
const XLeftCenter = XLeft + childrenLayouts[id].width / 3;
const XRightCenter = XLeft + childrenLayouts[id].width * 2 / 3;
const YTopCenter = YTop + childrenLayouts[id].height / 3;
const YBotCenter = YTop + childrenLayouts[id].height * 2 / 3;

childrenLayouts.forEach((item, index) => {
if (index === id) return;

const XLeftItem = item.x;
const YTopItem = item.y;
const XCenterItem = item.x + item.width / 2;
const XRightItem = item.x + item.width;
const YBotItem = item.y + item.height;
if (XLeftItem < XLeftCenter && XLeftCenter < XCenterItem && YTopItem < YTopCenter && YTopCenter < YBotItem) {
if (onChangePosition) onChangePosition(id, index, "before");
}
if (XRightItem > XRightCenter && XRightCenter > XCenterItem && YTopItem < YBotCenter && YBotCenter < YBotItem) {
if (onChangePosition) onChangePosition(id, index, "after");
}
});
}

const panGestureEvent = useAnimatedGestureHandler({
onStart: (_, context) => {
context.x = x.value;
context.y = y.value;
},
onActive: (event, context) => {
const translationX = context.x + event.translationX;
const translationY = context.y + event.translationY;

x.value = translationX;
y.value = translationY;
runOnJS(collision)(id, context)
},
onEnd: () => {
x.value = withSpring(0, {
dampingRatio: 1,
stiffness: 100,
overshootClamping: false,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 2});
y.value = withSpring(0, {
dampingRatio: 1,
stiffness: 100,
overshootClamping: false,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 2});
},
onFinish: () => {
runOnJS(onRelease)();
runOnJS(setDraggable)(false);
runOnJS(setMenu)(false);
}
}, [x, y]);

const panStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: x.value,
},
{
translateY: y.value,
}
],
};
}, [x, y]);

return (
<Animated.View style={[{zIndex: menu || draggable ? 10 : 0}, scaleRotateStyles]} onLayout={(event) => {
let layout = {};
layout.x = event.nativeEvent.layout.x;
layout.y = event.nativeEvent.layout.y;
layout.width = event.nativeEvent.layout.width;
layout.height = event.nativeEvent.layout.height;
onLayout(id, layout);
}} layout={Layout.duration(200)}>
{
menu && (<TouchableHighlight onPress={() => {
closeMenu();
if (onRelease) onRelease();
}}><BlurView intensity={25} style={styles.absoluteFill}/>
</TouchableHighlight>)
}
<TapGestureHandler
onHandlerStateChange={onPressGesture}
maxDurationMs={500}
>
<Animated.View>
<PanGestureHandler onGestureEvent={panGestureEvent} enabled={draggable}>
<Animated.View
style={[
panStyle,
{...style},
]}>
{children}
</Animated.View>
</PanGestureHandler>
</Animated.View>
</TapGestureHandler>
{
menu &&
<View style={{...styles.menu, ...(position ? {bottom: "102%"} : {top: "102%"})}}>
<TouchableHighlight onPress={() => {
closeMenu();
setDraggable(true);
}}>
<Text style={{
color: "white",
textAlign: "center",
}}>Move widget</Text>
</TouchableHighlight>
</View>
}
</Animated.View>
);
}

const styles = {
menu: {
position: "absolute",
backgroundColor: 'rgba(95, 97, 99, 0.95)',
height: 120,
width: 210,
borderRadius: 30,
zIndex: 10,
alignSelf: "center",
},
absoluteFill: {
zIndex: 0,
position: 'absolute',
top: -Dimensions.get('window').height,
left: -Dimensions.get('window').width,
width: Dimensions.get('window').width * 2,
height: Dimensions.get('window').height * 2
},
}

export {DraggableView};

I tried to use the new Gesture from react-native-gesture-handler
with GestureDetector and Gesture.Pan()
but unfortunately I encounter the same behavior

我尝试通过GestureDetector和Gesture.pann()使用来自REACTIVE-Native-Sigure-Handler的新手势,但不幸的是我遇到了相同的行为


I tried to recalculate the offset and remove it during the translation thanks to the onLayout on the first view but this gives a glitch effect

由于第一个视图上的onLayout,我尝试重新计算偏移量并在平移过程中将其删除,但这会产生毛刺效果


I tried to add a state in the dependencies of useAnimatedGestureHandler which updates when colliding between two views but that doesn't change anything

我尝试在useAnimatedGestureHandler的依赖项中添加一个状态,该状态在两个视图之间发生冲突时更新,但这不会改变任何事情


更多回答
优秀答案推荐
更多回答

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com