Logo Zephyrnet

Tối ưu hóa các mẫu SVG đến kích thước nhỏ nhất của chúng

Ngày:

Gần đây tôi đã tạo một mẫu tường gạch như một phần của #Mẫu nhỏ nhắn sê-ri, một thử thách trong đó tôi tạo ra các mẫu hoặc kết cấu trông hữu cơ trong SVG trong phạm vi 560 byte (hoặc kích thước xấp xỉ bằng hai tweet). Để phù hợp với hạn chế này, tôi đã trải qua một cuộc hành trình đã dạy cho tôi một số cách tối ưu hóa các mẫu SVG triệt để để chúng chứa ít mã nhất có thể mà không ảnh hưởng đến chất lượng hình ảnh tổng thể.

Tôi muốn hướng dẫn bạn về quy trình và chỉ cho bạn cách chúng tôi có thể lấy một mẫu SVG bắt đầu từ 197 byte xuống chỉ còn 44 byte - giảm đáng kể 77.7%!

Mẫu SVG

Dự phòng nhúng CodePen

Đây được gọi là mô hình gạch "trái phiếu đang chạy". Đây là mẫu gạch phổ biến nhất hiện có và chắc chắn bạn đã từng thấy trước đây: mỗi hàng gạch được bù lại bằng một nửa chiều dài của viên gạch, tạo ra một mô hình so le lặp lại. Việc sắp xếp khá đơn giản, khiến SVG's <pattern> phần tử phù hợp hoàn hảo để tái tạo nó trong mã.

SVG <pattern> phần tử sử dụng một đối tượng đồ họa được xác định trước có thể được sao chép (hoặc "lát gạch") theo những khoảng thời gian cố định dọc theo trục ngang và dọc. Về cơ bản, chúng tôi xác định một mẫu gạch hình chữ nhật và nó được lặp lại để tô vùng tô màu.

Đầu tiên, hãy đặt kích thước của một viên gạch và khoảng cách giữa mỗi viên gạch. Để đơn giản, chúng ta hãy sử dụng các số tròn, rõ ràng: chiều rộng của 100 và chiều cao của 30 cho viên gạch, và 10 cho các khoảng cách ngang và dọc giữa chúng.

Hiển thị một phần được đánh dấu của mẫu tường gạch, đây là ví dụ mà chúng tôi đang sử dụng để tối ưu hóa các mẫu SVG.

Tiếp theo, chúng ta phải xác định ô "cơ sở" của chúng tôi. Và bởi "ngói", tôi đang nói về gạch hoa văn hơn là gạch vật lý, đừng nhầm lẫn với gạch. Hãy sử dụng phần được đánh dấu của hình ảnh ở trên làm ô xếp mẫu của chúng ta: hai viên gạch nguyên ở hàng đầu tiên và một viên toàn bộ được kẹp giữa hai nửa viên gạch ở hàng thứ hai. Chú ý cách thức và vị trí các khoảng trống được đưa vào, bởi vì những khoảng trống đó cần được đưa vào ô hoa văn lặp lại.

Khi đang sử dụng <pattern>, chúng ta phải xác định mẫu của widthheight, tương ứng với chiều rộng và chiều cao của viên gạch nền. Để có được các kích thước, chúng ta cần một phép toán nhỏ:

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

Được rồi, vì vậy ô hoa văn của chúng tôi là 220✕80. Chúng tôi cũng phải thiết lập patternUnits thuộc tính, nơi giá trị userSpaceOnUse về cơ bản có nghĩa là pixel. Cuối cùng, thêm một id đối với mẫu là cần thiết để nó có thể được tham chiếu khi chúng ta sơn một phần tử khác với nó.

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

Bây giờ chúng ta đã thiết lập các kích thước ô, thách thức là tạo mã cho ô theo cách hiển thị đồ họa với số byte nhỏ nhất có thể. Đây là những gì chúng tôi hy vọng sẽ đạt được vào cuối cùng:

Các viên gạch (màu đen) và khoảng trống (màu trắng) của mẫu liên kết chạy cuối cùng

Đánh dấu ban đầu (197 byte)

Cách tiếp cận đơn giản và dễ khai báo nhất để tạo lại mô hình này mà tôi nghĩ đến là vẽ năm hình chữ nhật. Theo mặc định, fill của một phần tử SVG là màu đen và stroke là minh bạch. Điều này hoạt động tốt để tối ưu hóa các mẫu SVG, vì chúng tôi không phải khai báo rõ ràng các mẫu đó trong mã.

Mỗi dòng trong đoạn mã dưới đây xác định một hình chữ nhật. Các widthheight luôn luôn được thiết lập, và xy vị trí chỉ được đặt nếu một hình chữ nhật bị lệch khỏi 0 Chức vụ.

<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"/>

Hàng trên cùng của viên gạch có hai viên gạch có chiều rộng đầy đủ, viên gạch thứ hai được đặt ở vị trí x="110" cho phép 10 pixel của khoảng trống trước viên gạch. Tương tự có 10 pixel của khoảng cách sau vì gạch kết thúc ở 210 điểm ảnh (110 + 100 = 210) trên trục hoành mặc dù <pattern> chiều rộng là 220 điểm ảnh. Chúng tôi cần thêm một chút không gian; nếu không viên gạch thứ hai sẽ hợp nhất với viên gạch đầu tiên trong viên gạch liền kề.

Các viên gạch ở hàng thứ hai (dưới cùng) được đặt lệch nhau nên hàng này chứa hai nửa viên gạch và một viên gạch nguyên. Trong trường hợp này, chúng tôi muốn các viên gạch có nửa chiều rộng hợp nhất để không có khoảng cách ở đầu hoặc cuối, cho phép chúng chảy liền mạch với các viên gạch trong các ô hoa văn liền kề. Khi bù trừ những viên gạch này, chúng ta cũng phải bao gồm một nửa khoảng trống, do đó x giá trị là 55165, Tương ứng.

Tái sử dụng phần tử, (-43B, tổng 154B)

Có vẻ như không hiệu quả để xác định từng viên gạch một cách rõ ràng. Không có cách nào để tối ưu hóa các mẫu SVG bằng cách sử dụng lại các hình dạng để thay thế?

Tôi không nghĩ rằng SVG có một <use> yếu tố. Bạn có thể tham chiếu phần tử khác với nó và hiển thị phần tử được tham chiếu đó ở bất cứ đâu <use> Được sử dụng. Điều này tiết kiệm khá nhiều byte vì chúng ta có thể bỏ qua việc chỉ định chiều rộng và chiều cao của từng viên gạch, ngoại trừ viên gạch đầu tiên.

Mà nói, <use> không đi kèm với một mức giá nhỏ. Đó là, chúng ta phải thêm một id cho phần tử chúng tôi muốn sử dụng lại.

<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"/>

Ngắn nhất id có thể là một ký tự, vì vậy tôi đã chọn "b" cho gạch. Các <use> phần tử có thể được định vị tương tự như <rect>, Với xy các thuộc tính như hiệu số. Vì mỗi viên gạch hiện có toàn chiều rộng mà chúng tôi đã chuyển sang <use> (hãy nhớ rằng chúng tôi đã giảm một nửa số gạch ở hàng thứ hai của ô hoa văn một cách rõ ràng), chúng tôi phải sử dụng giá trị phủ định x giá trị trong hàng thứ hai, sau đó đảm bảo gạch cuối cùng tràn khỏi ô để có kết nối liền mạch giữa các viên. Tuy nhiên, những thứ này không sao, vì bất cứ thứ gì rơi ra ngoài ô hoa văn sẽ tự động bị cắt bỏ.

Bạn có thể phát hiện một số chuỗi lặp lại có thể được viết hiệu quả hơn không? Hãy làm việc tiếp theo.

Viết lại đường dẫn (-54B, tổng 100B)

<path> có lẽ là phần tử mạnh nhất trong SVG. Bạn có thể vẽ bất kỳ hình dạng nào với "lệnh" trong d thuộc tính. Có sẵn 20 lệnh, nhưng chúng ta chỉ cần những lệnh đơn giản nhất cho hình chữ nhật.

Đây là nơi tôi hạ cánh với nó:

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

Tôi biết, những con số và chữ cái siêu kỳ lạ! Tất cả đều có ý nghĩa, tất nhiên rồi. Đây là những gì đang xảy ra trong trường hợp cụ thể này:

  • M{x} {y}: Di chuyển đến một điểm dựa trên tọa độ.
  • z: Đóng phân đoạn hiện tại.
  • h{x}: Vẽ một đường ngang từ điểm hiện tại, với độ dài là x theo hướng được xác định bởi dấu hiệu của x. Chữ thường x chỉ ra một tọa độ tương đối.
  • v{y}: Vẽ một đường thẳng đứng từ điểm hiện tại, với độ dài là y theo hướng được xác định bởi dấu hiệu của y. Chữ thường y chỉ ra một tọa độ tương đối.

Đánh dấu này ngắn gọn hơn nhiều so với đánh dấu trước đó (ngắt dòng và khoảng trắng thụt lề chỉ để dễ đọc). Và, này, chúng tôi đã cố gắng cắt giảm một nửa kích thước ban đầu, đạt 100 byte. Tuy nhiên, có điều gì đó khiến tôi cảm thấy như điều này có thể nhỏ hơn…

Sửa đổi ngói (-38B, tổng số 62B)

Không phải ô hoa văn của chúng tôi có các phần lặp lại? Rõ ràng là ở hàng đầu tiên, toàn bộ viên gạch được lặp lại, nhưng ở hàng thứ hai thì sao? Khó nhìn hơn một chút, nhưng nếu chúng ta cắt viên gạch ở giữa làm đôi thì điều đó sẽ trở nên rõ ràng.

Nửa bên trái trước vạch đỏ giống với nửa bên phải.

Chà, viên gạch ở giữa không chính xác bị cắt làm đôi. Có một sự bù đắp nhỏ bởi vì chúng tôi cũng phải tính đến khoảng trống. Tuy nhiên, chúng tôi vừa tìm thấy một mẫu gạch cơ sở đơn giản hơn, có nghĩa là ít byte hơn! Điều này cũng có nghĩa là chúng ta phải giảm một nửa width của chúng tôi <pattern> phần tử từ 220 đến 110.

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

Bây giờ chúng ta hãy xem cách mà ô đơn giản được vẽ với <path>:

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

Kích thước giảm xuống còn 62 byte, nhỏ hơn một phần ba kích thước ban đầu! Nhưng tại sao phải dừng lại ở đây khi chúng ta còn có thể làm được nhiều hơn thế!

Các lệnh rút ngắn đường dẫn (-9B, tổng cộng 53B)

Bạn nên tìm hiểu sâu hơn một chút về <path> vì nó cung cấp nhiều gợi ý hơn để tối ưu hóa các mẫu SVG. Một quan niệm sai lầm mà tôi đã mắc phải khi làm việc với <path> liên quan đến cách fill thuộc tính hoạt động. Đã chơi rất nhiều với MS Paint trong thời thơ ấu của mình, tôi đã học được rằng bất kỳ hình dạng nào tôi muốn tô bằng màu đồng nhất đều phải được đóng lại, tức là không có điểm mở. Nếu không, sơn sẽ chảy ra ngoài hình dạng và tràn ra mọi thứ.

Trong SVG, tuy nhiên, điều này không đúng. Để tôi trích dẫn thông số kỹ thuật chinh no:

Thao tác điền lấp đầy các đường con đang mở bằng cách thực hiện thao tác điền như thể một lệnh “closepath” bổ sung được thêm vào đường dẫn để kết nối điểm cuối cùng của đường con với điểm đầu tiên của đường con.

Điều này có nghĩa là chúng ta có thể bỏ qua các lệnh đóng đường dẫn (z), bởi vì các đường dẫn con được coi là tự động đóng khi được lấp đầy.

Một điều hữu ích khác cần biết về các lệnh đường dẫn là chúng có các biến thể viết hoa và viết thường. Chữ thường có nghĩa là tọa độ tương đối được sử dụng; các chữ cái viết hoa có nghĩa là tọa độ tuyệt đối được sử dụng để thay thế.

Nó phức tạp hơn một chút so với HV lệnh bởi vì chúng chỉ bao gồm một tọa độ. Đây là cách tôi mô tả hai lệnh này:

  • H{x}: Vẽ một đường ngang từ điểm hiện tại để phối hợp x.
  • V{y}: Vẽ một đường thẳng đứng từ điểm hiện tại để tọa độ y.

Khi chúng tôi đang vẽ viên gạch đầu tiên trong ô hoa văn, chúng tôi bắt đầu từ (0,0) tọa độ. Sau đó, chúng tôi vẽ một đường ngang để (100,0) và một đường thẳng đứng để (100,30)và cuối cùng, vẽ một đường ngang để (0,30). Chúng tôi đã sử dụng h-100 ở dòng cuối cùng, nhưng nó tương đương với H0, là hai byte thay vì năm. Chúng tôi có thể thay thế hai lần xuất hiện tương tự và thay thế mã của <path> xuống cái này:

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

9 byte khác bị cạo đi - chúng ta có thể nhỏ hơn bao nhiêu?

Cầu nối (-5B, tổng 48B)

Các lệnh dài nhất theo cách của chúng tôi đối với mẫu SVG được tối ưu hóa hoàn toàn là các lệnh “di chuyển đến” lần lượt chiếm 4, 5 và 6 byte. Một hạn chế mà chúng tôi gặp phải là:

Một đoạn dữ liệu đường dẫn (nếu có) phải bắt đầu bằng lệnh “moveto”.

Nhưng nó ổn. Cái đầu tiên là con đường ngắn nhất. Nếu chúng ta hoán đổi các hàng, chúng ta có thể đưa ra định nghĩa đường dẫn trong đó chúng ta chỉ phải di chuyển theo chiều ngang hoặc chiều dọc giữa các khối hình. Điều gì sẽ xảy ra nếu chúng ta có thể sử dụng hv lệnh ở đó thay vì M?

Đường dẫn bắt đầu từ chấm đỏ ở góc trên cùng bên trái. Màu đỏ là các lệnh đường dẫn được hỗ trợ bằng các mũi tên, màu đen là các tọa độ mà các mũi tên trỏ đến.

Sơ đồ trên cho thấy ba hình dạng có thể được vẽ bằng một đường duy nhất. Lưu ý rằng chúng tôi đang tận dụng thực tế rằng fill hoạt động tự động đóng phần mở giữa (110,0)(0,0). Với sự sắp xếp lại này, chúng tôi cũng đã di chuyển khoảng trống sang bên trái của viên gạch có chiều rộng đầy đủ ở hàng thứ hai. Đây là giao diện của mã, vẫn được chia thành một viên gạch trên mỗi dòng:

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

Chắc chắn, chúng tôi đã tìm thấy giải pháp nhỏ nhất tuyệt đối bây giờ là chúng tôi xuống còn 48 byte, phải không ?! Tốt…

Cắt chữ số (-4B, tổng cộng 44B)

Nếu bạn có thể linh hoạt một chút với các kích thước, có một cách nhỏ khác để chúng ta có thể tối ưu hóa các mẫu SVG. Chúng tôi đã làm việc với chiều rộng gạch là 100 pixel, nhưng đó là ba byte. Thay đổi nó thành 90 có nghĩa là bớt một byte bất cứ khi nào chúng ta cần viết nó. Tương tự, chúng tôi đã sử dụng khoảng cách 10 pixel - nhưng nếu chúng tôi thay đổi nó thành 8 thay vào đó, chúng tôi tiết kiệm một byte cho mỗi lần xuất hiện đó.

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

Tất nhiên, điều này cũng có nghĩa là chúng ta phải điều chỉnh kích thước mẫu cho phù hợp. Đây là mã mẫu SVG được tối ưu hóa cuối cùng:

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

Dòng thứ hai trong đoạn mã trên - không tính các thụt lề - là 44 byte. Chúng tôi nhận được ở đây từ 197 byte trong sáu lần lặp lại. Đó là một chunky Giảm kích thước 77.7%!

Tôi đang tự hỏi mặc dù… đây có thực sự là kích thước nhỏ nhất có thể? Chúng ta đã xem xét tất cả các cách có thể để tối ưu hóa các mẫu SVG chưa?

Tôi mời bạn thử và rút gọn hơn nữa mã này hoặc thậm chí thử nghiệm với các phương pháp thay thế để tối ưu hóa các mẫu SVG. Tôi rất thích xem liệu chúng ta có thể tìm ra mức tối thiểu toàn cầu thực sự với sự khôn ngoan của đám đông hay không!

Tìm hiểu thêm về cách tạo và tối ưu hóa các mẫu SVG

Nếu bạn quan tâm để tìm hiểu thêm về cách tạo và tối ưu hóa các mẫu SVG, hãy đọc bài viết của tôi về tạo các mẫu với bộ lọc SVG. Hoặc, nếu bạn muốn xem bộ sưu tập hơn 60 mẫu, bạn có thể xem Bộ sưu tập PetitePatterns CodePen. Cuối cùng, chào mừng bạn đến xem hướng dẫn của tôi trên YouTube để giúp bạn hiểu sâu hơn nữa về các mẫu SVG.


Tối ưu hóa các mẫu SVG đến kích thước nhỏ nhất của chúng ban đầu được xuất bản trên CSS-Thủ thuật. Bạn nên nhận bản tin.

tại chỗ_img

Tin tức mới nhất

tại chỗ_img