[개인 Project] 마커 애니메이션 기능 작성

위치정보 에러 문제

이전에 시뮬레이터에서 현재 위치 값이 안잡히는 이유는 처음에 위치정보를 한번 구하는지 영원히 구하는지 클릭하는 requestForegroundPermissionsAsync설정에서 제가 한번 클릭하면 다시는 안나오는 버튼이 있는데 그걸 클릭해서 그랬습니다..다행히 폰에는 잘 나오니 폰과 시뮬레이터 둘다 확인해가면서 작성했습니다.

그리고 조금 더 명확하게 확인하고 싶어서 location값이 변경되면 날씨 정보를 받아오도록 코드를 수정하고, 로딩 중 페이지도 isLoading 변수로 불리언 값을 만들어서 제어하는 방식으로 수정했습니다.

  useEffect(() => {
    // console.log('mount', location);
    if (location !== null) {
      getWeather(location.coords.latitude, location.coords.longitude);
    }
    return () => {
      // console.log('unmount', location);
      // getWeather(location.coords.latitude, location.coords.longitude);
    };
  }, [location]);

검색을 하면 할 수록 제가 비정상적으로 코드를 작성하고 있다는 생각이 듭니다. 제가 작성한 코드는 보면 정신없는데 다른사람들의 코드는 왜이렇게 깔끔한가요..ㅋㅋㅋ 많이 작성해보는 것 만이 살길인 것 같습니다.

마커 애니메이션

특정 마커를 클릭하면 작은 창이 뜬 다음 창안에 해당 터미널의 정보를 보여주고 싶었습니다. 검색을 해보니 비슷한 내용이 있었습니다.

(참고 : https://codedaily.io/tutorials/Build-a-Map-with-Custom-Animated-Markers-and-Region-Focus-when-Content-is-Scrolled-in-React-Native)

(참고 : https://www.youtube.com/watch?v=2vILzRmEqGI
)

이 문서들을 참고해서 클론 코딩을 해보고 응용해보기로 했습니다.

마커 이미지

            <Animated.View style={[styles.markerWrap]}>
              <Animated.Image
                source={require('../../assets/map_marker.png')}
                style={[styles.marker]}
                resizeMode="cover"
              />
            </Animated.View>
          </MapView.Marker>

마커 이미지도 받아서 바꾸어 주었습니다.

      <Animated.ScrollView
        horizontal
        scrollEventThrottle={1}
        showsHorizontalScrollIndicator={false}
        style={styles.terminalScrollView}
        pagingEnabled //수평스크롤뷰에서 페이지를 매길 때 사용한다.
        snapToInterval={CARD_WIDTH + 20} // pagingEnabled과 같이 사용하면 페이징이 snapToInterval 간격으로 매겨진다.
        snapToAlignment="center"
        // ios 처음 카드의 간격을 위해 설정해준다.
        contentInset={{
          top: 0,
          left: SPACING_FOR_CARD_INSET,
          bottom: 0,
          right: SPACING_FOR_CARD_INSET,
        }}
        //android
        contentContainerStyle={{
          paddingHorizontal:
            Platform.OS === 'android' ? SPACING_FOR_CARD_INSET : 0,
        }}
        onScroll={Animated.event(
          [{ nativeEvent: { contentOffset: { x: mapAnimation } } }],
          { useNativeDriver: true },
        )}
      >
        {terminalData.map((el, idx) => (
          <View key={idx} style={styles.card}>
            <Image
              source={require('../../assets/sponge.jpeg')}
              style={styles.cardImage}
              resizeMode="cover"
            />
            <View style={styles.textContent}>
              <Text
                numberOfLines={1}
                style={styles.cardTitle}
              >{`터미널 이름 : ${el.Tmname}`}</Text>
              <Text
                numberOfLines={1}
                style={styles.cardDescription}
              >{`빈 보관대수 : ${el.Emptycnt}`}</Text>
            </View>
          </View>
        ))}
      </Animated.ScrollView>

Animated를 사용해서 스크롤뷰를 만든 다음 제가 보여주고 싶은 터미널 이름과, 빈 보관대수 값을 작성했습니다. 이제는 마커와 스크롤뷰를 연결해주면 됩니다.


Animated.ScrollView 에 onScroll값으로 x값을 정의해줍니다.

애니메이션의 초기값은 0으로 설정합니다.

  useEffect(() => {
    mapAnimation.addListener(({ value }) => {
      let index = Math.floor(value / CARD_WIDTH + 0.3);
      if (index >= terminalData.length) {
        index = terminalData - 1;
      }
      if (index <= 0) {
        index = 0;
      }

      clearTimeout(regionTimeout);

      const regionTimeout = setTimeout(() => {
        if (mapIndex !== index) {
          mapIndex = index;
          const { Latitude, Longitude } = terminalData[index];
          console.log(typeof parseFloat(Latitude), parseFloat(Latitude));
          _map.current.animateToRegion(
            {
              Latitude: parseFloat(Latitude),
              Longitude: parseFloat(Longitude),
              latitudeDelta: 0.048,
              longitudeDelta: 0.04,
            },
            350,
          );
        }
      }, 10);
    });
  });

index 값을 왜 저렇게 잡는지는 잘 모르지만 일단 따라해봅니다. 그리고 인덱스 값에 따른 터미널의 경도, 위도 값을 가져와서 _map.current.animateToRegion값에 넣어줍니다.

_map 은 useRef로 생성해줍니다.

  const _map = useRef(null);
  return isLoading ? (
	...
  ) : (
    <View style={styles.container}>
      <MapView
        ref={_map}
        ...

아직 이해가 안되는 부분은 latitudeDelta, longitudeDelta의 값을 어떻게 구하는지 모르겠습니다. 검색을 해도 명확한 공식 같은 건 나오지 않아서 일단 최대한 제 눈에 잘보이는 값으로 설정했습니다.

그리고 실행해 보았는데

터미널 정보를 가져오지 못했습니다. 왜그런가 하고 코드를 만져보았는데 문제는

누비자 api의 자체적인 하루 제한 건수가 정해져 있었습니다. 어떻게 1000번을 넘겼는지 모르겠습니다. 이렇게 되면 터미널 정보를 특정시간마다 새로 구하고 그값을 db에 저장하는 식으로 만들어야할 것 같습니다.

좋은 웹페이지 즐겨찾기