Jag skapade nyligen ett tegelväggsmönster som en del av min #PetitePatterns serie, en utmaning där jag skapar organiskt utseende mönster eller texturer i SVG inom 560 byte (eller ungefär storleken på två tweets). För att passa denna begränsning har jag gått igenom en resa som har lärt mig några radikala sätt att optimera SVG-mönster så att de innehåller så lite kod som möjligt utan att påverka den övergripande bildkvaliteten.
Jag vill vägleda dig genom processen och visa dig hur vi kan ta ett SVG-mönster som börjar på 197 byte hela vägen ner till bara 44 byte - en enorm minskning på 77.7 %!
SVG-mönstret
Detta är vad som kallas ett "running bond"-tegelmönster. Det är det vanligaste tegelmönstret där ute, och ett du säkert har sett förut: varje rad med tegelstenar förskjuts med hälften av en tegelstens längd, vilket skapar ett upprepat förskjutet mönster. Arrangemanget är ganska enkelt, vilket gör SVG:s <pattern>
element som passar perfekt för att återskapa det i kod.
SVG <pattern>
elementet använder ett fördefinierat grafiskt objekt som kan replikeras (eller "tilläggs") med fasta intervall längs de horisontella och vertikala axlarna. I huvudsak definierar vi ett rektangulärt kakelmönster och det upprepas för att måla fyllningsområdet.
Låt oss först ställa in måtten på en tegelsten och gapet mellan varje tegelsten. För enkelhetens skull, låt oss använda rena, runda siffror: en bredd på 100
och en höjd av 30
för tegelstenen, och 10
för de horisontella och vertikala gapen mellan dem.
Därefter måste vi identifiera vår "bas"-bricka. Och med "kakel" talar jag om mönsterplattor snarare än fysiska brickor, inte att förväxla med tegelstenarna. Låt oss använda den markerade delen av bilden ovan som vår mönsterbricka: två hela tegelstenar i den första raden och en hel inklämd mellan två halva tegelstenar i den andra raden. Lägg märke till hur och var mellanrummen ingår, eftersom de måste inkluderas i den upprepade mönsterbrickan.
När man använder <pattern>
, vi måste definiera mönstret width
och height
, som motsvarar basplattans bredd och höjd. För att få måtten behöver vi lite matematik:
Tile Width = 2(Brick Width) + 2(Gap) = 2(100) + 2(10) = 220
Tile Height = 2(Bright Height) + 2(Gap) = 2(30) + 2(10) = 80
Okej, så är vår mönsterbricka 220✕80
. Vi måste också ställa in patternUnits
attribut, där värdet userSpaceOnUse
betyder i huvudsak pixlar. Slutligen lägger man till en id
till mönstret är nödvändigt så att det kan refereras när vi målar ett annat element med det.
<pattern id="p" width="220" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>
Nu när vi har fastställt brickdimensionerna är utmaningen att skapa koden för brickan på ett sätt som gör grafiken med minsta möjliga antal byte. Detta är vad vi hoppas få till slut:
Initial uppmärkning (197 byte)
Det enklaste och mest deklarativa tillvägagångssättet för att återskapa detta mönster som jag tänker på är att rita fem rektanglar. Som standard är fill
av ett SVG-element är svart och stroke
är transparent. Detta fungerar bra för att optimera SVG-mönster, eftersom vi inte behöver uttryckligen deklarera dem i koden.
Varje rad i koden nedan definierar en rektangel. De width
och height
är alltid inställda, och x
och y
positioner ställs bara in om en rektangel är förskjuten från 0
position.
<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"/>
Den översta raden av brickan innehöll två tegelstenar i full bredd, den andra tegelstenen är placerad till x="110"
tillåter 10
pixlar av gap före tegelstenen. Likaså finns det 10
pixlar av gap efter, eftersom tegelstenen slutar vid 210
pixlar (110 + 100 = 210
) på den horisontella axeln även om <pattern>
bredd är 220
pixlar. Vi behöver lite extra utrymme; annars skulle den andra tegelstenen smälta samman med den första tegelstenen i den intilliggande brickan.
Tegelstenarna i den andra (nedre) raden är förskjutna så att raden innehåller två halva tegelstenar och en hel tegelsten. I det här fallet vill vi att tegelstenarna med halv bredd smälter samman så att det inte finns något mellanrum i början eller slutet, vilket gör att de kan flyta sömlöst med tegelstenarna i angränsande mönsterplattor. När vi kompenserar dessa tegelstenar måste vi också inkludera halva luckor, alltså x
värden är 55
och 165
, Respektive.
Elementåteranvändning, (-43B, totalt 154B)
Det verkar ineffektivt att definiera varje tegelsten så explicit. Finns det inte något sätt att optimera SVG-mönster genom att återanvända formerna istället?
Jag tror inte att det är allmänt känt att SVG har en <use>
element. Du kan referera till ett annat element med det och rendera det refererade elementet var som helst <use>
är använd. Detta sparar en hel del byte eftersom vi kan utelämna att ange bredden och höjden på varje sten, förutom den första.
Som sagt, <use>
kommer med ett litet pris. Det vill säga, vi måste lägga till en id
för det element vi vill återanvända.
<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"/>
Den kortaste id
möjligt är ett tecken, så jag valde "b" för tegel. De <use>
element kan placeras på liknande sätt som <rect>
, Med x
och y
attribut som offset. Eftersom varje tegelsten är full bredd nu som vi har bytt till <use>
(kom ihåg att vi uttryckligen halverade tegelstenarna i den andra raden av mönsterbrickan), vi måste använda en negativ x
värde i den andra raden, se sedan till att den sista tegelstenen rinner över från plattan för den sömlösa anslutningen mellan tegelstenarna. Dessa är dock okej, eftersom allt som faller utanför mönsterbrickan skärs automatiskt av.
Kan du se några återkommande strängar som kan skrivas mer effektivt? Låt oss arbeta med dem nästa.
Skriver om till sökväg (-54B, totalt 100B)
<path>
är förmodligen det mest kraftfulla elementet i SVG. Du kan rita nästan vilken form som helst med "kommandon" i dess d
attribut. Det finns 20 kommandon tillgängliga, men vi behöver bara de enklaste för rektanglar.
Här landade jag med det:
<path d="M0 0h100v30h-100z M110 0h100v30h-100 M0 40h45v30h-45z M55 40h100v30h-100z M165 40h55v30h-55z"/>
Jag vet, superkonstiga siffror och bokstäver! De har alla betydelse, självklart. Här är vad som händer i det här specifika fallet:
M{x} {y}
: Flyttar till en punkt baserat på koordinater.z
: Stänger det aktuella segmentet.h{x}
: Ritar en horisontell linje från den aktuella punkten, med längden påx
i den riktning som definieras av tecknet påx
. Små bokstäverx
indikerar en relativ koordinat.v{y}
: Ritar en vertikal linje från den aktuella punkten, med längden påy
i den riktning som definieras av tecknet påy
. Små bokstävery
indikerar en relativ koordinat.
Denna markering är mycket mer kortfattad än den föregående (radbrytningar och blanksteg för indrag är endast för läsbarhet). Och hej, vi har lyckats skära ut hälften av den ursprungliga storleken, och nått 100 byte. Ändå är det något som får mig att känna att det här kan vara mindre...
Kakelrevision (-38B, totalt 62B)
Har inte vår mönsterbricka återkommande delar? Det är klart att i första raden upprepas en hel tegelsten, men hur är det med andra raden? Det är lite svårare att se, men om vi skär den mittersta tegelstenen på mitten blir det uppenbart.
Tja, den mellersta tegelstenen är inte precis halverad. Det finns en liten offset eftersom vi också måste ta hänsyn till gapet. Hur som helst, vi har precis hittat ett enklare mönster för basplattor, vilket innebär färre byte! Detta innebär också att vi måste halvera width
av vår <pattern>
element från 220 till 110.
<pattern id="p" width="110" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>
Låt oss nu se hur den förenklade brickan ritas med <path>
:
<path d="M0 0h100v30h-100z M0 40h45v30h-45z M55 40h55v30h-55z"/>
Storleken är reducerad till 62 byte, vilket redan är mindre än en tredjedel av den ursprungliga storleken! Men varför stanna här när det finns ännu mer vi kan göra!
Kommandon för att förkorta vägen (-9B, 53B totalt)
Det är värt att gå lite djupare in i <path>
element eftersom det ger fler tips för att optimera SVG-mönster. En missuppfattning jag har haft när jag jobbade med <path>
handlar om hur fill
attribut fungerar. Efter att ha lekt mycket med MS Paint i min barndom, har jag lärt mig att alla former som jag vill fylla med en enfärgad färg måste vara stängda, dvs inte ha några öppna punkter. Annars kommer färgen att läcka ur formen och spilla över allt.
I SVG stämmer detta dock inte. Låt mig citera specifikationen sig:
Fyllningsoperationen fyller öppna undersökvägar genom att utföra fyllningsoperationen som om ett ytterligare "stängväg"-kommando lades till sökvägen för att koppla den sista punkten på undersökvägen med den första punkten på undervägen.
Detta betyder att vi kan utelämna kommandona för nära sökväg (z
), eftersom undersökvägarna anses automatiskt stängda när de fylls.
En annan användbar sak att veta om sökvägskommandon är att de finns i versaler och gemener. Små bokstäver betyder att relativa koordinater används; versaler betyder att absoluta koordinater används istället.
Det är lite knepigare än så med H
och V
kommandon eftersom de bara inkluderar en koordinat. Så här skulle jag beskriva dessa två kommandon:
H{x}
: Ritar en horisontell linje från den aktuella punkten för att koordinerax
.V{y}
: Ritar en vertikal linje från den aktuella punkten för att koordineray
.
När vi ska rita den första tegelstenen i mönsterbrickan utgår vi från (0,0)
koordinater. Vi drar sedan en horisontell linje till (100,0)
och en vertikal linje till (100,30)
, och slutligen, rita en horisontell linje till (0,30)
. Vi använde h-100
kommandot på sista raden, men det motsvarar H0
, vilket är två byte istället för fem. Vi kan ersätta två liknande förekomster och parera koden för vår <path>
ner till detta:
<path d="M0 0h100v30H0 M0 40h45v30H0 M55 40h55v30H55"/>
Ytterligare 9 byte rakade av — hur mycket mindre kan vi gå?
Överbryggning (-5B, totalt 48B)
De längsta kommandona som står i vägen för ett helt optimerat SVG-mönster är "flytta till"-kommandona som tar upp 4, 5 respektive 6 byte. En begränsning vi har är att:
Ett sökvägsdatasegment (om det finns ett) måste börja med ett "moveto"-kommando.
Men det är okej. Den första är i alla fall kortast. Om vi byter rader kan vi komma på en bandefinition där vi bara behöver röra oss antingen horisontellt eller vertikalt mellan tegelstenarna. Tänk om vi kunde använda h
och v
kommandon där istället för M
?
Diagrammet ovan visar hur de tre formerna kan ritas med en enda bana. Observera att vi utnyttjar det faktum att fill
operation stänger automatiskt den öppna delen mellan (110,0)
och (0,0)
. Med detta omarrangemang flyttade vi även mellanrummet till vänster om tegelstenen i full bredd i andra raden. Så här ser koden ut, fortfarande uppdelad i en kloss per rad:
<path d="M0 0v30h50V0 h10v30h50 v10H10v30h100V0"/>
Visst, vi har hittat den absolut minsta lösningen nu när vi är nere på 48 byte, eller hur?! Väl…
Siffror trimning (-4B, 44B totalt)
Om du kan vara lite flexibel med dimensionerna, finns det ett annat litet sätt att optimera SVG-mönster. Vi har arbetat med en tegelbredd på 100
pixlar, men det är tre byte. Ändra det till 90
betyder en byte mindre när vi behöver skriva det. På samma sätt använde vi en lucka på 10
pixlar — men om vi ändrar det till 8
istället sparar vi en byte på var och en av dessa förekomster.
<path d="M0 0v30h45V0 h8v30h45 v8H8v30h90V0"/>
Detta innebär naturligtvis också att vi måste anpassa mönstermåtten därefter. Här är den slutgiltiga optimerade SVG-mönsterkoden:
<pattern id="p" width="98" height="76" patternUnits="userSpaceOnUse"> <path d="M0 0v30h45V0h8v30h45v8H8v30h90V0"/>
</pattern>
Den andra raden i ovanstående utdrag - utan att räkna in indragen - är 44 bitgrupper. Vi kom hit från 197 byte i sex iterationer. Det är en chunky 77.7 % storleksminskning!
Jag undrar dock... är detta verkligen den minsta möjliga storleken? Har vi tittat på alla möjliga sätt att optimera SVG-mönster?
Jag inbjuder dig att försöka förminska denna kod ytterligare, eller till och med experimentera med alternativa metoder för att optimera SVG-mönster. Jag skulle älska att se om vi kunde hitta det sanna globala minimumet med publikens visdom!
Mer om att skapa och optimera SVG-mönster
Om du är intresserad av att lära dig mer om att skapa och optimera SVG-mönster, läs min artikel om skapa mönster med SVG-filter. Eller, om du vill kolla in ett galleri med 60+ mönster, kan du se PetitePatterns CodePen Collection. Till sist är du välkommen att titta mina tutorials på YouTube för att hjälpa dig komma ännu djupare in i SVG-mönster.
Optimera SVG-mönster till sin minsta storlek ursprungligen publicerad på CSS-tricks. Du borde få nyhetsbrevet.
- "
- 10
- 100
- 77
- 9
- 98
- Om oss
- Absolut
- Konto
- Annat
- Alla
- tillåta
- redan
- Annan
- tillvägagångssätt
- OMRÅDE
- Artikeln
- attribut
- tillgänglig
- AXLAR
- Bit
- Svart
- utmanar
- byta
- stängt
- koda
- Gemensam
- anslutning
- innehåller
- innehåll
- samordna
- kunde
- Skapa
- Aktuella
- datum
- djupare
- ner
- slutar
- etablerade
- allt
- exempel
- Utom
- experimentera
- Slutligen
- Förnamn
- passa
- flöda
- hittade
- ytterligare
- spalt
- få
- Välgörenhet
- har
- höjd
- hjälpa
- här.
- Markerad
- Hur ser din drömresa ut
- HTTPS
- identifiera
- bild
- innefattar
- ingår
- IT
- sig
- känd
- läckage
- LÄRA SIG
- lärt
- hävstångs
- linje
- liten
- såg
- älskar
- GÖR
- Framställning
- förvaltade
- matte
- emot
- mer
- mest
- flytta
- MS
- antal
- nummer
- offset
- Okej
- öppet
- optimerad
- annat
- Mönster
- fysisk
- Punkt
- placerad
- möjlig
- den mäktigaste
- pretty
- pris
- process
- ger
- kvalitet
- rund
- rinnande
- Nämnda
- sömlös
- Serier
- in
- former
- liknande
- Enkelt
- SEX
- Storlek
- So
- lösning
- något
- Utrymme
- Spot
- starta
- startar
- Som stöds
- tala
- Genom
- topp
- transparent
- självstudiekurser
- användning
- värde
- utsikt
- W3
- Kolla på
- välkommen
- Vad
- inom
- utan
- Arbete
- arbetssätt
- fungerar
- värt
- Youtube