leestijd
4 min

Paginaovergangen met Phoenix LiveView

Het probleem

Paginaovergangen blijven lastig voor webdevelopers. In de tijd van Macromedia Flash gebeurde er volop — misschien iets te veel — maar tegenwoordig zie je nog maar zelden een website met goede paginaovergangen. Zonder Single Page App zijn ze moeilijk te bouwen. Natuurlijk kennen we genoeg Webflow-sites en overdadig geanimeerde pagina’s, maar subtiele animaties die logisch aanvoelen, zeker tussen pagina’s, kom je niet vaak tegen.

Onlangs sprak ik in een podcast over mijn vorige blogpost, en ook dit onderwerp kwam aan bod. We bespraken kort een paar nieuwe ontwikkelingen. Daarbij viel me een tweet op van Addy Osmani, die aan Google Chrome werkt, over de Shared Element Transitions API:

Als dit voorstel wordt aangenomen, kunnen developers gemakkelijker animaties voor paginaovergangen maken, zoals we die bijvoorbeeld op iOS kennen.

De oplossing

Op dit moment kunnen we de Shared Element Transitions API nog niet gebruiken. Maar je bent hier omdat je wilt weten hoe je nú mooie, subtiele animaties tussen pagina’s in Phoenix LiveView maakt. Laten we beginnen.

Eerst moeten we het verschil begrijpen tussen routeren naar pagina’s en naar componenten. LiveView patcht de DOM op basis van je wijzigingen. Wanneer je met Phoenix.Liveview.push_patch of Phoenix.Liveview.push_redirect naar een andere pagina gaat, vervangt LiveView dus alleen de view of component die moet veranderen. Het verschil is dat push_redirect (of live_redirect) de view opnieuw laadt, terwijl push_patch (of live_patch) de huidige view bijwerkt. Via DOM-diffing vergelijkt LiveView de nieuwe HTML-uitvoer met de oude en past het alleen de afwijkende delen aan.

Hoe je paginaovergangen werkend krijgt, hangt sterk af van de structuur van je project. Bij mave beperken we het aantal layouts en hebben we het project zo ingericht dat we voor de animaties uitsluitend patch_to gebruiken. We willen namelijk alleen de binnenste container animeren, vergelijkbaar met native applicaties, en de rest van de pagina ongemoeid laten. Onze applicatie bestaat uit één view waarin iedere “pagina” een component is. Daardoor kunnen we naar iedere component patchen — feitelijk alleen de content vervangen — en zo nodig toch gegevens in de view bijwerken.

Op de contentcomponent passen we de volgende Tailwind-classes toe: transition duration-100 ease-out opacity-0 scale-95. Dit zijn de standaardclasses wanneer de component wordt gerenderd. De div is dus aanvankelijk niet zichtbaar. Vervolgens activeren we de animatie zodra de component is geladen, met de mount-hook in JavaScript:

Voeg dit toe aan je hooks in JavaScript (let op: dit werkt nog niet)
↳ Voeg dit toe aan je hooks in JavaScript (let op: dit werkt nog niet)

Voeg nu phx-hook="transition" toe aan de div van de contentcomponent die je wilt animeren. Phoenix vereist bij een phx-hook ook een id-attribuut, dus vergeet dat niet. Helaas kunnen de meeste browsers slecht overweg met animaties die direct na een HTML-wijziging worden toegepast — precies wat Phoenix bij de DOM-diff doet. De code hierboven werkt daarom niet. We moeten enkele milliseconden wachten voordat we de animatie uitvoeren:

De animatie werkt zoals bedoeld nadat deze correct is weergegeven
↳ De animatie werkt zoals bedoeld nadat deze correct is weergegeven

Nu wordt de overgangsanimatie iedere keer uitgevoerd wanneer de component wordt gemount.

Maar wat als je voor een specifieke view een eigen animatie wilt? De ene view kan bijvoorbeeld van links naar binnen schuiven en de andere van rechts. Daarvoor wil je de classes in je HEEX-template aanpassen, niet in JavaScript. Laten we de oplossing daarom algemener maken:

Hiermee worden de classes uit een lijst als attributen aan de betreffende div toegevoegd. Ook voegen we de hook updated toe. Zo voorkomen we dat de component na een wijziging terugvalt naar de standaardstatus opacity-0 en dus onzichtbaar wordt.

In plaats van de hook rechtstreeks aan te roepen, maken we een hulpmethode waarmee je deze overgangen gemakkelijker uitschrijft:

voeg dit toe aan de functie live_helpers in /lib/<application_name>_web.ex-bestand
↳ voeg dit toe aan de functie live_helpers in /lib/<application_name>_web.ex-bestand

Zonder je JavaScript aan te passen kun je nu de div schrijven en de gewenste wijzigingen toepassen: <div class="transition duration-100 ease-out opacity-0 scale-95" {transition("opacity-0 scale-95", "opacity-100 scale-100")}> ... </div> Je hoeft geen id-attribuut of hook toe te voegen. Pas de functie toe op je div en alle bovenstaande attributen worden automatisch toegevoegd.

Dat is alles. Ik hoop dat deze manier om tussen “pagina’s” in een Phoenix-applicatie te schakelen met de Shared Element Transitions API onderdeel van het framework wordt. Dat zou opnieuw een voordeel ten opzichte van andere stacks zijn:

Wil je bovenstaande code gebruiken? Ik heb de snippets beschikbaar gemaakt als GitHub-gist.

Gepubliceerd op 9 augustus 2022
werkt met
Ontwikkelaar?
Onze documentatie begeleidt je bij het embedden van video: van eenvoudige eerste stappen tot handmatige configuraties voor ervaren gebruikers. Je vindt er meerdere hostingopties, waaronder ons standaard-CDN, en uitleg over populaire webframeworks.
script
react
vue
1
2
3
4
5
🍪 Klik op ‘Accepteren’ om te bevestigen dat je het ermee eens bent dat we geen cookies gebruiken. Ja, deze banner is puur voor de show!
Accepteren