제퍼넷 로고

SVG 패턴을 가장 작은 크기로 최적화하기

시간

나는 최근에 내 작업의 일부로 벽돌 벽 패턴을 만들었습니다. #쁘띠패턴 560바이트(또는 대략 XNUMX개의 트윗 크기) 내에서 SVG에서 유기적으로 보이는 패턴이나 텍스처를 만드는 챌린지 시리즈입니다. 이 제약 조건에 맞추기 위해 저는 SVG 패턴을 최적화하여 전체 이미지 품질에 영향을 미치지 않으면서 가능한 한 적은 코드를 포함하도록 하는 몇 가지 근본적인 방법을 가르쳐준 여정을 거쳤습니다.

이 과정을 안내하고 197바이트에서 시작하여 44바이트로 감소하는 SVG 패턴을 가져오는 방법을 보여드리고자 합니다. 이는 무려 77.7%나 감소한 것입니다!

SVG 패턴

CodePen 임베드 폴백

이것은 "러닝 본드" 브릭 패턴이라고 합니다. 가장 일반적인 벽돌 패턴이며 이전에 분명히 본 적이 있습니다. 각 벽돌 행은 벽돌 길이의 절반만큼 오프셋되어 반복되는 지그재그 패턴을 만듭니다. 배열은 매우 간단하여 SVG를 <pattern> 요소는 코드에서 재현하기에 완벽합니다.

SVG <pattern> 요소는 수평 및 수직 축을 따라 고정된 간격으로 복제(또는 "타일링")될 수 있는 사전 정의된 그래픽 개체를 사용합니다. 기본적으로 직사각형 타일 패턴을 정의하고 채우기 영역을 칠하기 위해 반복됩니다.

먼저 벽돌의 치수와 각 벽돌 사이의 간격을 설정합니다. 단순함을 위해 깨끗하고 둥근 숫자를 사용하겠습니다. 100 그리고 높이 30 벽돌을 위해, 그리고 10 그들 사이의 수평 및 수직 간격을 위해.

SVG 패턴 최적화에 사용하는 예인 벽돌 벽 패턴의 강조 표시된 부분을 표시합니다.

다음으로 "기본" 타일을 식별해야 합니다. 그리고 "타일"이란 벽돌과 혼동하지 않기 위해 물리적 타일보다는 패턴 타일을 말하는 것입니다. 위의 이미지에서 강조 표시된 부분을 패턴 타일로 사용하겠습니다. 첫 번째 행에는 두 개의 전체 벽돌이 있고 두 번째 행에는 두 개의 절반 벽돌 사이에 하나의 전체가 끼워져 있습니다. 간격은 반복 패턴 타일에 포함되어야 하므로 간격이 포함되는 방법과 위치를 확인합니다.

사용시 <pattern>, 우리는 패턴의 widthheight, 기본 타일의 너비와 높이에 해당합니다. 치수를 얻으려면 약간의 수학이 필요합니다.

Tile Width = 2(Brick Width) + 2(Gap) = 2(100) + 2(10) = 220
Tile Height = 2(Bright Height) + 2(Gap) = 2(30) + 2(10) = 80

좋아, 그래서 우리의 패턴 타일은 220✕80. 우리는 또한 설정해야합니다 patternUnits 속성, 여기서 값 userSpaceOnUse 본질적으로 픽셀을 의미합니다. 마지막으로 id 다른 요소를 칠할 때 참조할 수 있도록 패턴에 필요합니다.

<pattern id="p" width="220" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>

타일 ​​크기를 설정했으므로 가능한 가장 작은 바이트 수로 그래픽을 렌더링하는 방식으로 타일에 대한 코드를 만드는 것이 과제입니다. 이것이 우리가 가장 마지막에 끝내기를 바라는 것입니다:

최종 러닝 본드 패턴의 벽돌(검정색)과 틈(흰색)

초기 마크업(197바이트)

이 패턴을 재현하는 가장 간단하고 선언적인 접근 방식은 XNUMX개의 직사각형을 그리는 것입니다. 기본적으로 fill SVG 요소의 검은색이고 stroke 투명하다. 이것은 코드에서 명시적으로 선언할 필요가 없기 때문에 SVG 패턴을 최적화하는 데 잘 작동합니다.

아래 코드의 각 줄은 사각형을 정의합니다. 그만큼 widthheight 항상 설정되어 있으며 xy 위치는 직사각형이 에서 오프셋된 경우에만 설정됩니다. 0 위치.

<rect width="100" height="30"/>
<rect x="110" width="100" height="30"/>
<rect y="40" width="45" height="30"/>
<rect x="55" y="40" width="100" height="30"/>
<rect x="165" y="40" width="55" height="30"/>

타일의 맨 위 행에는 두 개의 전체 너비 벽돌이 포함되어 있고 두 번째 벽돌은 다음 위치에 배치됩니다. x="110"10 벽돌 앞의 간격 픽셀. 마찬가지로 있다 10 벽돌이 에서 끝나기 때문에 간격의 픽셀 210 픽셀(110 + 100 = 210) 가로축에 있음에도 불구하고 <pattern> 너비는 220 픽셀. 약간의 추가 공간이 필요합니다. 그렇지 않으면 두 번째 벽돌이 인접한 타일의 첫 번째 벽돌과 합쳐집니다.

두 번째(하단) 행의 벽돌은 오프셋되어 행에 XNUMX개의 절반 벽돌과 XNUMX개의 전체 벽돌이 포함됩니다. 이 경우 절반 너비 벽돌을 병합하여 시작 또는 끝 부분에 간격이 없도록 하여 인접한 패턴 타일의 벽돌과 원활하게 흐르도록 합니다. 이러한 벽돌을 오프셋할 때 절반 간격도 포함해야 하므로 x 값은 55165각각.

요소 재사용, (-43B, 총 154B)

각 브릭을 그렇게 명시적으로 정의하는 것은 비효율적입니다. 대신 모양을 재사용하여 SVG 패턴을 최적화할 수 있는 방법이 없을까요?

SVG가 있다는 것은 널리 알려져 있지 않다고 생각합니다. <use> 요소. 다른 요소를 참조하고 참조된 요소를 어디에서나 렌더링할 수 있습니다. <use> 사용. 이것은 첫 번째 벽돌을 제외하고 각 벽돌의 너비와 높이 지정을 생략할 수 있기 때문에 꽤 많은 바이트를 절약합니다.

즉, 상기 <use> 약간의 가격으로 제공됩니다. 즉, 추가해야 합니다. id 재사용하려는 요소에 대해

<rect id="b" width="100" height="30"/>
<use href="#b" x="110"/>
<use href="#b" x="-55" y="40"/>
<use href="#b" x="55" y="40"/>
<use href="#b" x="165" y="40"/>

최단 id 가능은 한 문자이므로 벽돌로 "b"를 선택했습니다. 그만큼 <use> 요소는 다음과 유사하게 배치될 수 있습니다. <rect>,와 xy 속성을 오프셋으로 사용합니다. 이제 각 벽돌은 전체 너비이므로 다음으로 전환했습니다. <use> (패턴 타일의 두 번째 행에서 벽돌을 명시적으로 반으로 나눈 것을 기억하십시오.) x 두 번째 행에 값을 입력한 다음 벽돌 사이의 원활한 연결을 위해 타일에서 마지막 벽돌이 오버플로되는지 확인합니다. 그러나 패턴 타일을 벗어나는 것은 자동으로 잘리기 때문에 괜찮습니다.

더 효율적으로 작성할 수 있는 반복되는 문자열을 찾을 수 있습니까? 다음에 작업해 보겠습니다.

경로에 다시 쓰기(-54B, 총 100B)

<path> SVG에서 아마도 가장 강력한 요소일 것입니다. "명령"을 사용하여 거의 모든 모양을 그릴 수 있습니다. d 기인하다. 20개의 명령을 사용할 수 있지만 직사각형에 가장 간단한 명령만 필요합니다.

내가 착륙 한 곳은 다음과 같습니다.

<path d="M0 0h100v30h-100z M110 0h100v30h-100 M0 40h45v30h-45z M55 40h100v30h-100z M165 40h55v30h-55z"/>

알아요, 엄청나게 이상한 숫자와 문자! 모두 의미가 있다, 물론이야. 이 특정한 경우에 일어나는 일은 다음과 같습니다.

  • M{x} {y}: 좌표를 기준으로 한 점으로 이동합니다.
  • z: 현재 세그먼트를 닫습니다.
  • h{x}: 현재 점에서 의 길이로 수평선을 그립니다. x 기호로 정의된 방향으로 x. 소문자 x 상대 좌표를 나타냅니다.
  • v{y}: 의 길이로 현재 점에서 수직선을 그립니다. y 기호로 정의된 방향으로 y. 소문자 y 상대 좌표를 나타냅니다.

이 마크업은 이전 마크업보다 훨씬 간결합니다(줄 바꿈 및 들여쓰기 공백은 가독성을 위해서만 사용됨). 그리고 이봐, 우리는 초기 크기의 절반을 잘라내어 100바이트에 도달했습니다. 그래도 뭔가 작아질 수 있을 것 같은 느낌이...

타일 ​​수정(-38B, 총 62B)

패턴 타일에 반복되는 부분이 없습니까? 첫 번째 행에서 전체 벽돌이 반복되는 것이 분명하지만 두 번째 행은 어떻습니까? 보기가 조금 더 어렵지만 중간 벽돌을 반으로 자르면 분명해집니다.

빨간색 선 앞의 왼쪽 절반은 오른쪽과 동일합니다.

글쎄, 중간 벽돌은 정확히 반으로 잘리지 않습니다. 차이도 고려해야 하기 때문에 약간의 오프셋이 있습니다. 어쨌든, 우리는 더 적은 바이트를 의미하는 더 단순한 기본 타일 패턴을 찾았습니다! 이것은 또한 우리가 반으로 줄여야 함을 의미합니다 width 우리의 <pattern> 220에서 110까지의 요소.

<pattern id="p" width="110" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>

이제 단순화 된 타일이 어떻게 그려지는지 봅시다. <path>:

<path d="M0 0h100v30h-100z M0 40h45v30h-45z M55 40h55v30h-55z"/>

크기는 이미 원래 크기의 62분의 XNUMX 미만인 XNUMX바이트로 축소되었습니다! 하지만 우리가 할 수 있는 일이 더 있는데 왜 여기서 멈추세요!

경로 단축 명령(-9B, 총 53B)

조금 더 깊이 들어갈 가치가 있습니다. <path> SVG 패턴 최적화를 위한 더 많은 힌트를 제공하기 때문입니다. 작업할 때 내가 가지고 있던 한 가지 오해 <path> 방법에 관한 것입니다 fill 속성이 작동합니다. 어린 시절에 MS Paint를 많이 사용하면서 단색으로 채우고 싶은 모양은 닫혀 있어야 한다는 것, 즉 열린 점이 없어야 한다는 것을 배웠습니다. 그렇지 않으면 페인트가 모양에서 새어 나와 모든 것을 엎지르게 됩니다.

그러나 SVG에서는 이것이 사실이 아닙니다. 인용하자 사양 그 자체:

채우기 작업은 하위 경로의 마지막 지점과 하위 경로의 첫 번째 지점을 연결하기 위해 경로에 추가 "closepath" 명령이 추가된 것처럼 채우기 작업을 수행하여 열린 하위 경로를 채웁니다.

이것은 닫기 경로 명령(z), 하위 경로가 채워지면 자동으로 닫힌 것으로 간주되기 때문입니다.

경로 명령에 대해 알아야 할 또 다른 유용한 사항은 대문자 및 소문자 변형이 있다는 것입니다. 소문자는 상대 좌표가 사용됨을 의미합니다. 대문자는 절대 좌표가 대신 사용됨을 의미합니다.

그것보다 조금 더 까다롭습니다 HV 하나의 좌표만 포함하기 때문입니다. 이 두 명령을 설명하는 방법은 다음과 같습니다.

  • H{x}: 현재 점에서 좌표로 수평선을 그립니다. x.
  • V{y}: 현재 점에서 좌표로 수직선을 그립니다. y.

패턴 타일의 첫 번째 벽돌을 그릴 때 (0,0) 좌표. 그런 다음 수평선을 그립니다. (100,0) 그리고 수직선은 (100,30), 그리고 마지막으로 수평선을 그립니다. (0,30). 우리는 사용 h-100 명령은 마지막 줄에 있지만 다음과 같습니다. H0, XNUMX가 아닌 XNUMX바이트입니다. 우리는 두 개의 유사한 발생을 대체하고 우리의 코드를 비교할 수 있습니다 <path> 아래로 :

<path d="M0 0h100v30H0 M0 40h45v30H0 M55 40h55v30H55"/>

또 다른 9바이트가 줄어들었습니다. 얼마나 더 작아질 수 있을까요?

브리징(-5B, 총 48B)

완전히 최적화된 SVG 패턴을 방해하는 가장 긴 명령은 각각 4, 5, 6바이트를 차지하는 "이동" 명령입니다. 한 가지 제약 조건은 다음과 같습니다.

경로 데이터 세그먼트(있는 경우)는 "moveto" 명령으로 시작해야 합니다.

하지만 괜찮습니다. 어쨌든 첫 번째가 가장 짧습니다. 행을 바꾸면 벽돌 사이에서 수평 또는 수직으로만 이동해야 하는 경로 정의를 얻을 수 있습니다. 우리가 사용할 수 있다면 어떨까요? hv 대신 거기에 명령 M?

경로는 왼쪽 상단 모서리에 있는 빨간색 점에서 시작됩니다. 빨간색은 화살표로 지원되는 경로 명령이고 검은색은 화살표가 가리키는 좌표입니다.

위의 다이어그램은 단일 경로로 세 가지 모양을 그리는 방법을 보여줍니다. 우리는 fill 작업 사이에 열린 부분이 자동으로 닫힙니다. (110,0)(0,0). 이 재배치로 두 번째 행의 전체 너비 벽돌 왼쪽으로 간격도 옮겼습니다. 코드가 어떻게 보이는지 여전히 한 줄에 하나의 벽돌로 나뉩니다.

<path d="M0 0v30h50V0 h10v30h50 v10H10v30h100V0"/>

확실히, 우리는 48바이트로 줄어들었기 때문에 절대적으로 가장 작은 솔루션을 찾았습니다. 맞죠?! 잘…

디지트 트리밍(-4B, 총 44B)

치수에 약간의 유연성을 가질 수 있다면 SVG 패턴을 최적화할 수 있는 또 다른 작은 방법이 있습니다. 우리는 벽돌 너비로 작업했습니다. 100 픽셀이지만 XNUMX바이트입니다. 로 변경 90 쓸 필요가 있을 때마다 XNUMX바이트가 줄어듭니다. 마찬가지로, 우리는 의 간격을 사용했습니다. 10 픽셀 — 하지만 다음으로 변경하면 8 대신, 우리는 각각의 발생에 대해 바이트를 저장합니다.

<path d="M0 0v30h45V0 h8v30h45 v8H8v30h90V0"/>

물론 이것은 또한 그에 따라 패턴 치수를 조정해야 함을 의미합니다. 다음은 최적화된 최종 SVG 패턴 코드입니다.

<pattern id="p" width="98" height="76" patternUnits="userSpaceOnUse"> <path d="M0 0v30h45V0h8v30h45v8H8v30h90V0"/>
</pattern>

들여쓰기를 제외하고 위의 스니펫에서 두 번째 줄은 다음과 같습니다. 44 바이트. 197번의 반복으로 XNUMX바이트에서 여기까지 왔습니다. 덩어리지네요 77.7% 크기 축소!

궁금한데... 이게 정말 가장 작은 사이즈가 가능한가요? SVG 패턴을 최적화할 수 있는 모든 방법을 살펴보았습니까?

이 코드를 추가로 축소하거나 SVG 패턴을 최적화하기 위한 대체 방법을 실험해 보시기 바랍니다. 군중의 지혜로 진정한 글로벌 최소값을 찾을 수 있는지 알고 싶습니다!

SVG 패턴 생성 및 최적화에 대한 추가 정보

SVG 패턴 생성 및 최적화에 대해 자세히 알아보려면 내 기사를 읽어보세요. SVG 필터로 패턴 만들기. 또는 60개 이상의 패턴 갤러리를 확인하려면 PetitePatterns CodePen 컬렉션. 마지막으로 시청해 주시면 감사하겠습니다 YouTube의 내 튜토리얼 SVG 패턴을 더 깊이 이해하는 데 도움이 됩니다.


SVG 패턴을 가장 작은 크기로 최적화하기 원래에 게시 CSS 트릭. 너는해야한다. 뉴스레터 받기.

spot_img

최신 인텔리전스

spot_img

우리와 함께 채팅

안녕하세요! 어떻게 도와 드릴까요?