SVG + javascript & CSS == ♥

Skrevet av Pål Strøm -

KIS! Mye eksperimentell kode her, så det er kun verifisert i Chrome. Sitter du med en annen nettleser, kan jeg ikke garantere at du kommer til å trives maksimalt.

I utvikling av ny nettside har vi jobbet mye med SVG. Og måter å lage interaktivitet med SVG med CSS og javascript. Og det er det mye kjærleik å hente.

Grunnen til dette er nok fordi vi de siste årene har begynt å bruke SVG til ting det egentlig ikke skulle brukes til. Akkurat som vi har gjort med alt annet på web. Det finnes folk som tar det til det ekstreme, som alltid. Noen av de teknikkene som er i bruk her har vi "lånt" og tatt i bruk på vår egen nettside enkelte steder.

Det er et par ting vi har funnet ut når vi har holdt på med SVG til nettsiden. Jeg tenkte å ta tak én av dem, et kult lite triks når man skal koble javascript opp mot SVG og jobbe med enkeltdelene, det være seg polygoner, paths, circle, eller hva du nå enn har tenkt å putte inn i den SVG-en din.

SVG

Her er utgangspunktet mitt. En SVG som jeg har bare har eksportert rett ut fra Illustrator:


Som du kan se, er det litt issues med glippe mellom hver polygon. Dette har med eksportpresisjon, som Illustrator sliter med, og rendering av former. Det er måter å fikse dette på, men det innebærer en del manuell jobbing, så det får heller bli en annen post.

Tanken vår var at man skulle kunne endre hver enkelt polygon med musepekeren. Etter å ha kjørt SVG-filen fra Illustrator gjennom svgo (reduserte filstørrelsen med 22%), har jeg en SVG med en haug med path i seg. Fordelen med det er at absolutt alle elementene i SVG-en min nå er path, så jeg slipper å skrive CSS/javascript for polygon/rectangle/text osv.

Den enkleste måten å få interaksjon med SVG-en på, er å bare legge på en :hover-selector i CSSen. Da vil jeg få dette resultatet:


CSS
	
path {
    transition: transform .2s ease;
    transform-origin: center center;
}
path:hover { transform: scale(.8); }
	

Funker helt OK. Men det blir litt dølt. Så vi tenkte at vi skulle gjøre muserpekeren litt større, slik at den treffer flere polygoner samtidig.


Som ser litt naisere ut.

Javascript

For å få dette til å funke må vi gjøre et par knep i javascript, og det går ut på å finne ut hvor alle polygonene er, slik at vi finne polygoner som er i nærheten av musepekeren. Så vi starter med å lage oss en array som inneholder alle polygonene våre, for deretter å gå gjennom alle polygonene som er i SVG-en og lagre plasseringen.

JS
	
var rects = [],
    polygons = Array.apply(null, document.querySelectorAll('svg path')),
for (var i = 0; i < polygons.length; i++) {
    var poly = polygons[i];
    var off = poly.getBoundingClientRect();

    rects.push({
        left: off.left,
        right: off.left + off.width,
        top: off.top + scrollTop,
        bottom: off.top + scrollTop + off.height,
        obj: poly
    });
}
	

Grunnen til at jeg bruker jQuery sin offset() er bare en snarvei til å få offset fra document-toppen, hvor getBoundingClientRect() gir meg offset i forhold til skjermen. Her kan man så klart traversere DOM for å finne rett offset fra top, osv., men når man først har jQuery, hvorfor ikke bruke det. Så bruker jeg heller getBoundingClientRect() til høyde og bredde, da disse er raskere enn jQuery sin width() og height(). Jeg behandler også alle polygoner som rektangler for enkelhets skyld:


Så 100% treffsikker er vi ikke i dette tilfellet.

Jeg har også tatt vare på selve polygonen med obj: $poly, for å ha hurtig tilgang til selve DOM-elementet når jeg skal manipulere det senere.

Neste steg er å legge til en event-lytter på SVG-en, slik at vi kan detektere hvor musepekeren er når den over SVG-en et eller annet sted.

JS
	
var area = 40;
document.querySelector('svg.js').addEventListener("mousemove", function (e) {
    var mXmin = e.pageX - area,
        mXmax = mXmin + area * 2,
        mYmin = e.pageY - area,
        mYmax = mYmin + area * 2;
    for (i = 0; i < rects.length; i++) {
        var rect = rects[i];
        var overlap = !(mXmax < rect.left ||
                        mXmin > rect.right ||
                        mYmax < rect.top ||
                        mYmin > rect.bottom);
        if (overlap) {
            rect.obj.style.transform = "scale(.8)";
        } else {
            rect.obj.style.transform = "scale(1)";
        }
    }
});
	

Her er det rett og slett bare en god gammeldags kollisjonsdeteksjon som kjøres. Jeg har laget men en variabel som heter area for å styre størrelsen på området musepekeren skal dekke. På grunn av at alle polygoner ligger i minnet allerede er det lynkjapt å gå gjennom alle for å kjøre kollisjonsdeteksjonen. Operasjonen tar i snitt rundt 5ms. (Åpne console i nettleseren din og sjekke selv, da vel.)

Videre kan vi jobbe mer med dette å holde på posisjonen til hver enkelt polygon. Vi kan for eksempel få alle polygonene til å eksplodere ut fra sentrum av SVG-en:


Trykk på ansiktet for awesome effekter.

Her er det mye samme kode som brukes. Vi finner alle polygoner og regner ut hvor de er i forhold til midtpunktet til SVG-elementet. Deretter kan vi flytte dem proposjonalt bort fra midtpunktet.

Håper det har gitt deg noen ideer til hva man kan gjøre med SVG med CSS og javascript. Vi har laget mye rart, som har blitt forkastet, til vår nye nettside. Leking og eksperimentering med SVG er rett og slett:

Vi bruker Google Analytics for å spore aktiviteten på nettsiden vår. Dataen bruker vi til å tilpasse design, navigasjon eller innhold. Og så sporer vi om du kom hit via en Google-annonse. Les mer i vår personvernerklæring.