My Boundary As Much As I Experienced

리액트네이티브의 레이아웃 이벤트 및 스크롤 이벤트를 활용하여 요소를 조작하려면?(onLayout) 본문

FrontEnd/React Native

리액트네이티브의 레이아웃 이벤트 및 스크롤 이벤트를 활용하여 요소를 조작하려면?(onLayout)

Bumang 2024. 9. 25. 19:55

레이아웃 이벤트(Layout Events)

레이아웃이 바뀔 때 발생한다. 특히 컴포넌트가 처음 화면에 렌더링될 때

처음으로 컴포넌트가 마운트되어 레이아웃 좌표가 생길 때 발생한다.

그 외 컴포넌트의 크기나 위치가 변경될 때,

화면 회전, 부모 레이아웃 변경, 스타일 변경 등으로 인해

레이아웃이 다시 계산될 때 트리거된다.

  • onLayout: 요소가 화면에 배치될 때 호출.

레이아웃 이벤트 객체 타입: LayoutChangeEvent

  • nativeEvent:
    • layout:
      • x, y: 요소가 화면에 배치된 좌표.
      • width, height: 요소의 크기.
import React from 'react';
import { View, Text } from 'react-native';

const LayoutEventExample = () => {
  const handleLayout = (event) => {
    const { width, height, x, y } = event.nativeEvent.layout; // 너비, 높이, x, y 등을 꺼내올 수 있다.
    console.log(`Width: ${width}, Height: ${height}, X: ${x}, Y: ${y}`);
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <View 
        onLayout={handleLayout}
        style={{ width: 100, height: 100, backgroundColor: 'lightblue' }}
      >
        <Text>Box</Text>
      </View>
    </View>
  );
};

export default LayoutEventExample;

 

레이아웃이벤트는 translateX, Y값이 바뀌는 것을 감지하나?

아니다. 감지 못한다. transform이벤트는 실제 좌표값을 바꾸는게 아니라

시각적인 요소만 옮기는 것이기 때문에 transform: translateX(...) 같은게 처리되지 않는다.

 

뷰포트에 들어오는 것을 onLayout으로 감지할 수 있나?

아니다. 없다. 레이아웃이 형성되거나 바뀌는걸 감지하는거지 스크롤 이벤트를 감지하는게 아니기 때문이다.

 

스크롤 시에도 onLayout 이벤트가 발생하나?

아니다! 스크롤 시에도 onLayout 이벤트가 발생하지 않는다.

다시 말하지만 onLayout 이벤트는 '컴포넌트의 레이아웃(크기 또는 위치)'이 변경될 때 발생한다.

스크롤은 보통 컴포넌트 자체의 레이아웃을 변경하는게 아니라 보고있는 위치(뷰포트)를 바꾸는 것이기 때문에

onLayout 이벤트가 발생하지 않는다.

 

결론은 전체 레이아웃 내에서 너비, 높이, 좌표 등이 바뀔 때만 발생한다.

그래서 레이아웃 변경 혹은 마운트가 발생할 때만 그 때의 좌표값만 불러올 수 있기 때문에 

내가 원하는 시점에 onLayout자체를 다시 발생시킬수는 없다.

(물론 내가 원하는 시점이 레이아웃 변경이라면 가능..)

 

그러면 내가 원하는 시점에 어떤 요소의 x, y, 너비, 높이값을 가져오려면 어떻게 해야될까?

 

measure 메서드로 특정 컴포넌트 좌표값 알아내기

measure 메서드를 사용하면, ScrollView에서 특정 컴포넌트의 스크린 기준 좌표를 추적할 수 있다.

이 메서드로 컴포넌트의 절대 좌표를 포함한 여러 정보를 받아낼 수 있다.

특히 스크린 기준의 x,y 좌표를 알아낼 수 있기 때문에 스크롤 이벤트와 같이 사용하여 인터랙션을 주기 좋다!

measure 메서드에서 반환되는 값들:

  • fx: 컴포넌트의 부모 내에서의 x 좌표.
  • fy: 컴포넌트의 부모 내에서의 y 좌표.
  • width: 컴포넌트의 너비.
  • height: 컴포넌트의 높이.
  • px: 스크린 기준의 x 좌표.
  • py: 스크린 기준의 y 좌표.
import React, { useRef, useState } from 'react';
import { ScrollView, View, Text, Button, findNodeHandle } from 'react-native';

const ScrollViewMeasureExample = () => {
  const targetRef = useRef(null);
  const [coordinates, setCoordinates] = useState({ x: 0, y: 0 });

  const handleMeasure = () => {
    if (targetRef.current) {
      // 특정 컴포넌트의 좌표 측정
      targetRef.current.measure((fx, fy, width, height, px, py) => {
        setCoordinates({ x: px, y: py });
        console.log(`X: ${px}, Y: ${py}, Width: ${width}, Height: ${height}`);
      });
    }
  };

  return (
    <ScrollView>
      <View style={{ height: 1000 }}>
        <Text>Scroll down</Text>
      </View>
      <View ref={targetRef} style={{ height: 200, backgroundColor: 'lightblue' }}>
        <Text>Target Component</Text>
      </View>
      <View style={{ height: 1000 }}>
        <Text>Scroll more</Text>
      </View>
      <Button title="Get Coordinates" onPress={handleMeasure} /> // 버튼을 누를 때마다 현재 요소의 x,y, 너비 높이 좌표를 알 수 있다.
      <View>
        <Text>Component Coordinates: X = {coordinates.x}, Y = {coordinates.y}</Text>
      </View>
    </ScrollView>
  );
};

export default ScrollViewMeasureExample;