Fix Page transitions by using a hacky FOUC trick

Seems to work though!
This commit is contained in:
2022-01-18 14:22:26 +01:00
parent b5b4b126c9
commit cffef1835f
5 changed files with 82 additions and 58 deletions

View File

@@ -15,5 +15,9 @@
<body> <body>
<div id="housesof">%svelte.body%</div> <div id="housesof">%svelte.body%</div>
<script>
document.body.style.opacity = '0'
</script>
</body> </body>
</html> </html>

View File

@@ -5,19 +5,32 @@
export let tag: string export let tag: string
export let label: string = undefined export let label: string = undefined
export let parallax: number = undefined export let parallax: number = undefined
export let offset: number = 0 export let offsetStart: number = undefined
export let offsetEnd: number = undefined
export let animate: boolean = true export let animate: boolean = true
let scrollY: number let scrollY: number
let innerWidth: number
let innerHeight: number
let titleEl: HTMLElement let titleEl: HTMLElement
let isLarger: boolean
$: if (titleEl) { // Define default values
const start = titleEl.offsetTop + offset $: if (titleEl && !offsetStart && !offsetEnd) {
const end = titleEl.offsetTop + (titleEl.offsetHeight + offset) * 0.9 offsetStart = titleEl.offsetTop - innerHeight * 0.75
parallax = map(scrollY, start, end, 0, 1, true) offsetEnd = titleEl.offsetTop + innerHeight * 0.25
} }
const classes = [ // Check if title is larger than viewport to translate it
$: isLarger = titleEl && titleEl.offsetWidth >= innerWidth
// Calculate the parallax value
$: if (titleEl) {
const toTranslate = 100 - innerWidth / titleEl.offsetWidth * 100
parallax = isLarger ? map(scrollY, offsetStart, offsetEnd, 0, -toTranslate, true) : 0
}
$: classes = [
'scrolling-title', 'scrolling-title',
'title-huge', 'title-huge',
$$props.class $$props.class
@@ -38,20 +51,31 @@
} : {} } : {}
</script> </script>
<svelte:window bind:scrollY /> <svelte:window
bind:scrollY
bind:innerWidth bind:innerHeight
/>
{#if tag === 'h1'} {#if tag === 'h1'}
<h1 bind:this={titleEl} <h1 bind:this={titleEl}
class={classes} aria-label={label} class={classes} aria-label={label}
style="--parallax-x: {parallax};" style="--parallax-x: {parallax}%;"
use:reveal={revealOptions} use:reveal={revealOptions}
> >
<slot /> <slot />
</h1> </h1>
{:else if tag === 'h2'}
<h2 bind:this={titleEl}
class={classes} aria-label={label}
style="--parallax-x: {parallax}%;"
use:reveal={revealOptions}
>
<slot />
</h2>
{:else if tag === 'p'} {:else if tag === 'p'}
<p bind:this={titleEl} <p bind:this={titleEl}
class={classes} aria-label={label} class={classes} aria-label={label}
style="--parallax-x: {parallax};" style="--parallax-x: {parallax}%;"
use:reveal={revealOptions} use:reveal={revealOptions}
> >
<slot /> <slot />

View File

@@ -1,9 +1,10 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/env'
import { page } from '$app/stores' import { page } from '$app/stores'
import { getContext, onMount } from 'svelte' import { getContext, onMount } from 'svelte'
import anime from 'animejs' import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs' import type { AnimeTimelineInstance } from 'animejs'
import { DELAY } from '$utils/contants'
import { sleep } from '$utils/functions'
// Components // Components
import Metas from '$components/Metas.svelte' import Metas from '$components/Metas.svelte'
import PageTransition from '$components/PageTransition.svelte' import PageTransition from '$components/PageTransition.svelte'
@@ -31,49 +32,39 @@
*/ */
let timeline: AnimeTimelineInstance let timeline: AnimeTimelineInstance
if (browser) { const transition = async () => {
requestAnimationFrame(() => { requestAnimationFrame(() => timeline.play())
// Setup animations
timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
})
// Reveal text
anime.set('.homepage__headline', {
translateY: 16,
opacity: 0,
})
timeline.add({
targets: '.homepage__headline',
translateY: 0,
opacity: 1,
}, 900)
// Animate collage photos
anime.set('.homepage__collage .photo-card', {
opacity: 0,
translateY: '33.33%',
rotate: -4,
})
timeline.add({
targets: '.homepage__collage .photo-card',
translateY: 0,
rotate (item: HTMLElement) {
return getComputedStyle(item).getPropertyValue('--rotation')
},
opacity: 1,
duration: 1200,
delay: anime.stagger(75),
}, 0)
})
} }
onMount(() => {
requestAnimationFrame(() => { onMount(async () => {
timeline.play() timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
}) })
// Reveal text
timeline.add({
targets: '.homepage__headline',
translateY: [16, 0],
opacity: [0, 1],
}, 900)
// Animate collage photos
timeline.add({
targets: '.homepage__collage .photo-card',
translateY: ['33.33%', 0],
rotate (item: HTMLElement) {
return [-4, getComputedStyle(item).getPropertyValue('--rotation')]
},
opacity: [0, 1],
duration: 1200,
delay: anime.stagger(75),
}, 0)
await sleep(DELAY.PAGE_LOADING)
await transition()
}) })
</script> </script>
@@ -91,7 +82,8 @@
tag="h1" tag="h1"
class="homepage__title--houses" class="homepage__title--houses"
label="Houses of the World" label="Houses of the World"
offsetTop={100} offsetStart={-300}
offsetEnd={400}
> >
<SplitText text="Houses" mode="chars" /> <SplitText text="Houses" mode="chars" />
</ScrollingTitle> </ScrollingTitle>
@@ -150,11 +142,7 @@
<section class="homepage__locations"> <section class="homepage__locations">
<InteractiveGlobe /> <InteractiveGlobe />
<ScrollingTitle <ScrollingTitle tag="p" class="homepage__title--world mask">
tag="p"
class="homepage__title--world mask"
offset={-1 * innerHeight / 2}
>
<SplitText text="World" mode="chars" /> <SplitText text="World" mode="chars" />
</ScrollingTitle> </ScrollingTitle>

View File

@@ -1,5 +1,5 @@
.scrolling-title { .scrolling-title {
transform: translate3d(calc(-1px * var(--parallax-x) * 100), 0, 0); transform: translate3d(var(--parallax-x), 0, 0);
transition: transform 1.2s var(--ease-quart); transition: transform 1.2s var(--ease-quart);
will-change: transform; will-change: transform;
} }

View File

@@ -56,6 +56,14 @@ export const capitalizeFirstLetter = (string: string) => {
} }
/**
* Create a delay
*/
export function sleep (milliseconds: number) {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
/** /**
* Linear Interpolation * Linear Interpolation
*/ */