No 4. css stroke-dashoffset + view로 나타내기까지

Jetom·2024년 1월 18일
0

css

목록 보기
4/5
post-thumbnail

회사에서 프로젝트를 갈아엎으면서 로직을 수정해야할 일이 있었다. (대공사함)
그중 특히 애 먹었던게 곡선의 svg를 따라가던 캐릭터를 일직선의 svg를 따라가게 만들어야했던 부분이다.
곡선 svg를 그릴땐 사수분의 도움으로 해결했었고, 사실상 내가 로직을 구현했던것이 아니라 이해를 완벽하게 못하고 넘어갔었다.(사수님이 퇴사한 지금.. 잘 이해하고 넘어갈껄 후회중이다..😥)

여튼 삽질하고 삽질한걸 바탕으로 velog를 쓸 예정!


개요(?) 😎

간략하게 설명하자면 디자이너가 만들어준 선을 따라서 캐릭터가 움직여야하는 상황이였다. 처음엔 (그러니까 완전 프로젝트를 처음했을때) 이미지로 할까? 0.1초 고민하다 잘못된 생각 같아 피그마에서 svg로 추출했다. 와 정말 html 태그가 그렇게 현란한건 처음봤다.. 😂 svg를 마치고 나니 stroke-dashoffset를 넣는 과정도 백분율로 계산해서 넣어야 하는 상황이였고, 이 과정에서 백엔드에서 주는 데이터를 계산해서 백분율로 표현해야해서 수학에 약한 나는 몹시 당황했었다.

path? svg?

앞서 말했듯 현란한 svg가 문제였는데, path, svg, filter, feFlood 등등 처음보는 태그들이 있어서 당황했다.

chatGPT를 통해 이들이 어떤 역할을 하는지 찾아봤고, 그중 불필요한 태그는 제거했다.

path: 실질적인 경로 담당
svg: svg 이미지를 정의하는데 사용되는 컨테이너 역할(div 같은 느낌)
circle: 원을 그리는데 사용 (원 중심 좌표와 반지름을 지정해 원을 추가함)
filter: svg 그래픽에 필터를 적용함 (ex- 흐림 효과, 그림자)
feFlood: 특정 색상으로 채우는것

따라서 남은건 svg, path, circle 정도였다.

<svg>
 <path/>
 <CharacterPath/>
 <circle/>
 <CharacterContainer>
  {character}
 </CharacterContainer>
</svg>

백분율로 표현하기

우선 백엔드에서 주는 데이터를 가지고 캐릭터를 0~100%까지 움직이도록 해야했다. 문제는 백엔드의 데이터는 1단위씩 오는게 아니기 때문에 이것을 백분율로 바꿔서 표현해줬는데 그 내용은 아래와 같다.

const distanceValue = Number(원본 데이터 값)
const distance = Math.floor(distanceValue / 데이터 단위)

Math.floor를 사용한 이유는 소숫점을 제외하고 0~100 화면에 보여주려하기 위함이였다.

이제 진짜 css 적용해야지? 😥

2차 멘붕이 온건 이 부분이였다. 이걸 어떻게 움직여야하는지 감이 잡히지 않았기때문에 GPT가 생각도 안났고, 바로 구글에 검색했다. 그래서 나온 내용은 stroke-dashoffset였고, 이것을 이용해 움직이는건지 알게됐다. 나는 emotion을 사용하고있고, 이것으로 css를 구성했다.

const CharacterContainer = styled.foreignObject`

  will-change: offset-distance, transform;

  offset-path: path('${SVG_LINE_PATH}');
  
  transition-property: offset-distance, transform;
  transition-duration: 0.2;
  transition-timing-function: ease;
  transform-origin: center;

각각 속성을 설명하자면 다음과 같다.

foreignObject: html을 포함할 수 있는 컨테이너 역할을 하는 svg요소!
will-change: 브라우저에게 offset-distance, transform이 변경 될것임을 알리는 속성(최적화에 사용됨)
offset-path: 캐릭터가 따라가야할 svg 라인을 뜻함
transition-property: offset-distance, transform의 변화가 일어날 때 적용될 css 속성
transition-duration: 트랜지션의 지속 시간
transition-timing-function: 캐릭터가 움직일때 부드럽게 보이게 하기 위한 함수
transform-origin: transform 속성을 적용할 때 변형의 기준점을 정의(캐릭터가 뒤집힐때 중앙을 기준으로 뒤집히게 하려는 의도)

svg와 foreignObject의 차이 📚

svg는 텍스트 처리가 되지않고 단순히 벡터 그래픽을 생성하고 표현하는 반면 foreignObject는 html을 사용해 텍스트 처리 할 수 있고, div나 p 태그를 사용해 스타일링을 할 수 있다. (nextjs에서 svg안에 html을 바로 때려넣으면 경고뜸..)

svg안에 path는 두 개지롱!

svg만 css 속성을 적용하고 땡치면 좋았겠지만 캐릭터마다 path의 컬러가 다 지정되어있다...🤬 그렇기 때문에 svg의 path는 캐릭터가 선을 따라가기 전, 그러니까 그냥 path는 밑바탕이고 CharacterPath가 실질적으로 캐릭터가 걸어가면 색상이 입혀지는 path다.

(연보라가 밑바탕인 path고 CharacterPath가 보라색 캐릭터는 커서로 표현했다.)

결과적으로 캐릭터가 걸어가면 색상이 입혀지는 path의 css는 다음과 같다.

/* typescript를 사용해서 props의 type을 정의함 */

const CharacterPath = styled.path<{ color: string; distance: number }>`
  stroke: ${({ color }) => color};

  stroke-dashoffset: ${({ distance }) => path길이 * (100 - distance / 100) - 실질적 캐릭터 걸음이 멈추는 길이};
  transition-property: stroke-dashoffset;
  transition-duration: 0.1s;

싸늘하다 path 계산이 안맞다. 😱

디자인과 ppi가 안맞는지 묘하게 path 길이가 웹 브라우저와 디자인이 맞지 않았다. 그래서 path 길이를 늘려놓고 CharacterPath의 stroke-dashoffset를 늘린 path를 기준으로 계산을 하니 당연히 맞지않았다. 이틀에 걸쳐 끙끙거리다가 윗선의 압박으로 (퇴사한 분 말고) 사수님께 여쭤봤고 확인해보니 위의 언급한 이유때문이였다.. 너무 허무했지만 어쨋든 내가 했던것이 완전 틀리진 않았으니 조금의 정신승리를 하고 끝맺겠다.. (다음엔 이런 실수를 안하려는 의미로 오랜만에 블로그에 글을 썼다..🙄)

profile
사람이 좋은 인간 리트리버 신혜리입니다🐶

0개의 댓글