スクロールの大枠の最適化
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
import { FC } from "react";
|
import { FC, useRef, useState, useCallback } from "react";
|
||||||
import { View, Text, ScrollView, useWindowDimensions } from "react-native";
|
import { View, Text, ScrollView, useWindowDimensions } from "react-native";
|
||||||
import { ExGridViewItem } from "./ExGridViewItem";
|
import { ExGridViewItem } from "./ExGridViewItem";
|
||||||
import Animated, {
|
import Animated, {
|
||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
|
runOnJS,
|
||||||
} from "react-native-reanimated";
|
} from "react-native-reanimated";
|
||||||
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
||||||
|
|
||||||
@@ -31,28 +32,114 @@ export const ExGridView: FC<{
|
|||||||
// ドラッグ位置を保持する共有値
|
// ドラッグ位置を保持する共有値
|
||||||
const widthX = useSharedValue(width);
|
const widthX = useSharedValue(width);
|
||||||
const savedWidthX = useSharedValue(width);
|
const savedWidthX = useSharedValue(width);
|
||||||
|
const [scrollEnabled, setScrollEnabled] = useState(true);
|
||||||
|
const scrollRef = useRef<Animated.ScrollView>(null);
|
||||||
|
const [contentScrollPos, setContentScrollPos] = useState(0);
|
||||||
|
|
||||||
|
// ScrollViewの有効/無効を切り替える関数
|
||||||
|
const toggleScrollEnabled = useCallback((enabled: boolean) => {
|
||||||
|
setScrollEnabled(enabled);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// パンジェスチャー(ドラッグ)のハンドラー
|
// パンジェスチャー(ドラッグ)のハンドラー
|
||||||
const pinchGesture = Gesture.Pinch()
|
const pinchGesture = Gesture.Pinch()
|
||||||
.onUpdate((e) => {
|
.onUpdate((e) => {
|
||||||
const calc = savedWidthX.value * e.scale;
|
const calc = savedWidthX.value * e.scale;
|
||||||
widthX.value = calc > width ? calc : width;
|
widthX.value = calc > width ? calc : width;
|
||||||
|
//runOnJS(scrollToRightEnd)();
|
||||||
})
|
})
|
||||||
.onEnd(() => {
|
.onEnd(() => {
|
||||||
savedWidthX.value = widthX.value;
|
savedWidthX.value = widthX.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const gesture = Gesture.Pan()
|
||||||
|
.minPointers(2) // 最低2本指
|
||||||
|
.maxPointers(2) // 最大2本指
|
||||||
|
.onTouchesDown((e) => {
|
||||||
|
if (e.numberOfTouches >= 2) runOnJS(toggleScrollEnabled)(false);
|
||||||
|
})
|
||||||
|
.onTouchesUp((e) => {
|
||||||
|
runOnJS(toggleScrollEnabled)(true);
|
||||||
|
})
|
||||||
|
.onEnd((e) => {
|
||||||
|
runOnJS(toggleScrollEnabled)(true);
|
||||||
|
});
|
||||||
|
// ジェスチャーを組み合わせる
|
||||||
|
const composed = Gesture.Simultaneous(pinchGesture, gesture);
|
||||||
|
|
||||||
// アニメーションスタイル
|
// アニメーションスタイル
|
||||||
const animatedStyle = useAnimatedStyle(() => ({
|
const animatedStyle = useAnimatedStyle(() => ({
|
||||||
width: widthX.value,
|
width: widthX.value,
|
||||||
}));
|
}));
|
||||||
return (
|
return (
|
||||||
<GestureDetector gesture={pinchGesture}>
|
<GestureDetector gesture={composed}>
|
||||||
<ScrollView horizontal nestedScrollEnabled pinchGestureEnabled={false}>
|
<Animated.ScrollView
|
||||||
|
horizontal
|
||||||
|
nestedScrollEnabled
|
||||||
|
pinchGestureEnabled={false}
|
||||||
|
scrollEnabled={scrollEnabled}
|
||||||
|
onScroll={(d) => {
|
||||||
|
setContentScrollPos(d.nativeEvent.contentOffset.x);
|
||||||
|
}}
|
||||||
|
onContentSizeChange={(d) => {
|
||||||
|
if (d < contentScrollPos) {
|
||||||
|
scrollRef.current?.scrollToEnd();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
ref={scrollRef}
|
||||||
|
contentContainerStyle={{
|
||||||
|
flexDirection: "column",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
{
|
||||||
|
backgroundColor: "white",
|
||||||
|
width: width - 50,
|
||||||
|
flexDirection: "row",
|
||||||
|
},
|
||||||
|
animatedStyle,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{Array.from({ length: 60 }, (_, i) => i + 1).map((num) => {
|
||||||
|
if (num % 5 === 0) {
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
key={num}
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
textAlign: "center",
|
||||||
|
borderRightWidth: 0.5,
|
||||||
|
borderColor: "#ccc",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{num - 5}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
} else return <></>;
|
||||||
|
})}
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
textAlign: "center",
|
||||||
|
borderRightWidth: 0.5,
|
||||||
|
borderColor: "#ccc",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
fontSize: 12,
|
||||||
|
width: 50
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
(分)
|
||||||
|
</Text>
|
||||||
|
</Animated.View>
|
||||||
<Animated.ScrollView
|
<Animated.ScrollView
|
||||||
style={[{ backgroundColor: "white", width: width }, animatedStyle]}
|
style={[{ backgroundColor: "white", width: width }, animatedStyle]}
|
||||||
pinchGestureEnabled={false}
|
pinchGestureEnabled={false}
|
||||||
minimumZoomScale={0.5}
|
minimumZoomScale={0.5}
|
||||||
maximumZoomScale={3.0}
|
maximumZoomScale={3.0}
|
||||||
|
scrollEnabled={scrollEnabled}
|
||||||
stickyHeaderIndices={
|
stickyHeaderIndices={
|
||||||
groupKeys.at(0) ? groupKeys.map((_, i) => i * 2) : []
|
groupKeys.at(0) ? groupKeys.map((_, i) => i * 2) : []
|
||||||
}
|
}
|
||||||
@@ -68,7 +155,8 @@ export const ExGridView: FC<{
|
|||||||
}}
|
}}
|
||||||
key={hour}
|
key={hour}
|
||||||
>
|
>
|
||||||
<Text style={{ fontSize: 15 }}>{hour}時台</Text>
|
<Text style={{ fontSize: 15, zIndex: 1, backgroundColor: 'white', marginLeft: contentScrollPos }}>{hour}時台</Text>
|
||||||
|
|
||||||
</View>,
|
</View>,
|
||||||
<View style={{ flexDirection: "row", position: "relative" }}>
|
<View style={{ flexDirection: "row", position: "relative" }}>
|
||||||
{groupedData[hour].map((d, i) => (
|
{groupedData[hour].map((d, i) => (
|
||||||
@@ -82,7 +170,7 @@ export const ExGridView: FC<{
|
|||||||
</View>,
|
</View>,
|
||||||
])}
|
])}
|
||||||
</Animated.ScrollView>
|
</Animated.ScrollView>
|
||||||
</ScrollView>
|
</Animated.ScrollView>
|
||||||
</GestureDetector>
|
</GestureDetector>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user