Edwin-Blog (1) 3D-Card 만들기

Edwin·2023년 6월 3일
0

edwinblog

목록 보기
2/5

Edwin-Blog (1) 3D-Card 만들기

  • 다음의 글은 1분코딩님의 유튜버 강의를 기반으로 작성했습니다.

결과물부터 보면 위와 같다. 이를 위해서 다음과 CSS의 다음의 규칙 선언기(rule declaration)와 속성이 사용되었다.

  • @keyframes
  • perspective
  • animation : @keyframesName infinite linear alternate
  • transform-style : preserve-3d

첫째, @keyframes

CSS에서 @keyframes은 애니메이션을 정의하는데 사용되는 규칙 선언기 이다. 요소의 스타일을 시간에 따라 동작이 가능하도록 한다. 동작을 한다는 것은 쉽게 요소의 시작과 끝 지점을 정의하고, 그 속성의 변경을 기록하여 원하는 결과를 도출할 때 사용한다. 본 프로젝트에서는 Z축을 기준으로 좌우로 회전하는 3D-Card를 구현할 때 사용하였다.

  @keyframes poster-ani {
    // from
    0% {
      transform: rotateY(-15deg);
    }
    // to
    100% {
      transform: rotateY(15deg);
    }
  }

둘째, perspective

CSS 속성 가운데 perspective는 요소의 원근감의 깊이를 나타날 때 사용된다. px을 포함한 단위(%, rem)가 작아질수록 원근감은 깊어진다. 반면 커질수록 원근감의 깊이는 완만해진다. 3D공간을 다룰 때 상대적으로 큰 값을 사용하는 경우가 많은데, GPT에 따르면 일반적으로 1000px이 사용된다고 한다. 본 프로젝트에서는 500px를 주었다.

셋째, animation

CSS의 animation 속성은 애니메이션을 적용하는데 사용된다. 본 프로젝트에서는 위에서 선언한 @keyframes을 참조하도록 정의하였고, 2.5초를 지정하여 동작하도록 하였다.

  • infinite : 동작이 무한반복되도록 지정하며
  • linear : 동작의 속도가 일정하게 움직이도록 설정한다.
  • alternate : 동작이 연속적으로 움직이듯, 진행된 시점을 역방향으로 되감아가며 다시 실행한다.

alternate를 사용하지 않으면, @keyframes의 종료시점에서 프레임이 끊어지고, @keyframes의 시작시점으로 바로 연결되기에 연결이 끊어지는 듯한, 연속되지 않아보이는 현상을 볼 수 있다. 이를 방지하기 위해 선언된 alternate는 종료된 시점을 역방향으로 실행하고, 다시 시작-역방향-시작-역방향을 반복하며 연속적인 동작을 구현하도록 기능한다.

넷째, transform-style : preserve-3d

CSS는 3D 변환을 선언한 부모 요소에 대해서만 3D공간을 생성하기에, 그 하위 자식요소들에게까지 적용되지 못하는 특징이 있다. 그러나 3D공간을 만들면 보통 하위 자식요소들에게도 적용시키기 마련이다. transform-style : preserve-3d는 이를 충족시킨다. 설정의 결과로 부모 요소에 적용된 3D 변환을 자식요소들에게도 그대로 적용되도록 만든다.

export const Layoyt = styled.div`
	... 
    perspective: 500px;
`

export const Poster = styled.section<CardOnMouseState>`
	...
    @keyframes poster-ani{}
	animation: ${({ info }) => info ? `poster-ani 2.5s infinite linear alternate` : "none"};
	transform-style: ${({ info }) => info ? "preserve-3d" : "none"};
`

export const PosterTitle = styled.h1``
export const ProFileText = styled.div``
export const ProfileFigure = styled.figure``
export const PointEmmoji = styled.figure<cardPointItemSize>``

정리하면 이렇다. Layoyt 컴포넌트는 3D 공간의 원근감을 설정하고, Poster 컴포넌트는 3D 공간에서의 애니메이션과 자식 요소들의 스타일을 정의한다. 그리고 PosterTitle, ProFileText, ProfileFigure, PointEmmoji 컴포넌트는 3D 공간에서의 효과를 받아 Layoyt와 Poster의 스타일을 상속받아 자신을 표현한다.

나아가 셋째와 넷째는 조건문을 통해서 제어하였다. 해당 조건문은 850px 이상의 태블릿과 데스크탑에서는 마우스가 호버(onMounseEnter/Leave)했을 때 기능이 동작하도록 만들었다. 반면에 850px 이하의 태블릿과 모바일에서는 호버와 상관없이 동작하도록 설정해놓았다. 해당 부분이 최상단에 있는 이미지의 내용이다.

추가적인 지식

figure 태그

이번에 새롭게 알게된 HTML태그는 figure태그이다. 이 태그는 가지고 있는 하위 태그가 미디어와 관련된 것을 명시적으로 보여주는 시멘틱한 태그이다. 이를 몰랐다면, 무지성으로 div 태그 아래에 img를 넣고 제어를 했을 것이다. 기능상의 역할은 없지만 표현상에 있어서는 눈여겨 볼 태그명이다.

이와 함께 actical 태그와 section 태그에 대해서도 정의해보았다. section는 문단단위의 굵직한 묶음을 표현하는 시멘틱한 태그이고, actical는 그 안에 있는 하나의 작은 요소들에 대한 시멘틱한 태그라고 한다.

리액트 props에서 배열을 통해 styled-components 제어하기

솔직하게 얻어걸렸다. props로 함수도 내려보내는데, 배열이 안될 이유가 없지 않는가! 이러한 질문에서 무지성으로 일단 도전해보았고 결과는 좋았다.

적용된 사례는 위의 이미지에서 볼 수 있는 PointEmmoji와 관련된 부분이었다. 1분코딩님의 강의에서는 nth-of-type() 선택자를 활용하여 3개의 상황을 각각 정의해주었다. 물론 이는 HTML와 CSS로만 설명을 진행하며 발생된 제한사항 때문이었다. 그러나 리액트에서 이를 객체지향으로 만든다는 것은 이와 같이 반복적인 코드의 재사용성을 높인다는 개념으로 접근하게 만든다.

export const PointEmmoji = styled.figure<cardPointItemSize>`
  position: absolute;
  width: ${({ info }) => info[0]};
  left: ${({ info }) => info[1]};
  bottom: ${({ info }) => info[2]};
  transform: translateZ(${({ info }) => info[3]});

  img {
    max-width: 100%;
    height: auto;
  }
`

info로 받아오는 정보들에 대해서 순서에 따라 width, left, bottom, transform을 적용하도록 설정하였다. 그 결과 해당 스타일태그를 사용하는 반복되는 코드에 있어서 아래와 같이 선언만 해주면 되었다.

return (
  <>
  ...
  <Card.PointEmmoji
  	info={["25%", "80%", "75%", "5px"]}
	children={<img src={dinosaur} alt='dinosaur' />} />
  <Card.PointEmmoji
	info={["35%", "75%", "25%", "10px"]}
	children={<img src={dinosaur} alt='dinosaur' />} />
  <Card.PointEmmoji
	info={["50%", "-10%", "20%", "13px"]}
	children={<img src={dinosaur} alt='dinosaur' />} />
  ...
  </>
)

Author. Edwin
date. 23/06/03

profile
신학전공자의 개발자 도전기!!

0개의 댓글