メニューの地図機能の仮作成

This commit is contained in:
harukin-expo-dev-env 2025-04-13 10:35:08 +00:00
parent 44f8be994e
commit 934938287d
2 changed files with 208 additions and 366 deletions

View File

@ -1,5 +1,10 @@
import React, { useEffect } from "react";
import React, { useEffect, useRef } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { useWindowDimensions, Platform } from "react-native";
import Constants from "expo-constants";
import { Dimensions, StatusBar } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { AS } from "./storageControl";
import TrainBase from "./components/trainbaseview";
@ -13,10 +18,13 @@ import AllTrainDiagramView from "./components/AllTrainDiagramView";
import { useCurrentTrain } from "./stateBox/useCurrentTrain";
import { useNavigation } from "@react-navigation/native";
import { news } from "./config/newsUpdate";
import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs";
const Stack = createStackNavigator();
export function MenuPage() {
const { favoriteStation, setFavoriteStation } = useFavoriteStation();
const { height, width } = useWindowDimensions();
const tabBarHeight = useBottomTabBarHeight();
const { getCurrentTrain } = useCurrentTrain();
const navigation = useNavigation();
const { addListener } = navigation;
@ -42,8 +50,20 @@ export function MenuPage() {
})
.catch((error) => console.error("Error fetching icon setting:", error));
}, []);
const scrollRef = useRef(null);
const MapHeight =
height -
tabBarHeight +
(Platform.OS == "android" ? Constants.statusBarHeight : 0) -
100 -
((((width / 100) * 80) / 20) * 9 + 10 + 30);
useEffect(() => {
const unsubscribe = addListener("tabPress", (e) => {
scrollRef.current.scrollTo({
y: MapHeight - 80,
animated: true,
});
AS.getItem("favoriteStation")
.then((d) => {
const returnData = JSON.parse(d);
@ -65,7 +85,9 @@ export function MenuPage() {
gestureEnabled: true,
headerTransparent: true,
}}
children={() => <Menu getCurrentTrain={getCurrentTrain} />}
children={() => (
<Menu getCurrentTrain={getCurrentTrain} scrollRef={scrollRef} />
)}
/>
<Stack.Screen name="news" options={optionData} component={News} />
<Stack.Screen

548
menu.js
View File

@ -4,7 +4,6 @@ import {
Platform,
View,
ScrollView,
Linking,
Text,
TouchableOpacity,
LayoutAnimation,
@ -18,50 +17,50 @@ import {
} from "react-native-reanimated";
import StatusbarDetect from "./StatusbarDetect";
import { Ionicons } from "@expo/vector-icons";
import LottieView from "lottie-react-native";
import LED_vision from "./components/発車時刻表/LED_vidion";
import Sign from "./components/駅名表/Sign";
import { TitleBar } from "./components/Menu/TitleBar";
import { FixedContentBottom } from "./components/Menu/FixedContentBottom";
import { UsefulBox } from "./components/atom/UsefulBox";
import { lineList } from "./lib/getStationList";
import useInterval from "./lib/useInterval";
import { HeaderConfig } from "./lib/HeaderConfig";
import { useFavoriteStation } from "./stateBox/useFavoriteStation";
import { SheetManager } from "react-native-actions-sheet";
import { useTrainDelayData } from "./stateBox/useTrainDelayData";
import { useNavigation } from "@react-navigation/native";
import { useStationList } from "./stateBox/useStationList";
import { StationNumber } from "./components/Menu/StationPagination";
import lineColorList from "./assets/originData/lineColorList";
import { AS } from "./storageControl";
import { SimpleDot } from "./components/Menu/SimpleDot";
import { useAllTrainDiagram } from "./stateBox/useAllTrainDiagram";
import { TopMenuButton } from "@/components/Menu/TopMenuButton";
import { JRSTraInfoBox } from "@/components/Menu/JRSTraInfoBox";
import MapView from "react-native-maps";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs";
configureReanimatedLogger({
level: ReanimatedLogLevel.error, // Set the log level to error
strict: true, // Reanimated runs in strict mode by default
});
export default function Menu({ getCurrentTrain }) {
const { navigate } = useNavigation();
export default function Menu({ getCurrentTrain, scrollRef }) {
const { navigate, addListener, isFocused } = useNavigation();
const { favoriteStation } = useFavoriteStation();
const { originalStationList } = useStationList();
const { height, width } = useWindowDimensions();
const scrollRef = useRef(null);
const { bottom, left, right, top } = useSafeAreaInsets();
const tabBarHeight = useBottomTabBarHeight();
const [mapsOpacity, setMapsOpacity] = useState(false);
const mapsRef = useRef(null);
const mapsSizeOffset =
(height / 100) * 60 -
(((width / 100) * 80) / 20) * 9 +
60 -
(Platform.OS == "ios" ? Constants.statusBarHeight : 0);
const MapHeight =
height -
tabBarHeight +
(Platform.OS == "android" ? Constants.statusBarHeight : 0) -
100 -
((((width / 100) * 80) / 20) * 9 + 10 + 30);
useEffect(() => {
setTimeout(() => {
if (scrollRef.current) {
scrollRef.current.scrollTo({
y: mapsSizeOffset,
y: MapHeight - 80,
animated: false,
});
}
@ -82,9 +81,9 @@ export default function Menu({ getCurrentTrain }) {
const [position, setPosition] = useState(undefined);
const getCurrentPosition = () => {
if (!locationStatus) return () => {};
Location.getCurrentPositionAsync({}).then((location) => {
setPosition(location);
});
Location.getCurrentPositionAsync({}).then((location) =>
setPosition(location)
);
};
useEffect(() => {
if (!position) return () => {};
@ -219,380 +218,201 @@ export default function Menu({ getCurrentTrain }) {
<StatusbarDetect />
<TitleBar />
<ScrollView
stickyHeaderIndices={[1, 3]}
ref={scrollRef}
snapToStart={false}
snapToEnd={false}
decelerationRate={"normal"}
onScroll={(d) => {
console.log(Object.keys(scrollRef.current));
const scrollY = d.nativeEvent.contentOffset.y + 100;
setMapsOpacity(scrollY < mapsSizeOffset);
setMapsOpacity(scrollY < MapHeight);
}}
snapToOffsets={[mapsSizeOffset]}
snapToOffsets={[MapHeight - 80]}
>
<View style={{ position: "relative", height: 0 }}>
<MapView
ref={mapsRef}
<MapView
ref={mapsRef}
style={{ flex: 1, width: "100%", height: MapHeight }}
showsUserLocation={true}
loadingEnabled={true}
showsMyLocationButton={false}
moveOnMarkerPress={false}
showsCompass={false}
//provider={PROVIDER_GOOGLE}
initialRegion={{
latitude: 33.774519,
longitude: 133.533306,
latitudeDelta: 1.8, //小さくなるほどズーム
longitudeDelta: 1.8,
}}
onPress={() => alert("地図をタップ")}
/>
<View style={{ width: "100%", height: 40, flexDirection: "row" }}>
<TouchableOpacity
style={{
flex: 1,
width: "100%",
position: "absolute",
height:
(height / 100) * 60 + (((width / 100) * 80) / 20) * 9 + 60,
opacity: mapsOpacity ? 1 : 0,
}}
showsUserLocation={true}
loadingEnabled={true}
showsMyLocationButton={false}
moveOnMarkerPress={false}
showsCompass={false}
//provider={PROVIDER_GOOGLE}
initialRegion={{
latitude: 33.774519,
longitude: 133.533306,
latitudeDelta: 1.8, //小さくなるほどズーム
longitudeDelta: 1.8,
}}
onPress={() => {
alert("地図をタップ");
}}
></MapView>
</View>
<TopMenuButton show={mapsOpacity} />
<View style={{ height: mapsSizeOffset, position: "relative" }}>
<View
style={{
width: "100%",
position: "absolute",
bottom: 0,
backgroundColor: "#0099CC",
padding: 5,
alignItems: "center",
flexDirection: "row",
display: mapsOpacity ? "flex" : "none",
marginHorizontal: 5,
borderRadius: 30,
}}
disabled={!locationStatus}
onPress={() => {
if (!position) return;
const { latitude, longitude } = position.coords;
mapsRef.current.animateToRegion(
{
latitude,
longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05,
},
1000
);
}}
>
<TouchableOpacity
<Ionicons
name="locate-outline"
size={14}
color="white"
style={{ margin: 5 }}
/>
<Text
style={{
color: "white",
fontSize: 14,
fontWeight: "bold",
flex: 1,
backgroundColor: "#0099CC",
padding: 5,
alignItems: "center",
flexDirection: "row",
marginHorizontal: 5,
borderRadius: 30,
}}
disabled={!locationStatus}
onPress={()=>{
if(!position) return;
const { latitude, longitude } = position.coords;
mapsRef.current.animateToRegion(
{
latitude,
longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05,
},
1000
);
textAlign: "center",
}}
>
<Ionicons
name="locate-outline"
size={14}
color="white"
style={{ margin: 5 }}
/>
<Text
style={{
color: "white",
fontSize: 14,
fontWeight: "bold",
flex: 1,
textAlign: "center",
}}
>
現在地基準
</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
flex: 1,
backgroundColor: "#0099CC",
padding: 5,
alignItems: "center",
flexDirection: "row",
marginHorizontal: 5,
borderRadius: 30,
}}
onPress={() => {
// お気に入りリスト更新
}}
>
<Ionicons
name="star"
size={14}
color="white"
style={{ margin: 5 }}
/>
<Text
style={{
color: "white",
fontSize: 14,
fontWeight: "bold",
flex: 1,
textAlign: "center",
}}
>
お気に入りリスト
</Text>
</TouchableOpacity>
</View>
<View
現在地基準
</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
height: 0,
backgroundColor: "white",
position: "absolute",
top: mapsSizeOffset,
flex: 1,
backgroundColor: "#0099CC",
padding: 5,
alignItems: "center",
flexDirection: "row",
marginHorizontal: 5,
borderRadius: 30,
}}
/>
</View>
<></>
{originalStationList.length != 0 && allStationData.length != 0 && (
<View style={{ flex: 1, paddingTop: 10 }}>
<Carousel
ref={carouselRef}
data={originalStationList && allStationData}
height={(((width / 100) * 80) / 20) * 9 + 10}
pagingEnabled={true}
snapEnabled={true}
loop={false}
width={width}
style={{ width: width, alignContent: "center" }}
mode="parallax"
modeConfig={{
parallaxScrollingScale: 1,
parallaxScrollingOffset: 100,
parallaxAdjacentItemScale: 0.8,
}}
onSnapToItem={setSelectedCurrentStation}
renderItem={({ item, index }) => {
return (
<View
style={{
backgroundColor: "#0000",
width: width,
flexDirection: "row",
marginLeft: 0,
marginRight: 0,
}}
key={item[0].StationNumber}
>
<View style={{ flex: 1 }} />
<Sign
currentStation={item}
isCurrentStation={item == currentStation}
oP={oPSign}
oLP={oLPSign}
/>
<View style={{ flex: 1 }} />
</View>
);
}}
onPress={() => {
// お気に入りリスト更新
}}
>
<Ionicons
name="star"
size={14}
color="white"
style={{ margin: 5 }}
/>
<View
<Text
style={{
flexDirection: "row",
justifyContent: "center",
alignContent: "center",
alignItems: "center",
color: "white",
fontSize: 14,
fontWeight: "bold",
flex: 1,
textAlign: "center",
}}
>
{originalStationList &&
allStationData.map((d, index) => {
const active = index == selectedCurrentStation;
const numberIndex = d[0].StationNumber;
if (dotButton) {
return (
<StationNumber
onPress={() => setSelectedCurrentStation(index)}
currentStation={d}
active={active}
index={numberIndex}
お気に入りリスト
</Text>
</TouchableOpacity>
</View>
{allStationData.length != 0 && originalStationList.length != 0 && (
<>
<View style={{ flex: 1, paddingTop: 10 }}>
<Carousel
ref={carouselRef}
data={originalStationList && allStationData}
height={(((width / 100) * 80) / 20) * 9 + 10}
pagingEnabled={true}
snapEnabled={true}
loop={false}
width={width}
style={{ width: width, alignContent: "center" }}
mode="parallax"
modeConfig={{
parallaxScrollingScale: 1,
parallaxScrollingOffset: 100,
parallaxAdjacentItemScale: 0.8,
}}
onSnapToItem={setSelectedCurrentStation}
renderItem={({ item, index }) => {
return (
<View
style={{
backgroundColor: "#0000",
width,
flexDirection: "row",
marginLeft: 0,
marginRight: 0,
}}
key={item[0].StationNumber}
>
<View style={{ flex: 1 }} />
<Sign
currentStation={item}
isCurrentStation={item == currentStation}
oP={oPSign}
oLP={oLPSign}
/>
);
} else {
return (
<SimpleDot
onPress={() => setSelectedCurrentStation(index)}
active={active}
index={numberIndex}
/>
);
}
})}
<View style={{ flex: 1 }} />
</View>
);
}}
/>
<View
style={{
flexDirection: "row",
justifyContent: "center",
alignContent: "center",
alignItems: "center",
}}
>
{originalStationList &&
allStationData.map((d, index) => {
const active = index == selectedCurrentStation;
const numberIndex = d[0].StationNumber;
if (dotButton) {
return (
<StationNumber
onPress={() => setSelectedCurrentStation(index)}
currentStation={d}
active={active}
index={numberIndex}
/>
);
} else {
return (
<SimpleDot
onPress={() => setSelectedCurrentStation(index)}
active={active}
index={numberIndex}
/>
);
}
})}
</View>
</View>
</View>
{allStationData[selectedCurrentStation] && (
<LED_vision
station={allStationData[selectedCurrentStation]}
getCurrentTrain={getCurrentTrain}
navigate={navigate}
openStationACFromEachTrainInfo={() => {}}
/>
)}
</>
)}
{allStationData.length != 0 &&
originalStationList.length != 0 &&
allStationData[selectedCurrentStation] && (
<LED_vision
station={allStationData[selectedCurrentStation]}
getCurrentTrain={getCurrentTrain}
navigate={navigate}
openStationACFromEachTrainInfo={() => {}}
/>
)}
<TopMenuButton />
<JRSTraInfoBox />
<FixedContentBottom navigate={navigate} />
</ScrollView>
</View>
);
}
const TopMenuButton = ({ show }) => {
const buttonList = [
{
backgroundColor: "#F89038",
icon: "train-car",
onPress: () =>
Linking.openURL("https://www.jr-shikoku.co.jp/01_trainbus/sp/"),
title: "駅・鉄道情報",
},
{
backgroundColor: "#EA4752",
icon: "google-spreadsheet",
onPress: () =>
Linking.openURL(
"https://www.jr-shikoku.co.jp/01_trainbus/jikoku/sp/#mainprice-box"
),
title: "運賃表",
},
{
backgroundColor: "#91C31F",
icon: "clipboard-list-outline",
onPress: () => Linking.openURL("https://www.jr-shikoku.co.jp/e5489/"),
title: "予約",
},
];
return (
<View style={{ flexDirection: "row", opacity: !show ? 1 : 0 }}>
{buttonList.map((d, index) => (
<UsefulBox
backgroundColor={d.backgroundColor}
icon={d.icon}
flex={1}
disable={show}
onPressButton={d.onPress}
key={index + d.icon}
>
{d.title}
</UsefulBox>
))}
</View>
);
};
const JRSTraInfoBox = () => {
const { getTime, delayData, loadingDelayData, setLoadingDelayData } =
useTrainDelayData();
const styles = {
touch: {
backgroundColor: "#0099CC",
borderRadius: 5,
margin: 10,
borderColor: "black",
borderWidth: 2,
overflow: "hidden",
},
scroll: {
backgroundColor: "#0099CC",
borderRadius: 5,
maxHeight: 300,
},
bottom: {
position: "absolute",
top: 250,
alignItems: "center",
width: "100%",
height: 50,
backgroundColor: "#007FCC88",
},
box: {
padding: 10,
backgroundColor: "white",
borderBottomLeftRadius: 5,
borderBottomRightRadius: 5,
},
};
return (
<TouchableOpacity
onPress={() => SheetManager.show("JRSTraInfo")}
style={styles.touch}
>
<ScrollView scrollEnabled={false} style={styles.scroll}>
<View
style={{ padding: 10, flexDirection: "row", alignItems: "center" }}
>
<Text style={{ fontSize: 30, fontWeight: "bold", color: "white" }}>
列車遅延速報EX
</Text>
<View style={{ flex: 1 }} />
<Text style={{ fontSize: 30, fontWeight: "bold", color: "white" }}>
{getTime
? getTime.toLocaleTimeString("ja-JP").split(":")[0] +
":" +
getTime.toLocaleTimeString("ja-JP").split(":")[1]
: NaN}
</Text>
<Ionicons
name="reload"
color="white"
size={30}
style={{ margin: 5 }}
onPress={() => {
setLoadingDelayData(true);
}}
/>
</View>
<View style={styles.box}>
{loadingDelayData ? (
<View style={{ alignItems: "center" }}>
<LottieView
autoPlay
loop
style={{ width: 150, height: 150, backgroundColor: "#fff" }}
source={require("./assets/51690-loading-diamonds.json")}
/>
</View>
) : delayData ? (
delayData.map((d, index, array) => {
let data = d.split(" ");
return (
<View
style={{ flexDirection: "row" }}
key={data[1] + "key" + index}
>
<Text style={{ flex: 15, fontSize: 18 }}>
{data[0].replace("\n", "")}
</Text>
<Text style={{ flex: 5, fontSize: 18 }}>{data[1]}</Text>
<Text style={{ flex: 6, fontSize: 18 }}>{data[3]}</Text>
</View>
);
})
) : (
<Text>現在5分以上の遅れはありません</Text>
)}
</View>
</ScrollView>
<View style={styles.bottom}>
<View style={{ flex: 1 }} />
<Text style={{ color: "white", fontWeight: "bold", fontSize: 20 }}>
詳細を見る
</Text>
<View style={{ flex: 1 }} />
</View>
</TouchableOpacity>
);
};