-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(html-demo): view transition api
view transition api: 页面切换动画
- Loading branch information
1 parent
8c3c69b
commit 5f6430c
Showing
20 changed files
with
698 additions
and
0 deletions.
There are no files selected for viewing
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
50
html demo/basic-view-trasition-demo/scripts/circle_transition.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)", | ||
} | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.