recipes: view transitions for recipe detail navigation
All checks were successful
CI / update (push) Successful in 1m31s
All checks were successful
CI / update (push) Successful in 1m31s
Image morphs between CompactCard thumbnail and hero, title block slides up from bottom, header persists across transitions. Only activates for recipe detail navigations, not between list pages.
This commit is contained in:
@@ -1,7 +1,37 @@
|
||||
<script>
|
||||
import '$lib/css/recipe-links.css';
|
||||
import { page } from '$app/stores';
|
||||
import { onNavigate } from '$app/navigation';
|
||||
import Header from '$lib/components/Header.svelte'
|
||||
|
||||
onNavigate((navigation) => {
|
||||
if (!document.startViewTransition) return;
|
||||
|
||||
// Only use view transitions when navigating to/from a recipe detail page
|
||||
const toRecipe = navigation.to?.params?.name;
|
||||
const fromRecipe = navigation.from?.params?.name;
|
||||
if (!toRecipe && !fromRecipe) return;
|
||||
|
||||
// Measure title block position so the slide animation covers exactly the right distance
|
||||
const title = document.querySelector('[style*="view-transition-name: recipe-title"]');
|
||||
if (title) {
|
||||
const dist = window.innerHeight - title.getBoundingClientRect().top;
|
||||
document.documentElement.style.setProperty('--title-slide', `${dist}px`);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
document.startViewTransition(async () => {
|
||||
resolve();
|
||||
await navigation.complete;
|
||||
|
||||
// Set view-transition-name on the matching CompactCard image for reverse morph
|
||||
if (fromRecipe) {
|
||||
const card = document.querySelector(`img[data-recipe="${fromRecipe}"]`);
|
||||
if (card) card.style.viewTransitionName = `recipe-${fromRecipe}-img`;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
import UserHeader from '$lib/components/UserHeader.svelte';
|
||||
import LanguageSelector from '$lib/components/LanguageSelector.svelte';
|
||||
import OfflineSyncButton from '$lib/components/OfflineSyncButton.svelte';
|
||||
|
||||
@@ -282,6 +282,20 @@ h2{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* View transition: slide title block up from bottom */
|
||||
:global(::view-transition-new(recipe-title)) {
|
||||
animation: slide-up 0.35s ease both;
|
||||
}
|
||||
:global(::view-transition-old(recipe-title)) {
|
||||
animation: slide-down 0.25s ease both;
|
||||
}
|
||||
@keyframes slide-up {
|
||||
from { transform: translateY(var(--title-slide, 100vh)); }
|
||||
}
|
||||
@keyframes slide-down {
|
||||
to { transform: translateY(var(--title-slide, 100vh)); }
|
||||
}
|
||||
|
||||
</style>
|
||||
<svelte:head>
|
||||
<title>{data.strippedName} - {labels.title}</title>
|
||||
@@ -299,8 +313,8 @@ h2{
|
||||
<link rel="alternate" hreflang="x-default" href="https://bocken.org/rezepte/{data.germanShortName}" />
|
||||
</svelte:head>
|
||||
|
||||
<TitleImgParallax src={hero_img_src} color={img_color} alt={img_alt}>
|
||||
<div class=title>
|
||||
<TitleImgParallax src={hero_img_src} color={img_color} alt={img_alt} transitionName="recipe-{data.short_name}-img">
|
||||
<div class=title style="view-transition-name: recipe-title">
|
||||
{#if data.category}
|
||||
<a class="category g-pill g-btn-dark" href='/{data.recipeLang}/category/{data.category}'>{data.category}</a>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user