Skapa en Panorering Effekt för SVG

0
35

Tidigare denna månad på Animation på Jobbet Slack, vi hade en diskussion om att hitta ett sätt att låta användare pan inne i en SVG.

Jag gjorde denna demo nedan för att visa hur jag skulle närma oss denna fråga:

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

Här är de fyra stegen för att göra ovanstående demo arbete:

  1. Få musen och tryck händelser från användaren
  2. Beräkna musen avvikelser från dess ursprung
  3. Spara den nya viewBox koordinater
  4. Hantera dynamiska virtuella

Låt oss kontrollera dem steg en efter en mer ingående.

1. Mus & Touch Händelser

För att få musen eller tryck på position, måste vi först lägga till händelse lyssnare på vår SVG. Vi kan använda Pekaren Händelser för att hantera alla typer av pekare (mus (mouse)/touch/stylus/…), men dessa händelser är ännu inte stöds av alla webbläsare. Vi kommer att behöva lägga till vissa fallback för att se till att alla användare kommer att kunna dra SVG.

// Vi väljer SVG till sidan
var svg – = – dokument.querySelector(‘svg’);

// Om webbläsaren har stöd för pekaren händelser
if (window.PointerEvent) {
svg.addEventListener(‘pointerdown’, onPointerDown); // Pekare trycks
svg.addEventListener(‘pointerup’, onPointerUp); // släpp-pekare
svg.addEventListener(‘pointerleave’, onPointerUp); // Pekare får ut av SVG-området
svg.addEventListener(‘pointermove’, onPointerMove); // Pekaren rör sig
} else {
// Lägg till alla mus händelser lyssnare fallback
svg.addEventListener(‘mousedown’, onPointerDown); // att Trycka på musen
svg.addEventListener(‘funktioner’, onPointerUp); // släpp musen
svg.addEventListener(‘mouseleave’, onPointerUp); // Musen får ut av SVG-området
svg.addEventListener(‘mousemove’, onPointerMove); // Musen rör sig

// Lägg till alla touch händelser lyssnare fallback
svg.addEventListener(‘touchstart’, onPointerDown); // Fingret vidrör skärmen
svg.addEventListener(‘touchend’, onPointerUp); // Finger är inte längre att peka på skärmen
svg.addEventListener(‘touchmove’, onPointerMove); // Finger rör sig
}

Eftersom vi kunde ha kontakt händelser och pekare händelser, måste vi skapa en liten funktion återgår till koordinater antingen från första finger antingen från en pekare.

// Denna funktion returnerar ett objekt med X-Och Y-värden från pekaren händelse
funktion getPointFromEvent (event) {
var point = {x:0, y:0};
// Om händelsen utlöses av en touch-event, vi får positionen för den första finger
om (händelse.targetTouches) {
punkt.x = evenemanget.targetTouches[0].clientX;
punkt.y = – händelse.targetTouches[0].clientY;
} else {
punkt.x = evenemanget.clientX;
punkt.y = – händelse.clientY;
}

avkastning punkt.
}

När sidan är klar och väntar på att någon användarinteraktion, vi kan börja hantera mousedown/touchstart händelser för att spara den ursprungliga koordinaterna för pekaren och skapa en variabel för att låta oss veta om pekaren är nere eller inte.

// Denna variabel kommer att användas senare för att flytta händelser för att kolla om pekaren är nere eller inte
var isPointerDown = false;

// Denna variabel innehåller de ursprungliga koordinaterna när användaren börja trycka på musen eller trycker på skärmen
var pointerOrigin = {
x: 0,
y: 0
};

// Funktion som kallas av händelsen lyssnare när användaren börjar att trycka på/röra
funktion onPointerDown(event) {
isPointerDown = true; // Vi sätter pekaren nedåt

// Vi få pekaren position på klicka/touchdown så vi kan få värde när användaren börjar att dra
var pointerPosition = getPointFromEvent(event);
pointerOrigin.x = pointerPosition.x;
pointerOrigin.y = pointerPosition.y;
}

2. Beräkna Förskjutningar Musen

Nu när vi har koordinaterna för den ursprungliga position där användaren börjat dra inuti SVG, kan vi beräkna avståndet mellan den nuvarande pekaren position och dess ursprung. Vi gör detta för både X-och Y-axeln och vi tillämpar de beräknade värdena på viewBox.

// Vi sparar de ursprungliga värdena från viewBox
var viewBox = {
x: 0,
y: 0,
bredd: 500,
höjd: 500
};

// Det avstånd som beräknas från pekaren kommer att lagras här
var newViewBox = {
x: 0,
y: 0
};

// Funktion som kallas av händelsen lyssnare när användaren börja flytta/dra
funktion onPointerMove (event) {
// Bara köra den här funktionen om pekaren är ner
if (!isPointerDown) {
återvändande.
}
// Detta förhindrar användaren att göra ett urval på sidan
händelsen.preventDefault();

// Get pekaren position
var pointerPosition = getPointFromEvent(event);

// Vi beräkna avståndet mellan pekaren ursprung och nuvarande position
// ViewBox x-och y-värden ska beräknas från den ursprungliga värdena och avstånd
newViewBox.x = viewBox.x – (pointerPosition.x – pointerOrigin.x);
newViewBox.y = viewBox.y – (pointerPosition.y – pointerOrigin.y);

// Vi skapar en sträng med den nya viewBox värden
// X – & Y-värden är lika med den aktuella viewBox minus det beräknade avstånd
var viewBoxString = `${newViewBox.x} ${newViewBox.y} ${viewBox.bredd} ${viewBox.höjd}`;
// Vi tillämpa de nya viewBox värden på SVG
svg.setAttribute(‘viewBox’, viewBoxString);

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

Om du inte känner dig bekväm med begreppet viewBox, jag skulle föreslå att du först läsa detta bra artikel av Sara Soueidan.

3. Spara Uppdaterade viewBox

Nu när viewBox har uppdaterats, vi måste spara sin nya värden när användaren slutar att dra SVG.

Detta steg är viktigt eftersom annars skulle vi alltid beräkna pekaren avvikelser från den ursprungliga viewBox värderingar och användaren kommer att dra SVG från start varje gång.

funktion onPointerUp() {
// En pekare är inte längre anses ner
isPointerDown = false;

// Vi sparar viewBox koordinater baserat på den senaste pekaren förskjutningar
viewBox.x = newViewBox.x;
viewBox.y = newViewBox.y;
}

4. Hantera Dynamiska Virtuella

Om vi sätter en anpassad bredd på våra SVG, du kan observera medan du drar på demo nedan för att fågeln är i rörelse antingen snabbare eller långsammare än din pekare.

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

På den ursprungliga demo, SVG bredd är exakt matchande dess viewBox bredd. Den faktiska storleken av din SVG kan också kallas vy. I en perfekt situation när användaren flyttar pekaren genom att 1px, vi vill viewBox att översätta med 1px.

Men, de flesta av tiden, SVG har en lyhörd storlek och viewBox kommer sannolikt inte att matcha SVG-vy. Om SVG bredd är dubbelt så stor än viewBox, när användaren flyttar sina pekare av 1px, bild inne i SVG kommer att översätta av 2px.

För att åtgärda detta, vi behöver för att beräkna förhållandet mellan viewBox och det virtuella och tillämpa detta förhållande vid beräkningen av den nya viewBox. Detta förhållande måste också uppdateras när SVG storlek kan ändras.

// Beräkna förhållandet baserat på viewBox bredd och SVG-bredd
var ratio = viewBox.bredd / svg.getBoundingClientRect().bredd;
fönster.addEventListener(‘ändra storlek’, function() {
ratio = viewBox.bredd / svg.getBoundingClientRect().bredd;
});

När vi vet det förhållandet, att vi måste multiplicera musen förskjutningar av förhållandet till en proportionellt sett öka eller minska förskjutningar.

funktion onMouseMove (e) {
[…]
newViewBox.x = viewBox.x – ((pointerPosition.x – pointerOrigin.x) * förhållande).
newViewBox.y = viewBox.y – ((pointerPosition.y – pointerOrigin.y) * förhållande).
[…]
}

Så här fungerar det med en mindre vyport än viewBox bredd:

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

Och en annan demo med en vy som är större än viewBox bredd:

Se Pennan Större vy – SVG Panorering av Louis Hoebregts (@Mamboleoo) på CodePen.

[Bonus] Optimera koden

För att göra vår kod lite kortare, det är två mycket användbara begrepp i SVG vi kunde använda.

SVG-Poäng

Det första konceptet är att använda SVG-Poäng istället för grundläggande Javascript-objekt för att spara pekare positioner. När du har skapat en ny SVG Punkt variabel, kan vi applicera Matrix Omvandling på den för att konvertera position i förhållande till skärmen till en position i förhållande till den nuvarande SVG-användare enheter.

Kontrollera koden nedan för att se hur funktionerna getPointFromEvent() och onPointerDown() har förändrats.

// Skapa en SVG-punkt som innehåller x-och y-värden
var point = svg.createSVGPoint();

funktion getPointFromEvent (event) {
om (händelse.targetTouches) {
punkt.x = evenemanget.targetTouches[0].clientX;
punkt.y = – händelse.targetTouches[0].clientY;
} else {
punkt.x = evenemanget.clientX;
punkt.y = – händelse.clientY;
}

// Vi får den pågående omvandlingen matris av SVG och vi inversen det
var invertedSVGMatrix = svg.getScreenCTM().invers();

avkastning punkt.matrixTransform(invertedSVGMatrix);
}

var pointerOrigin;
funktion onPointerDown(event) {
isPointerDown = true; // Vi sätter pekaren nedåt

// Vi få pekaren position på klicka/touchdown så vi kan få värde när användaren börjar att dra
pointerOrigin = getPointFromEvent(event);
}

Genom att använda SVG-Poäng, du behöver inte ens hantera transformationer som tillämpas på din SVG! Jämför följande två exempel där den första är trasigt vid en rotation tillämpas på SVG och det andra exemplet använder SVG-Poäng.

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

Se Pennan Demo Bonus + omvandla – SVG Panorering av Louis Hoebregts (@Mamboleoo) på CodePen.

SVG Animerade Rect

Den andra okända begrepp i SVG vi kan använda för att förkorta vår kod, är användning av Animerade Rect.

Eftersom viewBox är faktiskt betraktas som en SVG-Rektangel (x, y, bredd, höjd), kan vi skapa en variabel från sin bas värde som kommer att automatiskt uppdatera viewBox om vi uppdaterar denna variabel.

Se hur lättare det är nu att uppdatera viewBox av våra SVG!

// Vi sparar de ursprungliga värdena från viewBox
var viewBox = svg.viewBox.baseVal;

funktion onPointerMove (event) {
if (!isPointerDown) {
återvändande.
}
händelsen.preventDefault();

// Get pekaren position som en SVG-Punkt
var pointerPosition = getPointFromEvent(event);

// Uppdatering viewBox variabel med avståndet från ursprung och nuvarande ställning
// Vi behöver inte ta hand om ett förhållande eftersom detta hanteras i getPointFromEvent funktion
viewBox.x -= (pointerPosition.x – pointerOrigin.x);
viewBox.y -= (pointerPosition.y – pointerOrigin.y);
}

Och här är den sista demo. Se hur mycket kortare koden är nu? 😀

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

Slutsats

Denna lösning är definitivt inte den enda vägen att gå för att hantera ett sådant beteende. Om du redan använder ett bibliotek för att hantera din SVGs, det kanske redan har en inbyggd funktion för att hantera det.

Jag hoppas att denna artikel kan hjälpa dig att förstå lite mer hur kraftfull SVG kan vara! Känn dig fri att bidra till att koden genom att kommentera med dina idéer eller alternativ till denna lösning.

Hp

  • Fågel designad av Freepik
  • Ett stort tack till Honom för hans värdefulla hjälp och alla trevliga människor från AAW Slack för deras feedback.