Opprette en Panorering Effekt for SVG

0
47

Tidligere denne måneden på Animasjon på Jobb Slakk, vi hadde en diskusjon om å finne en måte å la brukere pan inne i en SVG.

Jeg laget denne demoen nedenfor for å vise hvordan jeg skulle nærme seg dette spørsmålet:

Se Penn Demo – SVG Panorering av Louis Hoebregts (@Mamboleoo) på CodePen.

Her er de fire trinnene for å gjøre ovenfor demo-arbeid:

  1. Få musen og trykk på hendelser fra brukeren
  2. Beregne musen kvoter fra sin opprinnelse
  3. Lagre den nye viewBox koordinater
  4. Håndtere dynamisk vindu

La oss sjekke disse trinnene på en og en mer grundig.

1. Mus & Touch Arrangementer

For å få musen eller trykke posisjon, må vi først legge til hendelsen lyttere på våre SVG. Vi kan bruke Pekeren Hendelser til å håndtere alle slags tips (mus/touch/penn/…), men disse hendelsene er ennå ikke støttet av alle nettlesere. Vi trenger å legge til noen tilbakefall å sørge for at alle brukere vil være i stand til å dra SVG.

// Velger vi SVG til side
var svg = – dokument.querySelector(‘svg’);

// Hvis leseren støtter pekeren arrangementer
if (window.PointerEvent) {
svg.addEventListener(‘pointerdown’, onPointerDown); // Peker trykkes
svg.addEventListener(‘pointerup’, onPointerUp); // å Slippe pekeren
svg.addEventListener(‘pointerleave’, onPointerUp); // Peker får ut av SVG-området
svg.addEventListener(‘pointermove’, onPointerMove); // Musepekeren beveger seg
} else {
// Legger til alle mus hendelser lyttere fallback
svg.addEventListener(‘mousedown’, onPointerDown); // ved å Trykke på musen
svg.addEventListener(‘mouseup’, onPointerUp); // å Slippe musen
svg.addEventListener(‘mouseleave’, onPointerUp); // Mus kommer ut av SVG-området
svg.addEventListener(‘mousemove’, onPointerMove); // Musen for bevegelse

// Legger til alle touch hendelser lyttere fallback
svg.addEventListener(‘touchstart’, onPointerDown); // Finger er å berøre skjermen
svg.addEventListener(‘touchend’, onPointerUp); // Finger er ikke lenger å berøre skjermen
svg.addEventListener(‘touchmove’, onPointerMove); // Finger beveger seg
}

Fordi vi kunne ha kontakt hendelser og peker hendelser, må vi lage en liten funksjonen returnerer til koordinater, enten fra den første fingeren enten fra en peker.

// Denne funksjonen returnerer et objekt med X-Og Y-verdier fra pekeren event
funksjonen getPointFromEvent (hendelse) {
var tidspunkt = {x:0 y:0};
// Hvis hendelsen er utløst av en touch-event, får vi posisjonen til den første fingeren
hvis (event.targetTouches) {
punkt.x = event.targetTouches[0].clientX;
punkt.y = – event.targetTouches[0].clientY;
} else {
punkt.x = event.clientX;
punkt.y = – event.clientY;
}

returpunktet;
}

Når siden er klar og venter for en bruker vekselsvirkningene, kan vi begynne å håndtere mousedown/touchstart hendelser for å lagre de opprinnelige koordinatene for markøren og opprette en variabel til å la oss få vite hvis pekeren er nede eller ikke.

// Denne variabelen vil bli brukt senere for å flytte arrangementer for å sjekke hvis pekeren er nede eller ikke
var isPointerDown = false;

// Denne variabelen vil inneholde det opprinnelige koordinatene når brukeren starter å trykke på musen eller trykker på skjermen
var pointerOrigin = {
x: 0,
y: 0
};

// Funksjon kalt av arrangementet lyttere når brukeren starter å trykke på/berøre
funksjonen onPointerDown(hendelse) {
isPointerDown = true; // Vi sett pekeren som ned

// Vi få pekeren posisjon på klikk/touchdown, slik at vi kan få verdi når brukeren begynner å dra
var pointerPosition = getPointFromEvent(event);
pointerOrigin.x = pointerPosition.x;
pointerOrigin.y = pointerPosition.y;
}

2. Beregne Kvoter Musen

Nå som vi har koordinatene til den opprinnelige posisjonen der brukeren begynte å dra på innsiden av SVG, kan vi beregne avstanden mellom den aktuelle pekeren posisjon og sin opprinnelse. Vi gjør dette for både X-og Y-aksen, og vi bruker de beregnede verdiene på viewBox.

// Vi lagre de opprinnelige verdiene fra viewBox
var viewBox = {
x: 0,
y: 0,
bredde: 500,
høyde: 500
};

// Avstanden beregnes fra pekeren vil bli lagret her
var newViewBox = {
x: 0,
y: 0
};

// Funksjon kalt av arrangementet lyttere når brukeren begynner å flytte/dra
funksjonen onPointerMove (hendelse) {
// Bare kjøre denne funksjonen hvis pekeren er nede
if (!isPointerDown) {
tilbake;
}
// Dette hindre brukeren til å gjøre et utvalg på siden
event.preventDefault();

// Få pekeren posisjon
var pointerPosition = getPointFromEvent(event);

// Vi beregne avstanden mellom pekeren opprinnelse og gjeldende posisjon
// ViewBox x-og y-verdier skal beregnes fra den opprinnelige verdier og avstander
newViewBox.x = viewBox.x – (pointerPosition.x – pointerOrigin.x);
newViewBox.y = viewBox.y – (pointerPosition.y – pointerOrigin.y);

// Lager vi en strengen med den nye viewBox verdier
// X-Og Y-verdier som er lik den aktuelle viewBox minus beregnet avstander
var viewBoxString = `${newViewBox.x} ${newViewBox.y} ${viewBox.bredde} ${viewBox.høyde}`;
// Vi bruke den nye viewBox verdier på SVG
svg.setAttribute(‘viewBox’, viewBoxString);

dokumentet.querySelector(‘.viewbox’).innerHTML = viewBoxString;
}

Hvis du ikke føler deg komfortabel med begrepet viewBox, foreslår jeg at du først leser denne flotte artikkelen av Sara Soueidan.

3. Lagre Oppdatert viewBox

Nå som viewBox har blitt oppdatert, trenger vi å lagre nye verdier når brukeren slutter å dra SVG.

Dette trinnet er viktig, fordi ellers vil vi alltid beregne pekeren kvoter fra den opprinnelige viewBox verdier og brukeren vil dra SVG som utgangspunkt hver gang.

funksjonen onPointerUp() {
// Pekeren er ikke lenger ansett som så ned
isPointerDown = false;

// Vi lagre viewBox koordinater basert på siste pekeren kvoter
viewBox.x = newViewBox.x;
viewBox.y = newViewBox.y;
}

4. Håndtere Dynamisk Vindu

Hvis vi setter en tilpasset bredden på våre SVG-filer, kan du legge merke til mens du drar på demo nedenfor at fuglen beveger seg enten raskere eller langsommere enn pekeren.

Se Penn Dynamisk viewport – SVG Panorering av Louis Hoebregts (@Mamboleoo) på CodePen.

På den opprinnelige demo, SVG bredden er nøyaktig med sin viewBox bredde. Den faktiske størrelsen av SVG kan også bli kalt viewport. I en perfekt situasjon, når brukeren beveger seg sine pekeren ved 1px, vil vi viewBox til å oversette ved 1px.

Men, mesteparten av tiden, SVG har en forståelsesfull størrelse og viewBox vil mest sannsynlig ikke vil matche den SVG viewport. Hvis SVG-bredden er dobbelt så stor enn viewBox, når brukeren beveger seg sine pekeren ved 1px, bildet inne i SVG vil oversette med 2px.

For å fikse dette, vi trenger for å beregne forholdet mellom viewBox og viewport og bruke dette forholdet, mens beregning av den nye viewBox. Dette forholdet må også være oppdatert når det SVG-størrelse kan endres.

// Beregne forholdet basert på viewBox bredde og SVG-bredde
var ratio = viewBox.bredde – / svg.getBoundingClientRect().bredde;
vinduet.addEventListener(‘resize’, function() {
ratio = viewBox.bredde – / svg.getBoundingClientRect().bredde;
});

Når vi kjenner forholdet, må vi multiplisere musen forskyvning av forholdet til proporsjonalt øke eller redusere kvotene.

funksjonen onMouseMove (e) {
[…]
newViewBox.x = viewBox.x – ((pointerPosition.x – pointerOrigin.x) * forhold);
newViewBox.y = viewBox.y – ((pointerPosition.y – pointerOrigin.y) * forhold);
[…]
}

Her er hvordan det fungerer med et mindre vindu enn viewBox bredde:

Se Penn Mindre viewport – SVG Panorering av Louis Hoebregts (@Mamboleoo) på CodePen.

Og en annen demo med en viewport større enn viewBox bredde:

Se Penn Større viewport – SVG Panorering av Louis Hoebregts (@Mamboleoo) på CodePen.

[Bonus] Optimalisering av kode

For å gjøre våre etiske litt kortere, det er to svært nyttige begreper i SVG-vi kunne bruke.

SVG Poeng

Det første konseptet er å bruke SVG-Poeng i stedet for grunnleggende Javascript-objekter for å lagre musepekeren posisjoner. Etter å ha opprettet en ny SVG-Punkt variabel, kan vi bruke noen Matrix Transformasjon på den for å konvertere posisjon i forhold til skjermen til en posisjon i forhold til gjeldende SVG bruker enheter.

Sjekk koden under for å se hvordan den fungerer getPointFromEvent() og onPointerDown() har endret seg.

// Lager en SVG-tilgangspunkt som inneholder x og y verdier
var tidspunkt = svg.createSVGPoint();

funksjonen getPointFromEvent (hendelse) {
hvis (event.targetTouches) {
punkt.x = event.targetTouches[0].clientX;
punkt.y = – event.targetTouches[0].clientY;
} else {
punkt.x = event.clientX;
punkt.y = – event.clientY;
}

// Vi får den gjeldende transformasjon matrise av SVG-og vi inverse det
var invertedSVGMatrix = svg.getScreenCTM().invers();

returpunktet.matrixTransform(invertedSVGMatrix);
}

var pointerOrigin;
funksjonen onPointerDown(hendelse) {
isPointerDown = true; // Vi sett pekeren som ned

// Vi få pekeren posisjon på klikk/touchdown, slik at vi kan få verdi når brukeren begynner å dra
pointerOrigin = getPointFromEvent(event);
}

Ved hjelp av SVG -) Poeng, du behøver ikke engang å håndtere endringene som har søkt på SVG! Sammenlign følgende to eksempler hvor den første er brutt når en rotasjon brukes på SVG og andre eksemplet bruker SVG-Poeng.

Se Penn Demo + transformasjon – SVG Panorering av Louis Hoebregts (@Mamboleoo) på CodePen.

Se Penn Demo-Bonus + transform – SVG Panorering av Louis Hoebregts (@Mamboleoo) på CodePen.

SVG Animerte Rect

Den andre ukjent begrep i SVG-vi kan bruke til å forkorte koden vår, er bruken av Animerte Rect.

Fordi viewBox er faktisk ansett som en SVG-Rektangel (x, y, bredde, høyde), kan vi lage en variabel fra sin base verdi som vil automatisk oppdatere viewBox hvis vi oppdaterer denne variabelen.

Se hvordan det er lettere nå å oppdatere viewBox av våre SVG!

// Vi lagre de opprinnelige verdiene fra viewBox
var viewBox = svg.viewBox.baseVal;

funksjonen onPointerMove (hendelse) {
if (!isPointerDown) {
tilbake;
}
event.preventDefault();

// Få pekeren posisjon som en SVG-Punkt
var pointerPosition = getPointFromEvent(event);

// Oppdatere viewBox variabel med avstanden fra opprinnelse og nåværende stilling
// Vi ikke trenger å ta vare på forholdet, fordi dette er håndtert i getPointFromEvent funksjon
viewBox.x -= (pointerPosition.x – pointerOrigin.x);
viewBox.y -= (pointerPosition.y – pointerOrigin.y);
}

Og her er den siste demo. Se hvor mye kortere koden er nå? 😀

Se Penn Demo-Bonus – SVG Panorering av Louis Hoebregts (@Mamboleoo) på CodePen.

Konklusjon

Denne løsningen er definitivt ikke den eneste veien å gå for å håndtere en slik atferd. Hvis du allerede bruker et bibliotek for å håndtere din SVGs, kan den allerede har en innebygd funksjon for å håndtere det.

Jeg håper denne artikkelen kan hjelpe deg til å forstå litt mer hvor kraftig SVG kan være! Føl deg fri til å bidra til koden ved å kommentere med dine ideer eller alternativer til denne løsningen.

Studiepoeng

  • Fugl designet av Freepik
  • En stor takk til Blake for hans dyrebare hjelp og alle de fine folkene fra AAW Slakk for sine tilbakemeldinger.