Jeg har for nylig lavet et murstensvægmønster som en del af mit #PetitePatterns serie, en udfordring, hvor jeg skaber organisk udseende mønstre eller teksturer i SVG inden for 560 bytes (eller omtrent på størrelse med to tweets). For at passe til denne begrænsning har jeg gennemgået en rejse, der har lært mig nogle radikale måder at optimere SVG-mønstre på, så de indeholder så lidt kode som muligt uden at påvirke den overordnede billedkvalitet.
Jeg vil gerne lede dig gennem processen og vise dig, hvordan vi kan tage et SVG-mønster, der starter ved 197 bytes helt ned til blot 44 bytes - en kæmpe reduktion på 77.7 %!
SVG-mønsteret
Dette er det, der kaldes et "løbende bånd" murstensmønster. Det er det mest almindelige murstensmønster derude, og et du helt sikkert har set før: hver række af mursten er forskudt med halvdelen af en murstens længde, hvilket skaber et gentaget forskudt mønster. Arrangementet er ret simpelt, hvilket gør SVG'er <pattern>
element passer perfekt til at gengive det i kode.
SVG <pattern>
element bruger et foruddefineret grafisk objekt, som kan replikeres (eller "tillægges") med faste intervaller langs den vandrette og lodrette akse. I det væsentlige definerer vi et rektangulært flisemønster, og det bliver gentaget for at male fyldområdet.
Lad os først indstille dimensionerne på en mursten og afstanden mellem hver mursten. Lad os for nemhedens skyld bruge rene, runde tal: en bredde på 100
og en højde på 30
for mursten, og 10
for de vandrette og lodrette mellemrum mellem dem.
Dernæst skal vi identificere vores "base" flise. Og med "fliser" taler jeg om mønsterfliser frem for fysiske fliser, ikke at forveksle med klodserne. Lad os bruge den fremhævede del af billedet ovenfor som vores mønsterflise: to hele klodser i første række og en hel klemt mellem to halve klodser i anden række. Læg mærke til, hvordan og hvor hullerne er inkluderet, fordi de skal medtages i den gentagne mønsterflise.
Når du bruger <pattern>
, vi er nødt til at definere mønstret width
, height
, som svarer til bredden og højden af bundflisen. For at få dimensionerne har vi brug for lidt 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
Okay, så vores mønsterflise er 220✕80
. Vi skal også indstille patternUnits
attribut, hvor værdien userSpaceOnUse
betyder i bund og grund pixels. Til sidst tilføjes en id
til mønsteret er nødvendigt, så det kan refereres, når vi maler et andet element med det.
<pattern id="p" width="220" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>
Nu hvor vi har etableret flisedimensionerne, er udfordringen at skabe koden til flisen på en måde, der gør grafikken med det mindst mulige antal bytes. Dette er, hvad vi håber at ende med til allersidst:
Indledende opmærkning (197 bytes)
Den enkleste og mest deklarative tilgang til at genskabe dette mønster, der falder mig ind, er at tegne fem rektangler. Som standard er fill
af et SVG-element er sort og stroke
er gennemsigtig. Dette fungerer godt til at optimere SVG-mønstre, da vi ikke eksplicit behøver at angive dem i koden.
Hver linje i koden nedenfor definerer et rektangel. Det width
, height
er altid indstillet, og de x
, y
positioner indstilles kun, hvis et rektangel er forskudt fra 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 øverste række af flisen indeholdt to mursten i fuld bredde, den anden mursten er placeret til x="110"
så 10
pixel mellemrum før murstenen. På samme måde er der 10
pixels af mellemrum efter, fordi klodsen ender kl 210
pixels (110 + 100 = 210
) på den vandrette akse, selvom den <pattern>
bredde er 220
pixels. Vi har brug for den lille smule ekstra plads; ellers ville den anden klods smelte sammen med den første klods i den tilstødende flise.
Klodserne i den anden (nederste) række er forskudt, så rækken indeholder to halve klodser og en hel klods. I dette tilfælde ønsker vi at klodserne i halv bredde skal smelte sammen, så der ikke er noget hul i starten eller slutningen, hvilket giver dem mulighed for problemfrit at flyde med klodserne i tilstødende mønsterfliser. Når vi udligner disse klodser, skal vi også medtage halve huller, således x
værdier er 55
, 165
, henholdsvis.
Genbrug af element, (-43B, 154B i alt)
Det virker ineffektivt at definere hver mursten så eksplicit. Er der ikke en måde at optimere SVG-mønstre på ved at genbruge formerne i stedet for?
Jeg tror ikke, det er almindeligt kendt, at SVG har en <use>
element. Du kan referere til et andet element med det og gengive det refererede element hvor som helst <use>
anvendes. Dette sparer en del bytes, fordi vi kan undlade at angive bredden og højden af hver mursten, undtagen den første.
Det er sagt, <use>
kommer med en lille pris. Det vil sige, at vi skal tilføje en id
for det element, vi ønsker at genbruge.
<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 korteste id
muligt er et tegn, så jeg valgte "b" for mursten. Det <use>
element kan placeres på samme måde som <rect>
, med x
, y
attributter som forskydninger. Da hver klods er i fuld bredde, nu hvor vi har skiftet til <use>
(husk, vi har eksplicit halveret klodserne i anden række af mønsterflisen), vi skal bruge en negativ x
værdi i anden række, og sørg derefter for, at den sidste klods flyder over fra flisen for den sømløse forbindelse mellem klodserne. Disse er dog okay, fordi alt, der falder uden for mønsterflisen, skæres automatisk af.
Kan du få øje på nogle gentagne strenge, der kan skrives mere effektivt? Lad os arbejde på dem næste.
Omskrivning til sti (-54B, 100B i alt)
<path>
er nok det mest kraftfulde element i SVG. Du kan tegne næsten enhver form med "kommandoer" i dens d
attribut. Der er 20 kommandoer tilgængelige, men vi har kun brug for de enkleste til rektangler.
Her er hvor jeg landede med det:
<path d="M0 0h100v30h-100z M110 0h100v30h-100 M0 40h45v30h-45z M55 40h100v30h-100z M165 40h55v30h-55z"/>
Jeg ved det, super mærkelige tal og bogstaver! De har alle mening, selvfølgelig. Her er, hvad der sker i dette specifikke tilfælde:
M{x} {y}
: Flytter til et punkt baseret på koordinater.z
: Lukker det aktuelle segment.h{x}
: Tegner en vandret linje fra det aktuelle punkt med længden påx
i retningen defineret af tegnet påx
. Små bogstaverx
angiver en relativ koordinat.v{y}
: Tegner en lodret linje fra det aktuelle punkt med længden påy
i retningen defineret af tegnet påy
. Små bogstavery
angiver en relativ koordinat.
Denne opmærkning er meget mere kortfattet end den forrige (linjeskift og indryknings-mellemrum er kun for læsbarheden). Og hej, vi har formået at skære halvdelen ud af den oprindelige størrelse, og nået frem til 100 bytes. Alligevel er der noget, der får mig til at føle, at dette kunne være mindre...
Fliserevision (-38B, 62B i alt)
Har vores mønsterflise ikke gentagende dele? Det er tydeligt, at i den første række gentages en hel klods, men hvad med den anden række? Det er lidt sværere at se, men hvis vi skærer den midterste mursten over, bliver det tydeligt.
Nå, den midterste mursten er ikke ligefrem skåret i to. Der er en lille udligning, fordi vi også skal tage højde for forskellen. Anyways, vi har lige fundet et enklere grundflisemønster, hvilket betyder færre bytes! Det betyder også, at vi skal halvere width
vores <pattern>
element fra 220 til 110.
<pattern id="p" width="110" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>
Lad os nu se, hvordan den forenklede flise er tegnet med <path>
:
<path d="M0 0h100v30h-100z M0 40h45v30h-45z M55 40h55v30h-55z"/>
Størrelsen er reduceret til 62 bytes, hvilket allerede er mindre end en tredjedel af den oprindelige størrelse! Men hvorfor stoppe her, når der er endnu mere, vi kan gøre!
Forkorte sti-kommandoer (-9B, 53B i alt)
Det er værd at komme lidt dybere ind i <path>
element, fordi det giver flere tip til optimering af SVG-mønstre. En misforståelse jeg har haft, da jeg arbejdede med <path>
handler om, hvordan fill
egenskab virker. Efter at have leget meget med MS Paint i min barndom, har jeg lært, at enhver form, jeg vil udfylde med en ensfarvet, skal være lukket, dvs. ingen åbne punkter. Ellers vil malingen lække ud af formen og vælte ud over alt.
I SVG er dette dog ikke sandt. Lad mig citere spec selv:
Fyldoperationen udfylder åbne understier ved at udføre fyldoperationen, som om en yderligere "luk sti"-kommando blev tilføjet til stien for at forbinde det sidste punkt på understien med det første punkt i understien.
Det betyder, at vi kan udelade kommandoerne for tæt sti (z
), fordi understierne betragtes som automatisk lukkede, når de er udfyldt.
En anden nyttig ting at vide om stikommandoer er, at de kommer i store og små bogstaver. Små bogstaver betyder, at der bruges relative koordinater; store bogstaver betyder, at absolutte koordinater bruges i stedet.
Det er lidt sværere end det med H
, V
kommandoer, fordi de kun indeholder én koordinat. Sådan vil jeg beskrive disse to kommandoer:
H{x}
: Tegner en vandret linje fra det aktuelle punkt for at koordinerex
.V{y}
: Tegner en lodret linje fra det aktuelle punkt for at koordinerey
.
Når vi tegner den første klods i mønsterflisen, tager vi udgangspunkt i (0,0)
koordinater. Vi tegner så en vandret linje til (100,0)
og en lodret linje til (100,30)
, og til sidst tegne en vandret linje til (0,30)
. Vi brugte h-100
kommando i sidste linje, men det svarer til H0
, hvilket er to bytes i stedet for fem. Vi kan erstatte to lignende forekomster og parere koden for vores <path>
ned til dette:
<path d="M0 0h100v30H0 M0 40h45v30H0 M55 40h55v30H55"/>
Yderligere 9 bytes barberet af - hvor meget mindre kan vi gå?
Brodannelse (-5B, 48B i alt)
De længste kommandoer, der står i vejen for et fuldt optimeret SVG-mønster, er "flyt til"-kommandoer, som fylder henholdsvis 4, 5 og 6 bytes. En begrænsning vi har er at:
Et stidatasegment (hvis der er et) skal begynde med en "moveto"-kommando.
Men det er okay. Den første er i hvert fald den korteste. Hvis vi bytter rækkerne, kan vi komme med en stidefinition, hvor vi kun skal bevæge os enten vandret eller lodret mellem klodserne. Hvad hvis vi kunne bruge h
, v
kommandoer der i stedet for M
?
Ovenstående diagram viser, hvordan de tre figurer kan tegnes med en enkelt sti. Bemærk, at vi udnytter det faktum, at fill
drift lukker automatisk den åbne del mellem (110,0)
, (0,0)
. Med denne omarrangering flyttede vi også hullet til venstre for murstenen i fuld bredde i anden række. Sådan ser koden ud, stadig opdelt i én klods pr. linje:
<path d="M0 0v30h50V0 h10v30h50 v10H10v30h100V0"/>
Sikkert, vi har fundet den absolut mindste løsning nu, hvor vi er nede på 48 bytes, ikke?! Godt…
Cifferbeskæring (-4B, 44B i alt)
Hvis du kan være lidt fleksibel med dimensionerne, er der en anden lille måde, vi kan optimere SVG-mønstre på. Vi har arbejdet med en murstensbredde på 100
pixels, men det er tre bytes. Ændrer det til 90
betyder en byte mindre, når vi skal skrive det. På samme måde brugte vi et hul på 10
pixels — men hvis vi ændrer det til 8
i stedet gemmer vi en byte på hver af disse forekomster.
<path d="M0 0v30h45V0 h8v30h45 v8H8v30h90V0"/>
Det betyder selvfølgelig også, at vi skal justere mønsterdimensionerne derefter. Her er den endelige optimerede SVG-mønsterkode:
<pattern id="p" width="98" height="76" patternUnits="userSpaceOnUse"> <path d="M0 0v30h45V0h8v30h45v8H8v30h90V0"/>
</pattern>
Den anden linje i ovenstående uddrag - uden fordybninger - er 44 bytes. Vi kom her fra 197 bytes i seks iterationer. Det er en tyk 77.7 % størrelsesreduktion!
Jeg spekulerer dog på... er dette virkelig den mindste størrelse muligt? Har vi set på alle mulige måder at optimere SVG-mønstre på?
Jeg inviterer dig til at prøve at minimere denne kode yderligere, eller endda eksperimentere med alternative metoder til optimering af SVG-mønstre. Jeg ville elske at se, om vi kunne finde det sande globale minimum med mængdens visdom!
Mere om oprettelse og optimering af SVG-mønstre
Hvis du er interesseret i at lære mere om at skabe og optimere SVG-mønstre, så læs min artikel om skabe mønstre med SVG-filtre. Eller, hvis du vil se et galleri med mere end 60 mønstre, kan du se PetitePatterns CodePen Collection. Til sidst er du velkommen til at se med mine tutorials på YouTube for at hjælpe dig med at komme endnu dybere ind i SVG-mønstre.
Optimering af SVG-mønstre til deres mindste størrelse oprindeligt udgivet den CSS-tricks. Du burde få nyhedsbrevet.
- "
- 10
- 100
- 77
- 9
- 98
- Om
- absolutte
- Konto
- Yderligere
- Alle
- tillade
- allerede
- En anden
- tilgang
- OMRÅDE
- artikel
- attributter
- til rådighed
- AKSER
- Bit
- Sort
- udfordre
- lave om
- lukket
- kode
- Fælles
- tilslutning
- indeholder
- indhold
- koordinere
- kunne
- Oprettelse af
- Nuværende
- data
- dybere
- ned
- ender
- etableret
- at alt
- eksempel
- Undtagen
- eksperiment
- Endelig
- Fornavn
- passer
- flow
- fundet
- yderligere
- kløft
- få
- Global
- have
- højde
- hjælpe
- link.
- Fremhævet
- Hvordan
- HTTPS
- identificere
- billede
- omfatter
- medtaget
- IT
- selv
- kendt
- lække
- LÆR
- lærte
- løftestang
- Line (linje)
- lidt
- kiggede
- kærlighed
- maerker
- Making
- lykkedes
- matematik
- tankerne
- mere
- mest
- bevæge sig
- MS
- nummer
- numre
- offset
- Okay
- åbent
- optimeret
- Ellers
- Mønster
- fysisk
- Punkt
- positionerede
- mulig
- vigtigste
- smuk
- pris
- behandle
- giver
- kvalitet
- rundt
- kører
- Said
- sømløs
- Series
- sæt
- former
- lignende
- Simpelt
- SIX
- Størrelse
- So
- løsninger
- noget
- Space
- Spot
- starte
- starter
- Understøttet
- taler
- Gennem
- top
- gennemsigtig
- tutorials
- brug
- værdi
- Specifikation
- W3
- Ur
- velkommen
- Hvad
- inden for
- uden
- Arbejde
- arbejder
- virker
- værd
- youtube