Add transitions to missing pages and attempt to animate it better?

This commit is contained in:
2021-12-06 15:10:39 +01:00
parent 8728d0f834
commit 0b772f26cb
9 changed files with 366 additions and 164 deletions

View File

@@ -12,6 +12,7 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import advancedFormat from 'dayjs/plugin/advancedFormat.js' import advancedFormat from 'dayjs/plugin/advancedFormat.js'
import anime from 'animejs' import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
// Components // Components
import Metas from '$components/Metas.svelte' import Metas from '$components/Metas.svelte'
import SplitText from '$components/SplitText.svelte' import SplitText from '$components/SplitText.svelte'
@@ -213,9 +214,15 @@
} }
onMount(() => { /**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations // Setup animations
const timeline = anime.timeline({ timeline = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart', easing: 'easeOutQuart',
}) })
@@ -223,13 +230,17 @@
anime.set('.viewer-photo__picture', { anime.set('.viewer-photo__picture', {
opacity: 0, opacity: 0,
}) })
anime.set('.viewer-photo__picture.is-1', {
translateY: 24,
})
// Photos // Photos
timeline.add({ timeline.add({
targets: '.viewer-photo__picture.is-1', targets: '.viewer-photo__picture.is-1',
opacity: 1, opacity: 1,
translateY: 0,
duration: 900, duration: 900,
}, 200) }, 250)
timeline.add({ timeline.add({
targets: '.viewer-photo__picture:not(.is-0):not(.is-1)', targets: '.viewer-photo__picture:not(.is-0):not(.is-1)',
opacity: 1, opacity: 1,
@@ -238,20 +249,17 @@
return [`-${x}`, 0] return [`-${x}`, 0]
}, },
delay: anime.stagger(55) delay: anime.stagger(55)
}, 400) }, 500)
// Prev button // Prev/Next buttons
timeline.add({ timeline.add({
targets: '.viewer-photo__controls button:first-child', targets: '.viewer-photo__controls button',
translateX: [-16, 0], translateX (item: HTMLElement) {
let direction = item.classList.contains('prev') ? -1 : 1
return [16 * direction, 0]
},
opacity: [0, 1], opacity: [0, 1],
}, 600) }, 450)
// Next button
timeline.add({
targets: '.viewer-photo__controls button:last-child',
translateX: [16, 0],
opacity: [0, 1],
}, 600)
// Infos // Infos
@@ -263,19 +271,31 @@
}, 400) }, 400)
anime.set('.viewer-photo__index', { opacity: 0 }) anime.set('.viewer-photo__index', {
opacity: 0
})
// Index // Index
timeline.add({ timeline.add({
targets: '.viewer-photo__index', targets: '.viewer-photo__index',
opacity: 1, opacity: 1,
duration: 900, duration: 900,
}, 600) }, 600)
// Fly each number
timeline.add({ timeline.add({
targets: '.viewer-photo__index .char', targets: '.viewer-photo__index .char',
translateY: ['100%', 0], translateY: ['100%', 0],
delay: anime.stagger(200), delay: anime.stagger(200),
duration: 1000, duration: 1000,
}, 1100) }, 700)
})
}
onMount(() => {
// Transition in
requestAnimationFrame(() => {
timeline.play()
})
}) })
</script> </script>
@@ -328,10 +348,10 @@
{/each} {/each}
<div class="viewer-photo__controls"> <div class="viewer-photo__controls">
<ButtonCircle class="shadow-box-dark" disabled={!canGoNext} clone={true} on:click={goToPrevious}> <ButtonCircle class="prev shadow-box-dark" disabled={!canGoNext} clone={true} on:click={goToPrevious}>
<IconArrow color="pink" flip={true} /> <IconArrow color="pink" flip={true} />
</ButtonCircle> </ButtonCircle>
<ButtonCircle class="shadow-box-dark" disabled={!canGoPrev} clone={true} on:click={goToNext}> <ButtonCircle class="next shadow-box-dark" disabled={!canGoPrev} clone={true} on:click={goToNext}>
<IconArrow color="pink" /> <IconArrow color="pink" />
</ButtonCircle> </ButtonCircle>
</div> </div>

View File

@@ -1,7 +1,9 @@
<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'
import type { AnimeTimelineInstance } from 'animejs'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import advancedFormat from 'dayjs/plugin/advancedFormat.js' import advancedFormat from 'dayjs/plugin/advancedFormat.js'
import relativeTime from 'dayjs/plugin/relativeTime.js' import relativeTime from 'dayjs/plugin/relativeTime.js'
@@ -106,10 +108,17 @@
} }
onMount(() => { /**
const timeline = anime.timeline({ * Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
timeline = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart' easing: 'easeOutQuart',
autoplay: false,
}) })
// Title word // Title word
@@ -140,8 +149,12 @@
translateY: ['10%', 0], translateY: ['10%', 0],
opacity: [0, 1], opacity: [0, 1],
}, 900 + ($navigating ? DURATION.PAGE_IN : 0)) }, 900 + ($navigating ? DURATION.PAGE_IN : 0))
})
}
onMount(() => {
// Photos IntersectionObserver // Photos IntersectionObserver
observerPhotos = new IntersectionObserver(entries => { observerPhotos = new IntersectionObserver(entries => {
entries.forEach(({ isIntersecting, target }: IntersectionObserverEntry) => { entries.forEach(({ isIntersecting, target }: IntersectionObserverEntry) => {
@@ -173,6 +186,11 @@
const existingPhotos = photosListEl.querySelectorAll('.house') const existingPhotos = photosListEl.querySelectorAll('.house')
existingPhotos.forEach(el => observerPhotos.observe(el)) existingPhotos.forEach(el => observerPhotos.observe(el))
// Transition in
requestAnimationFrame(() => {
timeline.play()
})
// Destroy // Destroy
return () => { return () => {

View File

@@ -1,10 +1,65 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/env'
import { onMount } from 'svelte'
import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
// Components // Components
import Metas from '$components/Metas.svelte' import Metas from '$components/Metas.svelte'
import SiteTitle from '$components/atoms/SiteTitle.svelte' import SiteTitle from '$components/atoms/SiteTitle.svelte'
import InteractiveGlobe from '$components/organisms/InteractiveGlobe.svelte' import InteractiveGlobe from '$components/organisms/InteractiveGlobe.svelte'
import Image from '$components/atoms/Image.svelte'
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(() => {
// Transition in
requestAnimationFrame(() => {
timeline.play()
})
})
</script> </script>
<Metas <Metas

View File

@@ -1,7 +1,9 @@
<script lang="ts"> <script lang="ts">
import { getContext, onMount } from 'svelte' import { browser } from '$app/env'
import { page } from '$app/stores' import { page } from '$app/stores'
import { getContext, onMount } from 'svelte'
import anime from 'animejs' import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
// Components // Components
import Metas from '$components/Metas.svelte' import Metas from '$components/Metas.svelte'
import SplitText from '$components/SplitText.svelte' import SplitText from '$components/SplitText.svelte'
@@ -24,34 +26,55 @@
let scrollY: number, innerHeight: number let scrollY: number, innerHeight: number
onMount(() => { /**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations // Setup animations
const timeline = anime.timeline({ timeline = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart', easing: 'easeOutQuart',
autoplay: false,
}) })
// Reveal text // Reveal text
anime.set('.homepage__headline', {
translateY: 16,
opacity: 0,
})
timeline.add({ timeline.add({
targets: '.homepage__headline', targets: '.homepage__headline',
translateY: [16, 0], translateY: 0,
opacity: [0, 1], opacity: 1,
}, 900) }, 900)
// Animate collage photos // Animate collage photos
anime.set('.homepage__collage .photo-card', {
opacity: 0,
translateY: '33.33%',
rotate: -4,
})
timeline.add({ timeline.add({
targets: '.homepage__collage .photo-card', targets: '.homepage__collage .photo-card',
translateY: ['33.333%', 0], translateY: 0,
rotate (item: HTMLElement) { rotate (item: HTMLElement) {
// Get target CSS variable for rotation return getComputedStyle(item).getPropertyValue('--rotation')
const rotateEnd = getComputedStyle(item).getPropertyValue('--rotation')
return [-4, rotateEnd]
}, },
opacity: [0, 1], opacity: 1,
duration: 1200, duration: 1200,
delay: anime.stagger(75), delay: anime.stagger(75),
}, 0) }, 0)
}) })
}
onMount(() => {
requestAnimationFrame(() => {
timeline.play()
})
})
</script> </script>
<svelte:window bind:scrollY bind:innerHeight /> <svelte:window bind:scrollY bind:innerHeight />

View File

@@ -1,4 +1,5 @@
<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'
@@ -7,6 +8,7 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime.js' import relativeTime from 'dayjs/plugin/relativeTime.js'
import anime from 'animejs' import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
import { map, lerp, throttle } from '$utils/functions' import { map, lerp, throttle } from '$utils/functions'
// Components // Components
import Metas from '$components/Metas.svelte' import Metas from '$components/Metas.svelte'
@@ -239,13 +241,18 @@
} }
onMount(() => {
/** /**
* Entering animation * Transition: Anime timeline
*/ */
const timeline = anime.timeline({ let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations
timeline = anime.timeline({
duration: 1600, duration: 1600,
easing: 'easeOutQuart', easing: 'easeOutQuart',
autoplay: false,
}) })
// Reveal text // Reveal text
@@ -266,8 +273,11 @@
element.removeAttribute('style') element.removeAttribute('style')
} }
}, 1300) }, 1300)
})
}
onMount(() => {
/** /**
* Observers * Observers
*/ */
@@ -280,7 +290,7 @@
isIntersecting && observerPhotos.unobserve(target) isIntersecting && observerPhotos.unobserve(target)
}) })
}, { }, {
threshold: 0.3, threshold: 0.25,
rootMargin: '0px 0px 10%' rootMargin: '0px 0px 10%'
}) })
@@ -302,6 +312,11 @@
const existingPhotos = photosGridEl.querySelectorAll('.photo') const existingPhotos = photosGridEl.querySelectorAll('.photo')
existingPhotos.forEach(el => observerPhotos.observe(el)) existingPhotos.forEach(el => observerPhotos.observe(el))
// Transition in
requestAnimationFrame(() => {
timeline.play()
})
// Destroy // Destroy
return () => { return () => {

View File

@@ -1,5 +1,9 @@
<script lang="ts"> import { fade } from 'svelte/transition' <script lang="ts"> import { fade } from 'svelte/transition'
import { browser } from '$app/env'
import { onMount } from 'svelte'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
// Components // Components
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'
@@ -7,7 +11,56 @@
import InteractiveGlobe from '$components/organisms/InteractiveGlobe.svelte'; import InteractiveGlobe from '$components/organisms/InteractiveGlobe.svelte';
import EmailForm from '$components/molecules/EmailForm.svelte'; import EmailForm from '$components/molecules/EmailForm.svelte';
export let data: any
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(() => {
// Transition in
requestAnimationFrame(() => {
timeline.play()
})
})
</script> </script>
<Metas <Metas
@@ -17,11 +70,13 @@
/> />
<main class="subscribe"> <main class="subscribe">
<div class="subscribe__top">
<Heading <Heading
text="If you wish to be pinged when new photos are added to and limited prints become available on our shop, sign up below." text={data.newsletter_page_text}
/> />
<EmailForm /> <EmailForm />
</div>
<section class="subscribe__issues"> <section class="subscribe__issues">
<h2 class="title-small">Past Issues</h2> <h2 class="title-small">Past Issues</h2>
@@ -58,6 +113,10 @@
export async function load ({ page, fetch, session, stuff }) { export async function load ({ page, fetch, session, stuff }) {
const res = await fetchAPI(` const res = await fetchAPI(`
query { query {
settings {
newsletter_page_text
}
newsletter (limit: -1, sort: "-issue") { newsletter (limit: -1, sort: "-issue") {
issue issue
title title
@@ -72,6 +131,7 @@
return { return {
props: { props: {
data: data.settings,
issues: data.newsletter, issues: data.newsletter,
} }
} }

View File

@@ -1,9 +1,6 @@
.site-title { .site-title {
font-family: $font-serif; font-family: $font-serif;
.pink {
font-weight: 300;
}
.middle { .middle {
position: relative; position: relative;
top: -4px; top: -4px;
@@ -13,11 +10,6 @@
letter-spacing: 1px; letter-spacing: 1px;
} }
// Pink text
&__pink {
color: $color-secondary;
}
/* /*
** Variants ** Variants
@@ -53,7 +45,7 @@
&--inline { &--inline {
display: block; display: block;
align-items: baseline; align-items: baseline;
color: $color-lightpurple; color: $color-secondary;
justify-content: center; justify-content: center;
font-size: rem(52px); font-size: rem(52px);

View File

@@ -7,6 +7,9 @@
} }
a { a {
display: block;
width: 100%;
height: 100%;
text-decoration: none; text-decoration: none;
cursor: zoom-in; cursor: zoom-in;
} }

View File

@@ -109,11 +109,27 @@
color: rgba($color-tertiary, 0.6); color: rgba($color-tertiary, 0.6);
} }
a:hover { a:hover {
color: #fff; color: $color-tertiary;
} }
li { li {
display: inline-block; display: inline-block;
line-height: 1.5; line-height: 1.7;
picture {
display: inline-block;
width: 12px;
height: 12px;
margin-right: 4px;
overflow: hidden;
vertical-align: middle;
border-radius: 100%;
img {
display: block;
width: 100%;
height: 100%;
}
}
&:after { &:after {
display: inline-block; display: inline-block;