Run page animations onMount instead of browser

This commit is contained in:
2022-05-30 21:57:04 +02:00
parent 7a165721d9
commit d3f74be59e
8 changed files with 279 additions and 347 deletions

View File

@@ -213,88 +213,79 @@
} }
/**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations
timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
})
anime.set('.viewer__picture', {
opacity: 0,
})
anime.set('.viewer__picture.is-1', {
translateY: 24,
})
// Photos
timeline.add({
targets: '.viewer__picture.is-1',
opacity: 1,
translateY: 0,
duration: 900,
}, 250)
timeline.add({
targets: '.viewer__picture:not(.is-0):not(.is-1)',
opacity: 1,
translateX (element: HTMLElement, index: number) {
const x = getComputedStyle(element).getPropertyValue('--offset-x').trim()
return [`-${x}`, 0]
},
delay: anime.stagger(55)
}, 500)
// Prev/Next buttons
timeline.add({
targets: '.viewer__controls button',
translateX (item: HTMLElement) {
let direction = item.classList.contains('prev') ? -1 : 1
return [16 * direction, 0]
},
opacity: [0, 1],
}, 450)
// Infos
timeline.add({
targets: '.viewer__info > *',
translateY: [24, 0],
opacity: [0, 1],
delay: anime.stagger(200)
}, 400)
anime.set('.viewer__index', {
opacity: 0
})
// Index
timeline.add({
targets: '.viewer__index',
opacity: 1,
duration: 900,
}, 600)
// Fly each number
timeline.add({
targets: '.viewer__index .char',
translateY: ['100%', 0],
delay: anime.stagger(200),
duration: 1000,
}, 700)
})
}
onMount(() => { onMount(() => {
// Transition in /**
requestAnimationFrame(() => { * Animations
timeline.play() */
// Setup animations
const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
}) })
anime.set('.viewer__picture', {
opacity: 0,
})
anime.set('.viewer__picture.is-1', {
translateY: 24,
})
// Photos
timeline.add({
targets: '.viewer__picture.is-1',
opacity: 1,
translateY: 0,
duration: 900,
}, 250)
timeline.add({
targets: '.viewer__picture:not(.is-0):not(.is-1)',
opacity: 1,
translateX (element: HTMLElement, index: number) {
const x = getComputedStyle(element).getPropertyValue('--offset-x').trim()
return [`-${x}`, 0]
},
delay: anime.stagger(55)
}, 500)
// Prev/Next buttons
timeline.add({
targets: '.viewer__controls button',
translateX (item: HTMLElement) {
let direction = item.classList.contains('prev') ? -1 : 1
return [16 * direction, 0]
},
opacity: [0, 1],
}, 450)
// Infos
timeline.add({
targets: '.viewer__info > *',
translateY: [24, 0],
opacity: [0, 1],
delay: anime.stagger(200)
}, 400)
anime.set('.viewer__index', {
opacity: 0
})
// Index
timeline.add({
targets: '.viewer__index',
opacity: 1,
duration: 900,
}, 600)
// Fly each number
timeline.add({
targets: '.viewer__index .char',
translateY: ['100%', 0],
delay: anime.stagger(200),
duration: 1000,
}, 700)
// Transition in
requestAnimationFrame(timeline.play)
}) })
</script> </script>

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/env'
import { navigating, page } from '$app/stores' import { navigating, page } from '$app/stores'
import { onMount } from 'svelte' import { onMount } from 'svelte'
import anime from 'animejs' import anime from 'animejs'
@@ -108,52 +107,6 @@
} }
/**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
})
// Title word
timeline.add({
targets: '.location-page__intro .word',
translateY: ['110%', 0],
delay: anime.stagger(200)
}, 200 + ($navigating ? DURATION.PAGE_IN : 0))
// Illustration
timeline.add({
targets: '.location-page__illustration',
scale: [1.06, 1],
opacity: [0, 1],
duration: 2400,
}, 400 + ($navigating ? DURATION.PAGE_IN : 0))
// Title of
timeline.add({
targets: '.location-page__intro .of',
opacity: [0, 1],
duration: 1200,
}, 1050 + ($navigating ? DURATION.PAGE_IN : 0))
// Description
timeline.add({
targets: '.location-page__description',
translateY: ['10%', 0],
opacity: [0, 1],
}, 900 + ($navigating ? DURATION.PAGE_IN : 0))
})
}
onMount(() => { onMount(() => {
// Photos IntersectionObserver // Photos IntersectionObserver
observerPhotos = new IntersectionObserver(entries => { observerPhotos = new IntersectionObserver(entries => {
@@ -186,11 +139,48 @@
const existingPhotos = photosListEl.querySelectorAll('.house') const existingPhotos = photosListEl.querySelectorAll('.house')
existingPhotos.forEach(el => observerPhotos.observe(el)) existingPhotos.forEach(el => observerPhotos.observe(el))
/**
* Animations
*/
// Transition in // Transition in
requestAnimationFrame(() => { const timeline: AnimeTimelineInstance = anime.timeline({
timeline.play() duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
}) })
// Title word
timeline.add({
targets: '.location-page__intro .word',
translateY: ['110%', 0],
delay: anime.stagger(200)
}, 200 + ($navigating ? DURATION.PAGE_IN : 0))
// Illustration
timeline.add({
targets: '.location-page__illustration',
scale: [1.06, 1],
opacity: [0, 1],
duration: 2400,
}, 400 + ($navigating ? DURATION.PAGE_IN : 0))
// Title of
timeline.add({
targets: '.location-page__intro .of',
opacity: [0, 1],
duration: 1200,
}, 1050 + ($navigating ? DURATION.PAGE_IN : 0))
// Description
timeline.add({
targets: '.location-page__description',
translateY: ['10%', 0],
opacity: [0, 1],
}, 900 + ($navigating ? DURATION.PAGE_IN : 0))
requestAnimationFrame(timeline.play)
// Destroy // Destroy
return () => { return () => {

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/env'
import { onMount } from 'svelte' import { onMount } from 'svelte'
import anime from 'animejs' import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs' import type { AnimeTimelineInstance } from 'animejs'
@@ -13,53 +12,43 @@
export let data: any export let data: any
/**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations
timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
})
anime.set('.credits__heading > *, .credits__category > ul > li', {
opacity: 0,
translateY: 24,
})
anime.set('.credits__category', {
opacity: 0,
})
// Elements
timeline.add({
targets: '.credits__heading > *, .credits__category',
opacity: 1,
translateY: 0,
delay: anime.stagger(350),
}, 500)
// Names
timeline.add({
targets: '.credits__category > ul > li',
opacity: 1,
translateY: 0,
delay: anime.stagger(350),
}, 1100)
})
}
onMount(() => { onMount(() => {
// Transition in /**
requestAnimationFrame(() => { * Animations
timeline.play() */
// Setup animations
const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
}) })
anime.set('.credits__heading > *, .credits__category > ul > li', {
opacity: 0,
translateY: 24,
})
anime.set('.credits__category', {
opacity: 0,
})
// Elements
timeline.add({
targets: '.credits__heading > *, .credits__category',
opacity: 1,
translateY: 0,
delay: anime.stagger(350),
}, 500)
// Names
timeline.add({
targets: '.credits__category > ul > li',
opacity: 1,
translateY: 0,
delay: anime.stagger(350),
}, 1100)
// Transition in
requestAnimationFrame(timeline.play)
}) })
</script> </script>

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/env'
import { page } from '$app/stores' import { page } from '$app/stores'
import { goto } from '$app/navigation' import { goto } from '$app/navigation'
import { getContext, onMount } from 'svelte' import { getContext, onMount } from 'svelte'
@@ -245,42 +244,6 @@
} }
/**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations
timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
})
// Reveal text
timeline.add({
targets: '.photos__intro .discover',
translateY: [16, 0],
opacity: [0, 1],
}, 900)
// Filters
timeline.add({
targets: '.photos__intro .filter',
translateY: [16, 0],
opacity: [0, 1],
complete ({ animatables }) {
const element = animatables[0].target
// Remove style to not interfere with CSS when scrolling back up over photos
element.removeAttribute('style')
}
}, 1300)
})
}
onMount(() => { onMount(() => {
/** /**
* Observers * Observers
@@ -316,11 +279,38 @@
const existingPhotos = photosGridEl.querySelectorAll('.photo') const existingPhotos = photosGridEl.querySelectorAll('.photo')
existingPhotos.forEach(el => observerPhotos.observe(el)) existingPhotos.forEach(el => observerPhotos.observe(el))
/**
* Animations
*/
// Transition in // Transition in
requestAnimationFrame(() => { const timeline: AnimeTimelineInstance = anime.timeline({
timeline.play() duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
}) })
// Reveal text
timeline.add({
targets: '.photos__intro .discover',
translateY: [16, 0],
opacity: [0, 1],
}, 900)
// Filters
timeline.add({
targets: '.photos__intro .filter',
translateY: [16, 0],
opacity: [0, 1],
complete ({ animatables }) {
const element = animatables[0].target
// Remove style to not interfere with CSS when scrolling back up over photos
element.removeAttribute('style')
}
}, 1300)
// Play animation
requestAnimationFrame(timeline.play)
// Destroy // Destroy
return () => { return () => {

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/env'
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'
@@ -25,44 +24,6 @@
let scrolledPastIntro = false let scrolledPastIntro = false
/**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
})
// Hero image
timeline.add({
targets: '.shop-page__background',
scale: [1.06, 1],
opacity: [0, 1],
duration: 2400,
}, 400)
// Intro top elements
timeline.add({
targets: '.shop-page__intro .top > *',
translateY: [-100, 0],
delay: anime.stagger(250),
}, 400)
// Intro navbar
timeline.add({
targets: '.shop-page__nav .container > *',
translateY: [100, 0],
delay: anime.stagger(250),
}, 700)
})
}
onMount(() => { onMount(() => {
// Reveal the nav past the Intro // Reveal the nav past the Intro
navObserver = new IntersectionObserver(entries => { navObserver = new IntersectionObserver(entries => {
@@ -75,11 +36,42 @@
}) })
navObserver.observe(introEl) navObserver.observe(introEl)
// Transition in
requestAnimationFrame(() => { /**
timeline.play() * Animations
*/
// Setup animations
const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
}) })
// Hero image
timeline.add({
targets: '.shop-page__background',
scale: [1.06, 1],
opacity: [0, 1],
duration: 2400,
}, 400)
// Intro top elements
timeline.add({
targets: '.shop-page__intro .top > *',
translateY: [-100, 0],
delay: anime.stagger(250),
}, 400)
// Intro navbar
timeline.add({
targets: '.shop-page__nav .container > *',
translateY: [100, 0],
delay: anime.stagger(250),
}, 700)
// Transition in
requestAnimationFrame(timeline.play)
// Destroy // Destroy
return () => { return () => {

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/env'
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'
@@ -8,7 +7,6 @@
import PageTransition from '$components/PageTransition.svelte' import PageTransition from '$components/PageTransition.svelte'
import Metas from '$components/Metas.svelte' import Metas from '$components/Metas.svelte'
import Image from '$components/atoms/Image.svelte' import Image from '$components/atoms/Image.svelte'
import SiteTitle from '$components/atoms/SiteTitle.svelte'
import ButtonCart from '$components/atoms/ButtonCart.svelte' import ButtonCart from '$components/atoms/ButtonCart.svelte'
import ShopLocationSwitcher from '$components/molecules/ShopLocationSwitcher.svelte' import ShopLocationSwitcher from '$components/molecules/ShopLocationSwitcher.svelte'
import PostersGrid from '$components/organisms/PostersGrid.svelte' import PostersGrid from '$components/organisms/PostersGrid.svelte'
@@ -20,46 +18,8 @@
const { shop, shopLocations, posters } = getContext('shop') const { shop, shopLocations, posters } = getContext('shop')
let introEl: HTMLElement let introEl: HTMLElement
let navObserver: IntersectionObserver var navObserver: IntersectionObserver
let scrolledPastIntro = false var scrolledPastIntro = false
/**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
})
// Hero image
timeline.add({
targets: '.shop-page__background',
scale: [1.06, 1],
opacity: [0, 1],
duration: 2400,
}, 400)
// Intro top elements
timeline.add({
targets: '.shop-page__intro .top > *',
translateY: [-100, 0],
delay: anime.stagger(250),
}, 400)
// Intro navbar
timeline.add({
targets: '.shop-page__nav .container > *',
translateY: [100, 0],
delay: anime.stagger(250),
}, 700)
})
}
onMount(() => { onMount(() => {
@@ -74,11 +34,42 @@
}) })
navObserver.observe(introEl) navObserver.observe(introEl)
// Transition in
requestAnimationFrame(() => { /**
timeline.play() * Animations
*/
// Setup animations
const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
}) })
// Hero image
timeline.add({
targets: '.shop-page__background',
scale: [1.06, 1],
opacity: [0, 1],
duration: 2400,
}, 400)
// Intro top elements
timeline.add({
targets: '.shop-page__intro .top > *',
translateY: [-100, 0],
delay: anime.stagger(250),
}, 400)
// Intro navbar
timeline.add({
targets: '.shop-page__nav .container > *',
translateY: [100, 0],
delay: anime.stagger(250),
}, 700)
// Transition in
requestAnimationFrame(timeline.play)
// Destroy // Destroy
return () => { return () => {

View File

@@ -1,5 +1,4 @@
<script lang="ts"> import { fade } from 'svelte/transition' <script lang="ts">
import { browser } from '$app/env'
import { onMount } from 'svelte' import { onMount } from 'svelte'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import anime from 'animejs' import anime from 'animejs'
@@ -16,51 +15,41 @@
export let issues: any export let issues: any
/**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations
timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
})
anime.set('.subscribe__top > *, .subscribe__issues', {
opacity: 0,
translateY: 24,
})
// Elements
timeline.add({
targets: '.subscribe__top > *, .subscribe__issues',
opacity: 1,
translateY: 0,
delay: anime.stagger(200),
}, 500)
// Reveal each issue
timeline.add({
targets: '.subscribe__issues .issue',
opacity: [0, 1],
translateY: [16, 0],
delay: anime.stagger(150),
duration: 1000,
}, 1000)
})
}
onMount(() => { onMount(() => {
// Transition in /**
requestAnimationFrame(() => { * Animations
timeline.play() */
// Setup animations
const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
autoplay: false,
}) })
anime.set('.subscribe__top > *, .subscribe__issues', {
opacity: 0,
translateY: 24,
})
// Elements
timeline.add({
targets: '.subscribe__top > *, .subscribe__issues',
opacity: 1,
translateY: 0,
delay: anime.stagger(200),
}, 500)
// Reveal each issue
timeline.add({
targets: '.subscribe__issues .issue',
opacity: [0, 1],
translateY: [16, 0],
delay: anime.stagger(150),
duration: 1000,
}, 1000)
// Transition in
requestAnimationFrame(timeline.play)
}) })
</script> </script>

View File

@@ -113,7 +113,7 @@ export const getRandomElement = (array: any[]): any => {
/** /**
* Get a DOM element's position * Get a DOM element's position
*/ */
export const getPosition = (node, scope?: HTMLElement) => { export const getPosition = (node: any, scope?: HTMLElement) => {
const root = scope || document const root = scope || document
let offsetTop = node.offsetTop let offsetTop = node.offsetTop
let offsetLeft = node.offsetLeft let offsetLeft = node.offsetLeft