След като успешно обсъдихме използването на View Transitions API с Vanilla JavaScript и след това с ReactJS, за анимиране на преходи между различни маршрути (или изгледи) на нашето приложение, ще преминем стъпка по стъпка в нов урок със същата тема , Но сега ще използваме NextJS 13 с новата структура на App Router, Всичко това, използвайки само CSS, не са необходими допълнителни библиотеки на трети страни.

Съдържание

  • Какво представлява API за преглед на преходи
  • Дали View Transition API е широко възприет от съвременните браузъри?
  • Преди да преминете към този урок
  • Какво строим?
  • Да кодираме
  • Пълен изходен код

Какво представлява API за преглед на преходи

API за преглед на преходи предоставя начин за създаване на анимиран преход между два документа (изгледи), без да създавате припокриване в прехода.

View Transitions правят този процес лесен и пълен, като ви позволяват да направите своя DOM промяна без никакво припокриване между състоянията, като създадете анимация за преход между състоянията с помощта на моментални изгледи.

Текущата реализация на този API е насочена към приложения с една страница (SPA). В този урок ще обясним как да направите това с помощта на NextJS 13 с новата папка App.

Прочетете повече за View Transitions API тук.

Прегледът на преходите API широко ли е възприет от съвременните браузъри?

По времето, когато се пише тази статия, View Transitions API е възприет от Chrome (v 111+), браузър Edge (v 111+) и браузър Opera (v 97+) всички в производствените версии и в системата Android: на android браузър и Chrome за android.

Преди да преминете към този урок

Говорили сме за API за преглед на преходи преди в тази статия:



Бяхме обяснили какво представлява този API, как да го активирате във всички поддържани браузъри и как да го използвате, всичко това в ръководство стъпка по стъпка, с работещо приложение (MPA: многостранично приложение), с пълен изходен код, всички използващи само Vanilla JavaScript.

Трябва внимателно да прочетете внедряването на CSS, преди да продължите с този урок, защото няма да обясняваме отново API за преходи на изглед.

След това във втора статия, в същата тема, говорихме за използването на същия View Transitions API, но използвахме ReactJS.



Сега ще продължим със същия подход, но сега използвайки новия NextJS 13 с рутер за приложения и без да използваме допълнителни библиотеки на трети страни, всичко това използвайки CSS.

Това, което изграждаме

В тази статия ще създадем многостранично приложение, използвайки NextJS 13 и структурата на App Router, ще използваме новия API за преходи на изгледи, за да създаваме анимации, когато навигираме между различни изгледи (страници) и всичко това, използвайки само CSS.

В нашето приложение имаме три различни анимации:

  • Вижте анимация (пораснете)
  • Заглавна анимация (слайд отдясно)
  • Анимация на съдържанието (слайд отляво)

Това е анимиран GIF, показващ окончателното ни приложение в действие:

Да кодираме

Първо ще създадем ново приложение NextJS 13 с активиран рутер за приложения:

npx create-next-app@latest

Не забравяйте да изберете „Да“ за рутера на приложението:

Сега трябва да изчистим кода:

За src/app/global.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

И за src/app/page.tsx

export default function Home() {
  return (<></>)}

Ще започнем паралелно с разширяването на типа документ по подразбиране в NextJS, за да добавим новия експериментален API на View Transinions:

В папката src създайте нова папка: types и в нея създайте файл с име: extendedDocument.ts

export interface ExtendedDocument extends Document {
  startViewTransition?: any;
}

Ще използваме този нов тип extendedDocument, за да заменим документа по подразбиране в нашия код NextJS.

Със същата логика ще разширим куката useRouter от NextJS, за да я накараме да поддържа новия API за преходи на преглед:

useAnimatedRouter кука:

В папката src създайте нова папка: hook и в нея създайте файл с име: useAnimatedRouter.ts

"use client";
import { ExtendedDocument } from "@/types/extendedDocument";
import { useRouter } from "next/navigation";

export default function useAnimatedRouter() {
  const router = useRouter();
  const viewTransitionsStatus = () => {
    const extendedDocument = document as ExtendedDocument;
    let status = "Opss, Your browser doesn't support View Transitions API";
    if (extendedDocument?.startViewTransition) {
      status = "Yess, Your browser support View Transitions API";
    }
    return status;
  };
  // Navigate to the new route
  const animatedRoute = (url: string) => {
    const extendedDocument = document as ExtendedDocument;
    if (!extendedDocument.startViewTransition) {
      return router.push(url);
    } else {
      extendedDocument.startViewTransition(() => {
        router.push(url);
      });
    }
  };
  return { animatedRoute, viewTransitionsStatus };
}

Тази кука експортира две функции:

  • viewTansitionsStatus: Проверете дали браузърът поддържа новия API на View Transitions.
  • animatedRoute: Капсулирайте метода по подразбиране router.push() и променя поведението му, ако браузърът поддържа нашия API, той го капсулира с функцията startViewTransition() (моля, вижте предишните статии за повече подробности относно това функция), ако не, тя ще се върне с поведението по подразбиране на router.push()

animatedLink компонент

Сега ще презапишем поведението по подразбиране на компонента NextJS Link, за да го накараме да поддържа новия API за преходи на изгледи:

В папката src създайте нова папка: components,и в нея създайте файл: animatedLink.tsx

import useAnimatedRouter from "@/hooks/useAnimatedRouter";
import Link from "next/link";
import React from "react";

type Props = {
  href: string;
  children: React.ReactNode;
};
export default function AnimatedLink({ href, children }: Props) {
  const { animatedRoute } = useAnimatedRouter();
  return (
    <Link
      href={href}
      onClick={() => {        
        animatedRoute(href);
      }}
      passHref      
    >
      {children}
    </Link>
  );
}

Както можете да видите, ние използваме функцията animatedRoute() от нашата новосъздадена кука, за да презапишем поведението по подразбиране onClick на компонента Link.

И ние предаваме подпорите passHref тук, това ще позволи на компонента Link да предаде подпорката href на децата.

Компонент на заглавката:

import Link from "next/link";
import React from "react";
import AnimatedLink from "./animatedLink";

export default function Header() {
  return (
    <div className="bg-slate-700 text-slate-50 py-4 ">
      <div className="container mx-auto flex gap-2">
        <AnimatedLink href="/">Home</AnimatedLink>
        <AnimatedLink href="/about">About</AnimatedLink>
        <AnimatedLink href="/contact">Contact</AnimatedLink>
      </div>
    </div>
  );
}

Тук използваме компонента AnimatedLink като компонента Link по подразбиране. За да посочите трите страници: Начало, Информация и Контакти.

Компонент на долния колонтитул:

"use client";
import useAnimatedRouter from "@/hooks/useAnimatedRouter";
import React from "react";

export default function Footer() {
  const { viewTransitionsStatus } = useAnimatedRouter();
  return (
    <footer className="bg-gray-800 opacity-75 text-white p-1 text-center fixed bottom-0 left-0 right-0">
      <span>{viewTransitionsStatus()}</span>
    </footer>
  );
}

Използваме функцията viewTransitionsStatus() от нашата нова създадена кука: useAnimatedRouter, за да покажем съобщение в долната част на нашето приложение.

Файл с оформление

Сега във файла Layout.tsx в папката src/app променете кода, за да включите компонентите ‹Header/› и ‹Footer/›

"use client";
import Header from "@/components/header";
import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import Footer from "@/components/footer";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Header />
        {children}
        <Footer />
      </body>
    </html>
  );
}

Нашите три страници:

Променете page.tsx в папката src/app по следния начин:

export default function Home() {
  return (
    <div className="flex flex-col h-screen items-center justify-center bg-amber-100 gap-10">
      <h1 className="text-4xl pageHeader">Home Page</h1>
      <p className="mx-10 pageContent text-center line-clamp-3">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac urna
        auctor, viverra sapien. Donec euismod turpis eget massa lobortis, eget
        scelerisque justo.
      </p>
    </div>
  );
}

Това е нашата начална страница.

Същото за страницата Информация в папката: src/app/about, създайте нов файл с име: page.tsx

export default function About() {
  return (
    <div className="flex flex-col h-screen items-center justify-center bg-amber-200 gap-10">
      <h1 className="text-4xl pageHeader">About Page</h1>
      <p className="mx-10 pageContent text-center line-clamp-3">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac urna
        auctor, viverra sapien. Donec euismod turpis eget massa lobortis, eget
        scelerisque justo.
      </p>
    </div>
  );
}

В папката: src/app/contact създайте нов файл с име: page.tsx

export default function Contact() {
  return (
    <div className="flex flex-col h-screen items-center justify-center bg-amber-300 gap-10">
      <h1 className="text-4xl pageHeader">Contact Page</h1>
      <p className="mx-10 pageContent text-center line-clamp-3">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac urna
        auctor, viverra sapien. Donec euismod turpis eget massa lobortis, eget
        scelerisque justo.
      </p>
    </div>
  );
}

Разгледайте внимателно таговете ‹h1› и ‹p› в трите страници:

  • ‹h1› съдържат класа: pageHeader
  • ‹p› съдържат класа: pageContent

Ще използваме тези CSS класове, за да анимираме тези секции по-късно

globals.css файл

Сега ще използваме магията на View Transitions API, за да анимираме нашето приложение, използвайки само CSS.

Цялата магия се случва в този файл: globals.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

/* Animate the page header separatly */
.pageHeader {
    view-transition-name: page-header;
}

/* Animate the page content separatly */
.pageContent {
    view-transition-name: page-content;
}

::view-transition-old(root) {
    animation: fade-and-scale-out 0.5s ease-in-out 1 forwards;
}

::view-transition-new(root) {
    animation: fade-and-scale-in 1s ease-in-out 1 forwards;
}

::view-transition-old(page-header) {
    animation: hide 1s ease-in-out 1 forwards;
}

::view-transition-new(page-header) {
    animation: slide-right 2s ease-in-out 1 forwards;
}

::view-transition-old(page-content) {
    animation: hide 1s ease-in-out 1 forwards;
}

::view-transition-new(page-content) {
    animation: slide-left 2.5s ease-in-out 1 forwards;
}

/* First Animation */

@keyframes fade-and-scale-in {
    from {
        opacity: 0;
        transform: scale(0);
    }

    to {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes fade-and-scale-out {
    from {
        opacity: 1;
        transform: scale(1);
    }

    to {
        opacity: 0;
        transform: scale(0);
    }
}

/* Second Animation */

@keyframes hide {
    from {
        opacity: 1;

    }

    to {
        opacity: 0;

    }
}

@keyframes slide-left {
    from {
        opacity: 0;
        transform: translateX(-100%);
    }

    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes slide-right {
    from {
        opacity: 0;
        transform: translateX(100%);
    }

    to {
        opacity: 1;
        transform: translateX(0);
    }
}

Ще обясним всяка част от този файл:

@tailwind base;
@tailwind components;
@tailwind utilities;

Това е част от конфигурацията по подразбиране на TailwindCSS, присъства по подразбиране в нашия файл globals.css.

...

::view-transition-old(root) {
    animation: fade-and-scale-out 0.5s ease-in-out 1 forwards;
}

::view-transition-new(root) {
    animation: fade-and-scale-in 1s ease-in-out 1 forwards;
}

...

@keyframes fade-and-scale-in {
    from {
        opacity: 0;
        transform: scale(0);
    }

    to {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes fade-and-scale-out{
    from {
        opacity: 1;
        transform: scale(1);
    }

    to {
        opacity: 0;
        transform: scale(0);
    }
}

Тази част от кода ще анимира основния елемент на изгледите (нашите страници), състоянието на анимацията се променя между старото и новото състояние, за това имаме две анимации на CSS ключови кадри:

  • избледняване и мащабиране
  • избледняване и мащабиране
...

/* Animate the page header separatly */
.pageHeader {
    view-transition-name: page-header;
}

...

::view-transition-old(page-header) {
    animation: hide 1s ease-in-out 1 forwards;
}

::view-transition-new(page-header) {
    animation: slide-right 2s ease-in-out 1 forwards;
}

...

/* Second Animation */

@keyframes hide {
    from {
        opacity: 1;

    }

    to {
        opacity: 0;

    }
}

@keyframes slide-right {
    from {
        opacity: 0;
        transform: translateX(100%);
    }

    to {
        opacity: 1;
        transform: translateX(0);
    }
}

В тази част от кода създадохме нов контейнер за преход на изглед, с помощта на: име на изглед-преход: заглавка на страницата, с присвоен на CSS клас: pageHeader

Ще използваме това име: „page-header“, за да зададем нова анимация (скриване и плъзгане надясно).

...

/* Animate the page header separatly */
.pageContent {
    view-transition-name: page-content;
}

...

::view-transition-old(page-content) {
    animation: hide 1s ease-in-out 1 forwards;
}

::view-transition-new(page-header) {
    animation: slide-left 2.5s ease-in-out 1 forwards;
}

...

/* Second Animation */

@keyframes hide {
    from {
        opacity: 1;

    }

    to {
        opacity: 0;

    }
}

@keyframes slide-left {
    from {
        opacity: 0;
        transform: translateX(-100%);
    }

    to {
        opacity: 1;
        transform: translateX(0);
    }
}

Със същата логика създадохме нов контейнер за преход на изглед с помощта на: име на преход на изглед: съдържание на страница, с присвоено на CSS класа: pageContent.

Ще използваме това име: „страница-съдържание“, за да зададем нова анимация (скриване и плъзгане наляво).

Пълен изходен код

https://github.com/adelpro/nextjs-view-transitions

На обикновен английски

Благодарим ви, че сте част от нашата общност! Преди да тръгнете: