개발/React : React Three Fiber

【React Three Fiber】#06 화면 리사이징 하기, 더블 클릭 시 전체 화면 모드로 변경하기

고독한 쵸이 2022. 10. 13. 01:12
반응형

오늘은 React Three Fiber에서 리사이징하는 방법에 대해서 작성해보고자 한다.

 

이전 글은 아래를 참조해주세요.

 

【React Three Fiber】#05 카메라 설정&제어하기(Orthographic Camera, OrbitControls)

이번 글에서는 React Three Fiber의 카메라 중 Orthographic Camera, 그리고 카메라를 제어할 수 있는 OrbitControls에 대해 작성해보고자 한다. 이전 글은 아래를 참고해 주세요. 【React Three Fiber】#04 카메..

solitary-choi.tistory.com

 


브라우저 화면 크기에 맞춰 렌더링 하기

일단 App.tsx에서는 현재 화면 크기를 useState 초기값으로 설정한 후, Resizing 컴포넌트에 넘긴다.

참고로, JavaScript에서는 window.innerWidth와 window.innerHeight를 사용해 브라우저 화면 크기를 구할 수 있다.

js에서 윈도우 크기 구하는 방법
출처:https://www.javadrive.jp/javascript/webpage/index6.html

코드는 이런 느낌이다.

 

App.tsx

/**
 * App.
 * @return App.
 */
function App() {
  const [sizes, setSizes] = useState<Sizes>({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  return (
    <Resizing sizes={sizes} />
  );
}

export default App;

그다음에 Resizing 컴포넌트를 만들어, Canvas태그를 감싸는 div태그의 style 속성에 App.tsx에서 넘긴 사이즈를 적용한다.

 

Resizing.tsx

/* eslint-disable react/no-unknown-property */

import { Canvas, useFrame } from "@react-three/fiber";
import React, { useRef } from "react";

import { Sizes } from "../../Common";

/**
 * Props.
 */
interface Props {
  sizes: Sizes;
}

/**
 * 큐브.
 * @return Cube
 */
const Cube = () => {
  const cubeRef = useRef<THREE.Mesh>(null);

  useFrame((state) => {
    const elapsedTime = state.clock.getElapsedTime();
    if (!cubeRef.current) {
      return;
    }
    state.camera.lookAt(cubeRef.current.position);
    cubeRef.current.rotation.y = elapsedTime;
  });

  const object = (
    <mesh ref={cubeRef}>
      <boxGeometry args={[1, 1, 1, 5, 5, 5]} />
      <meshBasicMaterial color="#e91e63" />
    </mesh>
  );
  return object;
};

/**
 * 화면을 리사이징.
 * @param {object} sizes 화면 사이즈
 * @return Resizing.
 */
const Resizing = ({ sizes }: Props) => {
  return (
    <div style={{ width: sizes.width, height: sizes.height }}>
      <Canvas>
        <color attach="background" args={["black"]} />
        <Cube />
        <OrbitControls />
      </Canvas>
    </div>
  );
};

export default Resizing;

그러고 나서 코드를 실행하면 브라우저 화면 크기에 맞춰 3D가 렌더링 된 것을 확인할 수 있다.

현재 브라우저 화면 크기에 맞게 렌더링된 3D 화면


화면 리사이징 하기

하지만 이것만으로는 화면을 확대 OR 축소해도 3D 캔버스 사이즈는 변경되지 않는다.

이 문제에 대해서는 resize() 이벤트를 등록함으로써 해결할 수 있다.

화면 크기를 변경해도 3D 렌더링 사이즈가 그대로인 모습

내 경우에는 App.tsx에 useEffect에 resize 이벤트 리스너를 등록해,  화면 크기가 변경됨에 따라 화면 크기의 상태 값도 갱신하도록 하였다.

 

App.tsx

useEffect(() => {
    const resizeHandler = () => {
      setSizes({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };
    window.addEventListener("resize", resizeHandler);
    return () => window.removeEventListener("resize", resizeHandler);
  }, []);

위의 코드를 실행하면, 브라우저 화면의 크기를 변경했을 때 그에 따라 3D 캔버스 사이즈도 화면에 맞게 변화하는 것을 볼 수 있다. 

화면 크기에 맞춰 3D 렌더링 사이즈도 변경되는 모습


더블 클릭 시 전체 화면 모드로 변경하기

이번에는 3D 화면을 더블클릭했을 시, 화면이 전체 화면 모드로 변경되도록 해보자.

우선 Resizing 컴포넌트의 Canvas태그에 더블 클릭 이벤트를 등록한다.

 

Resizing.tsx

<Canvas onDoubleClick={(event) => onDoubleClick(event)}>
    <color attach="background" args={["black"]} />
    <Cube />
    <OrbitControls />
</Canvas>

그리고 더블 클릭 시 실행할 메서드를 추가한다.

메서드 내에는 더블 클릭 시 전체 화면 모드라면 일반 화면으로 돌아오게 하고, 전체 화면 모드가 아니라면 전체 화면 모드 표시를 하게 하는 if 조건문을 추가한다. 

/**
 * 더블 클릭시 실행하는 메소드.
 * @param {event} 마우스 이벤트
 */
const onDoubleClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
  const target = event.target as HTMLCanvasElement;

  if (!isFullscreen()) {
    requestFullscreen(target);
  } else {
    exitFullscreen();
  }
};

그리고 각 메서드를 추가한다.

/**
 * 전체 화면 모드인지 체크.
 */
const isFullscreen = () => {
  return !!document.fullscreenElement;
};

/**
 * 전체 화면 모드 표시.
 */
const requestFullscreen = (element: HTMLCanvasElement) => {
  element.requestFullscreen();
};

/**
 * 전체 화면 모드 해제.
 */
const exitFullscreen = () => {
  document.exitFullscreen();
};

그리고 코드를 실행하면, 화면을 더블 클릭 시 전체 화면 모드 표시 & 해제가 되는 것을 확인할 수 있다.

전체 화면 모드 표시 & 해제


브라우저 호환성 고려하기

위에서 추가한 전체 화면 모드 표시 & 해제 메서드의 경우, 일부 브라우저에서 호환이 안 되는 경우가 있다고 한다.

MDN 도큐먼트를 읽은 바로는, options.navigationUI파라미터나 Promise를 리턴하지 않는 한 requestFullScreen()과 exitFullscreen()을 사용해도 별 문제는 없어 보이지만, 혹시 모르니 브라우저에 따라 호환이 되는 메서드를 사용하도록 코드를 약간 수정해보자.

 

Element.requestFullscreen() - Web APIs | MDN

The Element.requestFullscreen() method issues an asynchronous request to make the element be displayed in fullscreen mode.

developer.mozilla.org

 

 

Document.exitFullscreen() - Web APIs | MDN

The Document method exitFullscreen() requests that the element on this document which is currently being presented in fullscreen mode be taken out of fullscreen mode, restoring the previous state of the screen. This usually reverses the effects of a previo

developer.mozilla.org

다른 브라우저의 경우, 해당 브라우저 용 메서드를 사용하도록 조건문을 추가하면 된다.

다만, 타입스크립트에서 exitFullscreen 이외의 메서드를 추가하려고 하면, 캡처와 같이 그런 메서드는 존재하지 않는다는 에러가 나타난다. (아래 캡처의 webkitExitFullscreen()은 ios용 전체 화면 모드 해제 메서드이다.)

webkitExitFullscreen이 없다고 에러가 뜬다

검색해보니 스택 오버플로에도 질문이 올라와있었다.

 

How do I get typescript to stop complaining about functions it doesn't know about?

I'm using Typescript for a web app that needs to use the JavaScript full screen API. The full screen API isn't officially supported yet, so you have to use vendor prefixes. Here's my code, based on...

stackoverflow.com

위의 답변들을 참고로 이래저래 고쳐봤지만 전혀 소용이 없었다.

그러다가 아래 깃헙을 발견해, 깃헙의 코드를 참고로 수정했더니 문제없이 실행되었다.

 

Example of fulllscreen functionality written in typescript, that supports multiple browsers.

Example of fulllscreen functionality written in typescript, that supports multiple browsers. - fullscreen.ts

gist.github.com

해결 방법은 다음과 같다.

  1. Document를 상속하는 인터페이스를 작성 후, 필요한 메서드를 추가한다.
  2. HTMLCanvasElement를 상속하는 인터페이스를 작성 후, 필요한 메서드를 추가한다.
  3. 전체 화면 모드 표시 & 해제 메서드에서 위에서 작성한 인터페이스의 메서드를 사용한다.

아래 코드에는 ios용 메서드만 추가되어 있는데, 필요하다면 Firefox나 ie용 메서드도 추가해서 사용할 수 있다.

 

Resizing.tsx

/**
 * DocumentWithFullscreen.
 */
interface DocumentWithFullscreen extends Document {
  webkitFullscreenElement?: Element;
  webkitExitFullscreen?: () => void;
}

/**
 * DocumentElementWithFullscreen.
 */
interface DocumentElementWithFullscreen extends HTMLCanvasElement {
  webkitRequestFullscreen?: () => void;
}

/**
 * 전체 화면 모드인가.
 */
const isFullscreen = () => {
  const doc = document as DocumentWithFullscreen;
  return !!(doc.fullscreenElement || doc.webkitFullscreenElement);
};

/**
 * 전체 화면 모드 표시.
 */
const requestFullscreen = (element: DocumentElementWithFullscreen) => {
  if (element.requestFullscreen) {
    element.requestFullscreen();
  } else if (element.webkitRequestFullscreen) {
    element.webkitRequestFullscreen();
  }
};

/**
 * 전체 화면 모드 해제.
 */
const exitFullscreen = (doc: DocumentWithFullscreen) => {
  if (doc.exitFullscreen) {
    doc.exitFullscreen();
  } else if (doc.webkitExitFullscreen) {
    doc.webkitExitFullscreen();
  }
};

코드를 실행해 콘솔 창에 에러가 나타나지 않는 것을 확인한다.

여력이 된다면  타 브라우저에서도 문제없이 동작하는지 확인해보도록 하자.

콘솔 에러가 뜨지 않는 화면

つづく

반응형