Skip to content

Commit

Permalink
feat(html-demo): view transition api
Browse files Browse the repository at this point in the history
view transition api: 页面切换动画
  • Loading branch information
changshou83 committed May 10, 2023
1 parent 8c3c69b commit 5f6430c
Show file tree
Hide file tree
Showing 20 changed files with 698 additions and 0 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions html demo/basic-view-trasition-demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Basic View Transitions API demo</title>

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@500&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style/index.css" />

<script type="module" src="scripts/utils.js" defer></script>
<!-- <script src="scripts/basic.js" defer></script> -->
<script type="module" src="scripts/circle_transition.js" defer></script>
</head>
<body></body>
</html>
35 changes: 35 additions & 0 deletions html demo/basic-view-trasition-demo/page-2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en" class="body-grid">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="https://glitch.com/favicon.ico" />
<title>Demo</title>
<link rel="stylesheet" href="style/index.css" />
<script type="module" src="scripts/circle_transition.js"></script>
</head>
<body>
<header class="main-header">
<a href="./" class="back-and-title">
<svg class="back-icon" viewBox="0 0 24 24">
<path
d="M20 11H7.8l5.6-5.6L12 4l-8 8 8 8 1.4-1.4L7.8 13H20v-2z"
></path>
</svg>
<span class="main-header-text">Demo site</span>
</a>
</header>
<main class="content content-alt">
<h1 class="content-title">Page 2</h1>
<p>This is the content for page 2.</p>
<ol>
<li>It</li>
<li>also</li>
<li>has</li>
<li>a</li>
<li>list!</li>
</ol>
<p>Ok, that's enough fun, you can go back to <a href="./">page 1</a>.</p>
</main>
</body>
</html>
63 changes: 63 additions & 0 deletions html demo/basic-view-trasition-demo/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const imageData = [
{
name: "Jungle coast",
file: "jungle-coast",
},
{
name: "Bird in the tree",
file: "tree-bird",
},
{
name: "A view from the sky",
file: "view-from-the-sky",
},
{
name: "The view across the water",
file: "watery-view",
},
];

const thumbs = document.querySelector(".thumbs");
const galleryImg = document.querySelector(".gallery-view img");
const galleryCaption = document.querySelector(".gallery-view figcaption");

function init() {
imageData.forEach((data) => {
const img = document.createElement("img");
const a = document.createElement("a");
a.href = "#";
a.title = `Click to load ${data.name} in main gallery view`;
img.alt = data.name;
img.src = `images/${data.file}_th.jpg`;
a.appendChild(img);
thumbs.appendChild(a);

a.addEventListener("click", updateView);
a.addEventListener("keypress", updateView);
});

galleryImg.src = `images/${imageData[0].file}.jpg`;
galleryCaption.textContent = imageData[0].name;
}

function updateView(event) {
// Handle the difference in whether the event is fired on the <a> or the <img>
const targetIdentifier = event.target.firstChild || event.target;

const displayNewImage = () => {
const mainSrc = `${targetIdentifier.src.split("_th.jpg")[0]}.jpg`;
galleryImg.src = mainSrc;
galleryCaption.textContent = targetIdentifier.alt;
};

// Fallback for browsers that don't support View Transitions:
if (!document.startViewTransition) {
displayNewImage();
return;
}

// With View Transitions:
const transition = document.startViewTransition(() => displayNewImage());
}

init();
79 changes: 79 additions & 0 deletions html demo/basic-view-trasition-demo/scripts/basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
document.body.innerHTML = `
<div class="container">
<aside>
<h3 class="title">Basic View Transitions API demo</h3>
<section class="thumbs"></section>
</aside>
<main>
<section class="gallery-view">
<figure>
<img />
<figcaption><div class="caption-text"></div></figcaption>
</figure>
</section>
</main>
</div>
`;

const imageData = [
{
name: "Jungle coast",
file: "jungle-coast",
},
{
name: "Bird in the tree",
file: "tree-bird",
},
{
name: "A view from the sky",
file: "view-from-the-sky",
},
{
name: "The view across the water",
file: "watery-view",
},
];

const thumbs = document.querySelector(".thumbs");
const galleryImg = document.querySelector(".gallery-view img");
const galleryCaption = document.querySelector(".gallery-view figcaption");

function init() {
imageData.forEach((data) => {
const img = document.createElement("img");
const a = document.createElement("a");
a.href = "#";
a.title = `Click to load ${data.name} in main gallery view`;
img.alt = data.name;
img.src = `images/${data.file}.jpg`;
a.appendChild(img);
thumbs.appendChild(a);

a.addEventListener("click", updateView);
a.addEventListener("keypress", updateView);
});

galleryImg.src = `images/${imageData[0].file}.jpg`;
galleryCaption.textContent = imageData[0].name;
}

function updateView(event) {
// Handle the difference in whether the event is fired on the <a> or the <img>
const targetIdentifier = event.target.firstChild || event.target;

const displayNewImage = () => {
galleryImg.src = targetIdentifier.src.replace("_th", "");
galleryCaption.textContent = targetIdentifier.alt;
};

// Fallback for browsers that don't support View Transitions:
if (!document.startViewTransition) {
displayNewImage();
return;
}

// With View Transitions:
const transition = document.startViewTransition(() => displayNewImage());
}

init();
50 changes: 50 additions & 0 deletions html demo/basic-view-trasition-demo/scripts/circle_transition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { getPageContent, onLinkNavigate, transitionHelper } from "./utils.js";

document.body.innerHTML = `
<header class="main-header">
<span class="main-header-text">Demo site</span>
</header>
<main class="content">
<h1 class="content-title">Page 1</h1>
<p>This is the content for page 1.</p>
<p>Why not check out <a href="./page-2.html">page 2</a>?</p>
</main>
`;

let lastClick;
addEventListener("click", (event) => (lastClick = event));

onLinkNavigate(async ({ toPath }) => {
const content = await getPageContent(toPath);

const x = lastClick?.clientX ?? innerWidth / 2;
const y = lastClick?.clientY ?? innerHeight / 2;
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
);

const transition = transitionHelper({
updateDOM() {
// This is a pretty heavy-handed way to update page content.
// In production, you'd likely be modifying DOM elements directly,
// or using a framework.
// innerHTML is used here just to keep the DOM update super simple.
document.body.innerHTML = content;
},
});

transition.ready.then(() => {
document.documentElement.animate(
[
{ clipPath: `circle(0 at ${x}px ${y}px)` },
{ clipPath: `circle(${endRadius}px at ${x}px ${y}px)` },
],
{
duration: 500,
easing: "ease-in",
pseudoElement: "::view-transition-new(root)",
}
);
});
});
92 changes: 92 additions & 0 deletions html demo/basic-view-trasition-demo/scripts/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
export async function getPageContent(url) {
// This is a really scrappy way to do this.
// Don't do this in production!
const response = await fetch(url);
const text = await response.text();
// Particularly as it uses regexp
return /<body[^>]*>([\w\W]*)<\/body>/.exec(text)[1];
}

function isBackNavigation(navigateEvent) {
if (
navigateEvent.navigationType === "push" ||
navigateEvent.navigationType === "replace"
) {
return false;
}
if (
navigateEvent.destination.index !== -1 &&
navigateEvent.destination.index < navigation.currentEntry.index
) {
return true;
}
return false;
}

// Intercept navigations
// https://developer.chrome.com/docs/web-platform/navigation-api/
// This is a naive usage of the navigation API, to keep things simple.
export async function onLinkNavigate(callback) {
navigation.addEventListener("navigate", (event) => {
const toUrl = new URL(event.destination.url);

if (location.origin !== toUrl.origin) return;

const fromPath = location.pathname;
const isBack = isBackNavigation(event);

event.intercept({
async handler() {
if (event.info === "ignore") return;

await callback({
toPath: toUrl.pathname,
fromPath,
isBack,
});
},
});
});
}

export function getLink(href) {
const fullLink = new URL(href, location.href).href;

return [...document.querySelectorAll("a")].find(
(link) => link.href === fullLink
);
}

// This helper function returns a View-Transition-like object, even for browsers that don't support view transitions.
// It won't do the transition in unsupported browsers, it'll act as if the transition is skipped.
// It also makes it easier to add class names to the document element.
export function transitionHelper({
skipTransition = false,
classNames = "",
updateDOM,
}) {
if (skipTransition || !document.startViewTransition) {
const updateCallbackDone = Promise.resolve(updateDOM()).then(
() => undefined
);

return {
ready: Promise.reject(Error("View transitions unsupported")),
domUpdated: updateCallbackDone,
updateCallbackDone,
finished: updateCallbackDone,
};
}

const classNamesArray = classNames.split(/\s+/g).filter(Boolean);

document.documentElement.classList.add(...classNamesArray);

const transition = document.startViewTransition(updateDOM);

transition.finished.finally(() =>
document.documentElement.classList.remove(...classNamesArray)
);

return transition;
}
Loading

0 comments on commit 5f6430c

Please sign in to comment.