CSS의 @property 사용예

자주 사용하지는 않지만 CSS에도 변수를 정의해 재활용할 수 있는 문법을 제공하는데 :root를 정의하고 이 안에 원하는 값을 넣어 정의하곤 했습니다. CSS에 대한 더 높은 수준의 경험 많은 개발자는 이런 CSS에서의 변수 정의를 반드시 사용해야 하는 상황을 만나게 됩니다. 이런 CSS에서의 변수를 정의하는 좀더 표준화된 문법이 있는데 그것은 @property입니다. 표준화의 의미는 엄격함이라는 조건을 달아 실수를 줄여 견고한 코드를 작성하도록 하는 장치와 같습니다.

여튼 저는 –a라는 이름의 변수(프로퍼티)를 다음처럼 정의했습니다.

@property --a {
  syntax: "<angle>"; /* https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax */
  inherits: false;  
  initial-value: 0turn;
}

프로퍼티가 가지는 타입(syntax)와 초기값을 지정하고 있습니다. 그럼 이 프로퍼티를 사용하는 코드를 보면 다음과 같습니다.

.box::before {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-conic-gradient(from var(--a), #f00, #ff0, #fff, #0ff, #f0f, #f00);
  border-radius: 25px;
  animation: rotating 4s linear infinite;
}

@keyframes rotating {
  0% {
    --a: 0turn;
  }
  100% {
    --a: -4turn;
  }
}

명확하고 직관적입니다.

실제 위의 코드가 적용한 예제 코드는 다음과 같습니다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      background-color: #222;
    }

    .box {
      position: relative;
      width: 400px;
      height: 300px;
    }

    .box span {
      color: white;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 2.5rem;
      font-weight: bold;
    }

    .box::before {
      content: '';
      position: absolute;
      inset: 0;
      background: repeating-conic-gradient(from var(--a), #f00, #ff0, #fff, #0ff, #f0f, #f00);
      border-radius: 25px;
      animation: rotating 4s linear infinite;
    }

    .box::after {
      content: '';
      position: absolute;
      inset: 0;
      background: repeating-conic-gradient(from var(--a), #f00, #ff0, #fff, #0ff, #f0f, #f00);
      border-radius: 25px;
      animation: rotating 4s linear infinite;
      filter: blur(40px);
      opacity: 0.75;
    }

    .box span {
      position: absolute;
      inset: 4px;
      background: #222;
      border-radius: 22px;
      z-index: 1;
    }

    @property --a {
      syntax: '<angle>'; /* https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax */
      inherits: false;  
      initial-value: 0turn;
    }

    @keyframes rotating {
      0% {
        --a: 0turn;
      }
      100% {
        --a: -4turn;
      }
    }
  </style>
</head>
<body>
  <div class="box">
    <span>GIS DEVELOPER</span>
  </div>
</body>
</html>

결과는 다음과 같습니다.

DOM에 대한 표시 여부 감시(IntersectionObserver)

먼저 코드는 다음과 같습니다.

import './style-intersectionObserver.css'

const divApp = document.querySelector("#app")

if(divApp) {
  for(let i=0; i<20; i++) {
    const div = document.createElement("div")
    div.innerHTML = (i+1).toString()
    divApp.append(div)
  }
  
  const observer = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      if(entry.isIntersecting) {
        entry.target.classList.add("visible")
      } else {
        entry.target.classList.remove("visible")
      }
    })
  }, {
    threshold: 0
  })

  const divList = divApp.querySelectorAll("div")
  divList?.forEach((div) => observer.observe(div))
}

스타일은 다음과 같구요.

body {
  margin: 0;
}

#app {
  overflow-y: auto;
  position: fixed;
  background-color: rgb(45, 46, 47);
  background-image: 
    linear-gradient(to right, rgb(35, 36, 37) 1px, transparent 1px), 
    linear-gradient(to bottom, rgb(35, 36, 37) 1px, transparent 1px);
  background-size: 32px 32px;

  width: 100%;
  height: 100vh;
}

#app div {
  color: white;
  font-size: 5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 8px auto;
  height: 160px;
  width: 160px;
  border-radius: 10%;
  background-color: black;
  border: 2.5px solid white;
  transition: all 0.5s ease-in-out;
  transform: rotate(-90deg) scale(0.1);
  opacity: 0;
}

#app div.visible {
  transform: rotate(0deg) scale(1);
  opacity: 1;
}

실행 결과는 다음과 같습니다.

인문학적 주소 DB

기록은 남겨야겠고.. 그렇다고 딱히 머라 정할 제목은 만만치 않아 “인문학적”이라는 어디에도 붙여 써먹어도 멋진 단어를 사용했습니다.

경기도에는 부천시가 있습니다. (아래 이미지 출처 : 위키백과)

부천시는 분류상 시군구(sig)입니다. 실제 시스템적 DB화는 부천시 하나입니다. 일반적으로 사람에게 의미있는 정보에 대해 좀더 시스템적으로 처리하기 위해 코드를 부여하는데.. 오직 “부천시”에만 코드값이 할당되어 있습니다. 그런데 우리들은 이 부천시를 위의 그림처럼 오정구, 원미구, 소사구로 다시 구분해서 실생활에 활용합니다. 구분할 필요가 전혀 없는데도 말입니다. 결국 “부천시”에 대해서 동일한 코드값을 갖지만 “부천시 오정구”, “부천시 원미구”, “부천시 소사구”를 DB에 저장할 필요가 있습니다.

여튼 서론이 다소 장황했지만 시스템은 사람이 사용하는 것이므로 부천시에 대한 각 구는 반드시 구분해서 DB화를 해야 한다는 것입니다.

육면체의 각면에 다른 도형을 넣는 쉐이더

위와 같은 육면체가 있는데 각기 다른 면에 대해 서로 다른 형태의 도형을 넣는 쉐이더 코드는 다음과 같습니다.

처음 코딩을 배웠을 때 책에 인쇄된 코드를 보며 설레였던 기분을 위의 쉐이더 코드를 보면서 다시 느끼게 되었습니다. 레고를 조립하듯 코드를 작성하는 것도 나쁘지 않다 정도가 아니라 현재와 미래에서 매우 중요한 코드 작성 방법 중 하나로 자리잡고 있습니다.

three.js를 이용한 VR(Vitrual Reality) 컨텐츠 제작

이 포스트는 three.js를 이용해서 웹 기반의 VR 어플리케이션을 만드는 기본 요소에 대한 개략적인 내용을 언급합니다.

작업 흐름

먼저, 프로젝트에 VRButton.js를 import 해야 합니다.

import { VRButton } from 'three/addons/webxr/VRButton.js';

VRButton.createButton()은 2가지 중요한 것을 수행합니다: VR 호환성을 가지는 버튼을 생성하며 사용자가 그 버튼을 활성화하면 VR 세션을 개시합니다. 이를 위해 단지 다음 코드를 작성하면 됩니다.

document.body.appendChild( VRButton.createButton( renderer ) );

다음으로는, WebGLRenderer 객체의 XR 렌더링을 활성화시킵니다.

renderer.xr.enabled = true;

마지막으로, 애니메이션 루프(Animation Loop)에 대한 코드로 자주 사용되는 requestAnimationFrame 함수 대신에 setAnimationLoop를 대신 사용하도록 합니다. 코드는 다음과 같습니다.

renderer.setAnimationLoop( function () {
    renderer.render( scene, camera );
} );

다음 단계

3D 장면에 대한 구성은 일반적인 three.js API로 개발하면 되고 VR와 관련된 UI적인 기능은 three.js에서 제공하는 VR에 특화된 추가적인 API를 사용해 개발하면 됩니다.

이 글의 원문입니다.