공부/threeJS

Three.js 따라 강의 - 02 반응형 다자인 (화면 채우기

캄성 2022. 5. 18. 00:09

2022.05.17 - [공부/JS] - Three.js 따라 강의 - 00 먼저 알아야 할 것 들

2022.05.17 - [공부/JS] - Three.js 따라 강의 - 01 Threejs 란? (정육면체만들기)

 

threejs 웹을 통하여 공부하며 개인적으로 정리한 내용을 개인경험을 추가하여 정리한 곳 입니다.

(예제가 실제 실행되지 않는 것을 고쳐 설명할경 정리하기 위하여 작성)

https://threejs.org/ 에서 제공 하는 Three.js Fundamentals 진행 이후,

유료 강의인 Three.js Journey에 대한 리뷰를 진행 할 예정입니다.

Learn Three.js의 경우 기본 이론의 심화 과정 느낌인데, 학생ID 이용시 무료 로 사용한 Ebook도 있습니다.


반응형 디자인 이란

 - web, 모바일등 다양한 환경에서도 일괄 적인 사이즈를 유지 시켜주기 위한 방법입니다.

 기본적으로 Canvas 요소는 300x150의 값을 가지고 있으나, 화면 확대를 위하여, CSS 부분을 사용하여 변경 합니다.

 

배경제거

HTML의 header 부분에 아래 코드를 추가 합니다.

<style>
html, body {
   margin: 0;
   height: 100%;
}
#c {
   width: 100%;
   height: 100%;
   display: block;
}
</style>

body의 경우 기본 5의 margin이 있으므로 0으로 변경 하고, html, body의 경우 높이를 지정하지 않으면 컨텐츠 높이 만큼 커지니 100%로 지정하여 전체 지정 합니다.

 

#C Canvas 요소의 경우 display 속성을 변경하여 변경 해주어야 글자취급 되지 않고, 화면을 채웁니다.

적용할 경우 Canvas는 충분히 커지지 만 비율이 깨집니다.

 

비율 조정

 Canvas에 그림을 그리기에 Canvas 화면 설정 값을 읽어와 비율에 반영 시켜 줍니다.

function render(time) {
  time *= 0.001;
 
  const canvas = renderer.domElement;
  camera.aspect = canvas.clientWidth / canvas.clientHeight;
  camera.updateProjectionMatrix();

위처럼 time *= 0.001; 밑에 위와같은 코드를 눟어주면 canvas 크기를 인식하여 비율을 맞춰 더이상 찌그러지지 않습니다.

 

계단 현상 제거

 Canvas는 2개 크기값이 있는데, 하나는 Canvas에서 지정한 Display 크기이고, Canvas 원본의 픽셀 수 입니다.

canvas의 원본 크기를 드로잉 버퍼 라고 하며, render.setSize를 통해 크기 지정이 가능합니다.

function resizeRendererToDisplaySize(renderer) {
  const canvas = renderer.domElement;
  const width = canvas.clientWidth;
  const height = canvas.clientHeight;
  const needResize = canvas.width !== width || canvas.height !== height;
  if (needResize) {
    renderer.setSize(width, height, false);
  }
  return needResize;
}

캔버스의 크기를 읽어 온 후, 읽어온 값이 현재 높이와 일치하지 않는 다면,  랜더를 다시하는 코드이다.

canvas.width 이 현재 크기 (현재 300x15)

canvas.clientWidth이  화면의 크기를 (전체화면) 나타내기에 확인하면 된다.

 

그 후 아래처럼 코드를 넣으면, 사이즈가 다를 때만, 화면을 재설정하는 코드가 완성 된다.

 function render(time) {
                time *= 0.001;  // convert time to seconds

                if (resizeRendererToDisplaySize(renderer)) {
                    const canvas = renderer.domElement;
                    camera.aspect = canvas.clientWidth / canvas.clientHeight;
                    camera.updateProjectionMatrix();
                }

 

최종 코드

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        html,
        body {
            margin: 0;
            height: 100%;
        }

        #c {
            width: 100%;
            height: 100%;
            display: block;
        }
    </style>

</head>

<body>
    <canvas id="c"></canvas>
    <script type="importmap">{
      "imports": {
          "three": "https://threejs.org/build/three.module.js"
      }
  }</script><!-- Remove this when import maps will be widely supported -->
    <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
    <script type="module">
        import * as THREE from 'three';
        function main() {
            const canvas = document.querySelector('#c');
            const renderer = new THREE.WebGLRenderer({ canvas });


            const fov = 75;
            const aspect = 2;  // the canvas default
            const near = 0.1;
            const far = 5;
            const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
            camera.position.z = 2;

            const scene = new THREE.Scene();

            {
                const color = 0xFFFFFF;
                const intensity = 1;
                const light = new THREE.DirectionalLight(color, intensity);
                light.position.set(-1, 2, 4);
                scene.add(light);
            }

            const boxWidth = 1;
            const boxHeight = 1;
            const boxDepth = 1;
            const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

            function makeInstance(geometry, color, x) {
                const material = new THREE.MeshPhongMaterial({ color });

                const cube = new THREE.Mesh(geometry, material);
                scene.add(cube);

                cube.position.x = x;

                return cube;
            }

            const cubes = [
                makeInstance(geometry, 0x44aa88, 0),
                makeInstance(geometry, 0x8844aa, -2),
                makeInstance(geometry, 0xaa8844, 2),
            ];

            function resizeRendererToDisplaySize(renderer) {
                const canvas = renderer.domElement;
                const width = canvas.clientWidth;
                const height = canvas.clientHeight;
                const needResize = canvas.width !== width || canvas.height !== height;
                if (needResize) {
                    renderer.setSize(width, height, false);
                }
                return needResize;
            }

            function render(time) {
                time *= 0.001;  // convert time to seconds

                if (resizeRendererToDisplaySize(renderer)) {
                    const canvas = renderer.domElement;
                    camera.aspect = canvas.clientWidth / canvas.clientHeight;
                    camera.updateProjectionMatrix();
                }

                cubes.forEach((cube, ndx) => {
                    const speed = 1 + ndx * .1;
                    const rot = time * speed;
                    cube.rotation.x = rot;
                    cube.rotation.y = rot;
                });

                renderer.render(scene, camera);

                requestAnimationFrame(render);
            }
            requestAnimationFrame(render);

        }
        main();
    </script>
</body>

</html>

 

 

별도의 이야기로 모바일 기기의 경우

HP-DPI 이기에, 아래와 같은 코드로 변경하는게 효과 적이라고 합니다.

 

    function resizeRendererToDisplaySize(renderer) {
      const canvas = renderer.domElement;
      const pixelRatio = window.devicePixelRatio;
      const width  = canvas.clientWidth  * pixelRatio | 0;
      const height = canvas.clientHeight * pixelRatio | 0;
      const needResize = canvas.width !== width || canvas.height !== height;
      if (needResize) {
        renderer.setSize(width, height, false);
      }
      return needResize;
    }