[TIL] 2023/09/17

yongkini ·2023년 9월 18일
0

Today I Learned

목록 보기
150/173
post-thumbnail

Today I Learned

Svg, Circle 이용해서 도넛 모양 마우스 커서 만들기

  • canvas, svg의 viewBox Attribute 등을 보면 기본적으로 (0,0)을 기준으로(보통 화면의 왼쪽 상단이 0,0이 됨) 얼마나 떨어져 있는 지점에서 시작할건지를 x,y 값으로 정하고 그 (x,y) 표부터 width, height 값을 적용하는 식으로 사용한다.
    ex) <svg viewBox="0 0 120 120"> 이렇게하면 (0, 0)을 기준으로 하고, x는 0부터 120만큼이고, y도 똑같으니까 (0,0) ~ (120, 120) 의 공간을 svg 내에 만드는 콘텐츠가 차지할 것이다라는걸 의미한다.
  • 실제로 svg, mask, circle 태그를 이용해 도넛 모양 마우스 커서를 만들어봤다.
export function getDrawCursor(strokeWidth: number) {
  const circle = `
  <svg
    height="${strokeWidth}"
    width="${strokeWidth}"
    viewBox="0 0 ${strokeWidth * 2} ${strokeWidth * 2}"
    xmlns="http://www.w3.org/2000/svg"
    >
        <defs>
            <mask 
                id="maskingFrame"
            >
                <circle
                    cx="50%"
                    cy="50%"
                    r="${strokeWidth}"
                    stroke="#000000"
                    fill="#FFFFFF"
                />  
                <circle
                    cx="50%"
                    cy="50%"
                    r="${strokeWidth / 1.2}"
                    fill="black"
                    stroke-width="5"
                    stroke="#000000"
                />
            </mask>
        </defs>
        <circle
            cx="50%"
            cy="50%"
            r="${strokeWidth - 0.6}"
            mask="url(#maskingFrame)"  
            stroke="#000000"
            stroke-width="0.6"
            fill="#FFFFFF"
            vector-effect="non-scaling-stroke"
        />
    </svg>
    `;

  return `url(data:image/svg+xml;base64,${window.btoa(circle)}) ${Math.ceil(
    strokeWidth / 2
  )} ${Math.ceil(strokeWidth / 2)}, pointer`;
}

위의 코드를 좀 더 파헤쳐보자

ViewBox

: viewBox 속성이 없어도 화면에 svg 요소를 그릴수는 있다. 하지만, viewBox 속성을 이용하면 화면의 크기에 따라 svg 요소의 크기가 자동으로 조절된다. 따라서 svg를 쓰면서 반응형 웹을 설계하기 위해서는 viewBox 속성을 필수적으로 써줘야한다.

viewBox는 svg 내에서 비율을 정할 때 쓴다고 할 수 도 있을 것 같다. 예를 들어,

<svg width="200" height="200" viewBox="0 0 100 100">
	<circle cx="50" cy="50" r="50" fill="blue" />
</svg>

위와 같은 코드가 있을 때 svg의 실제 크기는 200 * 200이라고 해도, viewBox의 100, 100 설정에 의해 내부에 circle 태그의 값들이 상대적인 값으로 변하게 된다. 좀 더 자세히 말해보면, width, height(200, 200)은 viewBox의 좌표 평면 기준으로 100, 100이 된다. 따라서, circle안에 cx, cy 에 50을 해주게되면 실제로는 100을 적용한 것과 같다(viewBox : width = 100 : 200 이니까 => 1:2 비율로 모든걸 계산한다고 했을 때, cx, cy에 적용한 50은 실제로 적용될 때는 100이된다.). 이에 따라 반지름도 100이 된다. 실제로 출력을 보면 저렇게 렌더링이 된다.

좀 더 이해를 돕기 위해 svg width, height와 viewBox의 값을 통일시켜봤다. 추가로 svg 영역을 좀 더 잘 확인하기 위해 border 속성을 추가해봤다.

<svg width="200" height="200" viewBox="0 0 200 200" style="border:1px solid black;">
    <circle cx="100" cy="100" r="50" fill="blue" />
</svg>

그렇다면

<svg width="200" height="200" viewBox="0 0 100 100">
	<circle cx="100" cy="100" r="50" fill="blue" />
</svg>

위와 같이 cx, cy를 각각 100씩 올려주면?
위와 같이 원점이 x,y축으로 100씩 움직이게 된다.

vector-effect="non-scaling-stroke"

: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/vector-effect

이 포스트는 연재(?)로 가야겠다. 아직 정리를 제대로 못했다. 가록만 남겨둔다.

profile
완벽함 보다는 최선의 결과를 위해 끊임없이 노력하는 개발자

0개의 댓글