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,15 +213,12 @@
} }
onMount(() => {
/** /**
* Transition: Anime timeline * Animations
*/ */
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations // Setup animations
timeline = anime.timeline({ const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart', easing: 'easeOutQuart',
}) })
@@ -286,15 +283,9 @@
delay: anime.stagger(200), delay: anime.stagger(200),
duration: 1000, duration: 1000,
}, 700) }, 700)
})
}
onMount(() => {
// Transition in // Transition in
requestAnimationFrame(() => { requestAnimationFrame(timeline.play)
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,14 +107,44 @@
} }
/** onMount(() => {
* Transition: Anime timeline // Photos IntersectionObserver
*/ observerPhotos = new IntersectionObserver(entries => {
let timeline: AnimeTimelineInstance entries.forEach(({ isIntersecting, target }: IntersectionObserverEntry) => {
target.classList.toggle('is-visible', isIntersecting)
if (browser) { // Run effect once
requestAnimationFrame(() => { isIntersecting && observerPhotos.unobserve(target)
timeline = anime.timeline({ })
}, {
threshold: 0.3,
rootMargin: '0px 0px 0px'
})
// Photos MutationObserver
mutationPhotos = new MutationObserver((mutationsList, observer) => {
// When adding new childs
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
// Observe new items
mutation.addedNodes.forEach((item: HTMLElement) => observerPhotos.observe(item))
}
}
})
mutationPhotos.observe(photosListEl, {
childList: true,
})
// Observe existing elements
const existingPhotos = photosListEl.querySelectorAll('.house')
existingPhotos.forEach(el => observerPhotos.observe(el))
/**
* Animations
*/
// Transition in
const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart', easing: 'easeOutQuart',
autoplay: false, autoplay: false,
@@ -149,47 +178,8 @@
translateY: ['10%', 0], translateY: ['10%', 0],
opacity: [0, 1], opacity: [0, 1],
}, 900 + ($navigating ? DURATION.PAGE_IN : 0)) }, 900 + ($navigating ? DURATION.PAGE_IN : 0))
})
}
requestAnimationFrame(timeline.play)
onMount(() => {
// Photos IntersectionObserver
observerPhotos = new IntersectionObserver(entries => {
entries.forEach(({ isIntersecting, target }: IntersectionObserverEntry) => {
target.classList.toggle('is-visible', isIntersecting)
// Run effect once
isIntersecting && observerPhotos.unobserve(target)
})
}, {
threshold: 0.3,
rootMargin: '0px 0px 0px'
})
// Photos MutationObserver
mutationPhotos = new MutationObserver((mutationsList, observer) => {
// When adding new childs
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
// Observe new items
mutation.addedNodes.forEach((item: HTMLElement) => observerPhotos.observe(item))
}
}
})
mutationPhotos.observe(photosListEl, {
childList: true,
})
// Observe existing elements
const existingPhotos = photosListEl.querySelectorAll('.house')
existingPhotos.forEach(el => observerPhotos.observe(el))
// Transition in
requestAnimationFrame(() => {
timeline.play()
})
// Destroy // Destroy

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,16 +12,12 @@
export let data: any export let data: any
onMount(() => {
/** /**
* Transition: Anime timeline * Animations
*/ */
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations // Setup animations
timeline = anime.timeline({ const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart', easing: 'easeOutQuart',
autoplay: false, autoplay: false,
@@ -51,15 +46,9 @@
translateY: 0, translateY: 0,
delay: anime.stagger(350), delay: anime.stagger(350),
}, 1100) }, 1100)
})
}
onMount(() => {
// Transition in // Transition in
requestAnimationFrame(() => { requestAnimationFrame(timeline.play)
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,14 +24,24 @@
let scrolledPastIntro = false let scrolledPastIntro = false
/** onMount(() => {
* Transition: Anime timeline // Reveal the nav past the Intro
*/ navObserver = new IntersectionObserver(entries => {
let timeline: AnimeTimelineInstance entries.forEach(entry => {
scrolledPastIntro = !entry.isIntersecting
})
}, {
threshold: 0,
rootMargin: '0px 0px 0px'
})
navObserver.observe(introEl)
if (browser) {
requestAnimationFrame(() => { /**
timeline = anime.timeline({ * Animations
*/
// Setup animations
const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart', easing: 'easeOutQuart',
autoplay: false, autoplay: false,
@@ -59,26 +68,9 @@
translateY: [100, 0], translateY: [100, 0],
delay: anime.stagger(250), delay: anime.stagger(250),
}, 700) }, 700)
})
}
onMount(() => {
// Reveal the nav past the Intro
navObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
scrolledPastIntro = !entry.isIntersecting
})
}, {
threshold: 0,
rootMargin: '0px 0px 0px'
})
navObserver.observe(introEl)
// Transition in // Transition in
requestAnimationFrame(() => { requestAnimationFrame(timeline.play)
timeline.play()
})
// Destroy // Destroy

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,18 +18,28 @@
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
onMount(() => {
// Reveal the nav past the Intro
navObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
scrolledPastIntro = !entry.isIntersecting
})
}, {
threshold: 0,
rootMargin: '0px 0px 0px'
})
navObserver.observe(introEl)
/** /**
* Transition: Anime timeline * Animations
*/ */
let timeline: AnimeTimelineInstance // Setup animations
const timeline: AnimeTimelineInstance = anime.timeline({
if (browser) {
requestAnimationFrame(() => {
timeline = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart', easing: 'easeOutQuart',
autoplay: false, autoplay: false,
@@ -58,26 +66,9 @@
translateY: [100, 0], translateY: [100, 0],
delay: anime.stagger(250), delay: anime.stagger(250),
}, 700) }, 700)
})
}
onMount(() => {
// Reveal the nav past the Intro
navObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
scrolledPastIntro = !entry.isIntersecting
})
}, {
threshold: 0,
rootMargin: '0px 0px 0px'
})
navObserver.observe(introEl)
// Transition in // Transition in
requestAnimationFrame(() => { requestAnimationFrame(timeline.play)
timeline.play()
})
// Destroy // Destroy

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,16 +15,12 @@
export let issues: any export let issues: any
onMount(() => {
/** /**
* Transition: Anime timeline * Animations
*/ */
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations // Setup animations
timeline = anime.timeline({ const timeline: AnimeTimelineInstance = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart', easing: 'easeOutQuart',
autoplay: false, autoplay: false,
@@ -52,15 +47,9 @@
delay: anime.stagger(150), delay: anime.stagger(150),
duration: 1000, duration: 1000,
}, 1000) }, 1000)
})
}
onMount(() => {
// Transition in // Transition in
requestAnimationFrame(() => { requestAnimationFrame(timeline.play)
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