Introdução às Transições de Página
As transições de página desempenham um papel fundamental na experiência do usuário, pois elas podem melhorar a usabilidade e a aparência do aplicativo. Uma transição de página bem projetada pode criar uma sensação de continuidade e fluidez, tornando a navegação mais agradável para os usuários. Neste artigo, vamos explorar como criar um sistema de transição de página leve e assíncrono usando JavaScript vanilla, GSAP e Vite.
Importância das Transições de Página
As transições de página são essenciais para criar uma experiência de usuário agradável. Elas permitem que os usuários naveguem entre diferentes páginas do aplicativo sem sentir uma interrupção na experiência. Além disso, as transições de página podem ser usadas para criar uma sensação de profundidade e dimensão, tornando o aplicativo mais interessante e atraente.
Desafios de Implementar Transições de Página
Implementar transições de página pode ser um desafio, pois requer uma combinação de habilidades técnicas e criativas. É necessário considerar fatores como a velocidade da transição, a aparência da transição e a compatibilidade com diferentes dispositivos e navegadores. Além disso, é necessário garantir que a transição de página seja levemente e assíncrona, para evitar atrasos e melhorar a experiência do usuário.
Arquitetura do Sistema de Transição de Página
O sistema de transição de página que vamos criar utilizará a seguinte arquitetura:
* JavaScript vanilla para a lógica de negócios
* GSAP para as animações e transições
* Vite para a configuração e gerenciamento do projeto
Essa arquitetura permitirá que criemos um sistema de transição de página leve e assíncrono, que seja fácil de configurar e gerenciar.
Implementação do Sistema de Transição de Página
A implementação do sistema de transição de página será realizada em várias etapas:
1. Criação da estrutura básica do sistema
2. Implementação das animações e transições com GSAP
3. Configuração da lógica de negócios com JavaScript vanilla
4. Testes e depuração do sistema
Essas etapas permitirão que criemos um sistema de transição de página robusto e eficaz, que atenda às necessidades dos usuários e dos desenvolvedores.
Conclusão
As transições de página são uma parte fundamental da experiência do usuário, e criar um sistema de transição de página leve e assíncrono pode melhorar a usabilidade e a aparência do aplicativo. Neste artigo, vamos explorar como criar um sistema de transição de página usando JavaScript vanilla, GSAP e Vite, e implementar as animações e transições com GSAP.
Configuração do Projeto
Criação do Projeto com Vite
npm create vite@latest
Estrutura de Pastas
yourproject/
├── node_modules/
├── public/
├── src/
│ ├── main.js
│ └── style.css
├── gitignore
├── index.html
├── package-lock.json
└── package.json
Configuração Básica do HTML e CSS
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Building Async Page Transitions in Vanilla JavaScript</title>
</head>
<body>
<div data-transition="wrapper">
<div data-transition="container" data-namespace="home">
<main id="page_content" class="page_content"></main>
</div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
@font-face {
font-family: "Neue";
src: url("/NeueMontreal-Medium.ttf");
font-weight: 600;
font-style: normal;
font-display: swap;
}
:root {
font-family: Neue;
line-height: 1;
color: rgb(0, 0, 0);
background-color: #000000;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
font-family: Neue;
margin: 0;
display: flex;
overflow-x: hidden;
min-height: 100vh;
}
main {
width: 100vw;
}
h1 {
font-size: 28.2vw;
margin: 0;
line-height: 80%;
}
a {
color: black;
text-decoration: none;
text-transform: uppercase;
}
.hero {
background-color: white;
width: 100%;
height: 100vh;
overflow: hidden;
display: flex;
justify-content: space-between;
flex-direction: column;
padding: 20px;
align-items: center;
}
.hero_content {
width: 100%;
padding-top: 8vh;
text-align: center;
}
[data-transition="container"] {
transform: translateZ(0);
backface-visibility: hidden;
}
Implementação do Roteador
Definição de Rotas
const routes = {
"/": {
namespace: "home",
loader: () => import("./pages/home/home.js"),
},
"/alternative-page": {
namespace: "alternative-page",
loader: () => import("./pages/alternative-page/alternative-page.js"),
},
};
Class Roteador
class Router {
constructor() {
this.currentNamespace = null;
this.isTransitioning = false;
}
async loadInitialPage() {
const path = window.location.pathname;
const route = routes[path];
const pageModule = await route.loader();
const content = document.getElementById("page_content");
content.innerHTML = pageModule.default();
const container = document.querySelector('[data-transition="container"]');
container.setAttribute("data-namespace", route.namespace);
this.currentNamespace = route.namespace;
}
async navigate(path) {
if (this.isTransitioning || window.location.pathname === path) return;
window.history.pushState({}, "", path);
await this.performTransition(path);
}
async performTransition(path) {
if (this.isTransitioning) return;
this.isTransitioning = true;
try {
const route = routes[path];
if (!route || this.currentNamespace === route.namespace) return;
const pageModule = await route.loader();
await executeTransition({
nextHTML: pageModule.default(),
});
this.currentNamespace = route.namespace;
} finally {
this.isTransitioning = false;
}
}
}
Instância do Roteador
export const router = new Router();
Motor de Transição
Para criar o motor de transição, precisamos implementar a lógica de transição assíncrona. Isso envolve criar um segundo container, injetar o conteúdo do próximo página nele, executar a animação entre os dois containers e, finalmente, limpar o ambiente.
Criando o Motor de Transição
import { gsap } from "../lib/index.js";
import { defaultTransition } from "./animations/defaultTransition.js";
export async function executeTransition({ nextHTML }) {
const currentContainer = document.querySelector('[data-transition="container"]');
const wrapper = document.querySelector('[data-transition="wrapper"]');
// Clonar o container para o próximo página
const nextContainer = currentContainer.cloneNode(false);
const content = document.createElement("main");
content.id = "page_content";
content.className = "page_content";
content.innerHTML = nextHTML;
nextContainer.appendChild(content);
// Adicionar o próximo página ao DOM
wrapper.appendChild(nextContainer);
// Executar a animação de transição
const timeline = defaultTransition(currentContainer, nextContainer);
await timeline.then();
// Limpar o ambiente
currentContainer.remove();
gsap.set(nextContainer, { clearProps: "all" });
gsap.set(nextContainer, { force3D: true });
}
Para implementar a lógica de transição assíncrona, precisamos criar uma função chamada `performTransition`. Essa função será responsável por executar a transição entre as páginas.
async performTransition(path) {
// Bloquear a execução se uma transição estiver em andamento
if (this.isTransitioning) return;
this.isTransitioning = true;
try {
// Resolver a rota correspondente
const route = routes[path];
// Executar a transição assíncrona
await executeTransition({
nextHTML: pageModule.default(),
});
// Atualizar o namespace atual
this.currentNamespace = route.namespace;
} finally {
// Liberar o bloqueio de transição
this.isTransitioning = false;
}
}
Agora que temos a lógica de transição assíncrona implementada, podemos atualizar a função `navigate` para usar a `performTransition` em vez de executar a transição diretamente.
async navigate(path) {
if (window.location.pathname === path || this.isTransitioning) return;
window.history.pushState({}, "", path);
await this.performTransition(path);
}
Com a lógica de transição assíncrona implementada, podemos agora adicionar uma animação de entrada para dar mais profundidade à transição.
Adicionando Animação de Entrada
import { gsap } from "../lib/index.js";
const ENTER = (nextContainer, delay) => {
const t = nextContainer?.querySelector("h1");
if (!t) return null;
gsap.set(t, { y: "100%" });
const tl = gsap.timeline({
delay,
});
tl.to(t, {
y: 0,
duration: 1.2,
force3D: true,
ease: "expo.out",
}, 0);
return { timeline: tl };
};
export default ENTER;
Para adicionar a animação de entrada, precisamos chamar a função `ENTER` dentro da função `init` de cada página.
export function init({ container }) {
ENTER(container, 0.45);
}
Agora que temos a animação de entrada adicionada, podemos atualizar a função `executeTransition` para chamar a função `init` do próximo página.
export async function executeTransition({ nextHTML, nextModule }) {
// ...
if (nextModule?.init) {
nextModule.init({ container: nextContainer });
}
// ...
}
Integração e Funcionamento
O roteador e o motor de transição se integram para fornecer uma experiência de usuário suave e atraente. O roteador é responsável por gerenciar a navegação entre as páginas, enquanto o motor de transição é responsável por criar as transições animadas entre as páginas.
Como funciona o sistema de transição de página
O sistema de transição de página funciona da seguinte maneira:
1. O roteador intercepta as requisições de navegação e atualiza a URL do navegador.
2. O motor de transição é chamado para criar a transição animada entre as páginas.
3. O motor de transição cria uma cópia da página atual e injeta a página nova na cópia.
4. O motor de transição anima a transição entre as duas páginas, criando uma transição suave e atraente.
5. Após a transição ser concluída, o motor de transição remove a página anterior e atualiza a página atual.
Código fonte
// Em router.js
async performTransition(path) {
// ...
await executeTransition({
nextHTML: pageModule.default(),
});
// ...
}
// Em pageTransition.js
export async function executeTransition({ nextHTML }) {
const currentContainer = document.querySelector('[data-transition="container"]');
const wrapper = document.querySelector('[data-transition="wrapper"]');
// ...
const timeline = defaultTransition(currentContainer, nextContainer);
await timeline.then();
// ...
}
Integração com o roteador
O motor de transição é chamado pelo roteador para criar as transições animadas entre as páginas. O roteador atualiza a URL do navegador e chama o motor de transição para criar a transição animada.
// Em router.js
async navigate(path) {
// ...
await this.performTransition(path);
// ...
}
Integração com o motor de transição
O motor de transição é responsável por criar as transições animadas entre as páginas. Ele cria uma cópia da página atual e injeta a página nova na cópia, e então anima a transição entre as duas páginas.
// Em pageTransition.js
export async function executeTransition({ nextHTML }) {
// ...
const timeline = defaultTransition(currentContainer, nextContainer);
await timeline.then();
// ...
}
Fonte de Referência: tympanus.net.
Curadoria e Adaptação: Redação Yassutaro Developers.