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 advancedFormat from 'dayjs/plugin/advancedFormat.js'
import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
// Components
import Metas from '$components/Metas.svelte'
import SplitText from '$components/SplitText.svelte'
@@ -213,69 +214,88 @@
}
/**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// Setup animations
timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
})
anime.set('.viewer-photo__picture', {
opacity: 0,
})
anime.set('.viewer-photo__picture.is-1', {
translateY: 24,
})
// Photos
timeline.add({
targets: '.viewer-photo__picture.is-1',
opacity: 1,
translateY: 0,
duration: 900,
}, 250)
timeline.add({
targets: '.viewer-photo__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-photo__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-photo__info > *',
translateY: [24, 0],
opacity: [0, 1],
delay: anime.stagger(200)
}, 400)
anime.set('.viewer-photo__index', {
opacity: 0
})
// Index
timeline.add({
targets: '.viewer-photo__index',
opacity: 1,
duration: 900,
}, 600)
// Fly each number
timeline.add({
targets: '.viewer-photo__index .char',
translateY: ['100%', 0],
delay: anime.stagger(200),
duration: 1000,
}, 700)
})
}
onMount(() => {
// Setup animations
const timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
// Transition in
requestAnimationFrame(() => {
timeline.play()
})
anime.set('.viewer-photo__picture', {
opacity: 0,
})
// Photos
timeline.add({
targets: '.viewer-photo__picture.is-1',
opacity: 1,
duration: 900,
}, 200)
timeline.add({
targets: '.viewer-photo__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)
}, 400)
// Prev button
timeline.add({
targets: '.viewer-photo__controls button:first-child',
translateX: [-16, 0],
opacity: [0, 1],
}, 600)
// Next button
timeline.add({
targets: '.viewer-photo__controls button:last-child',
translateX: [16, 0],
opacity: [0, 1],
}, 600)
// Infos
timeline.add({
targets: '.viewer-photo__info > *',
translateY: [24, 0],
opacity: [0, 1],
delay: anime.stagger(200)
}, 400)
anime.set('.viewer-photo__index', { opacity: 0 })
// Index
timeline.add({
targets: '.viewer-photo__index',
opacity: 1,
duration: 900,
}, 600)
timeline.add({
targets: '.viewer-photo__index .char',
translateY: ['100%', 0],
delay: anime.stagger(200),
duration: 1000,
}, 1100)
})
</script>
@@ -328,10 +348,10 @@
{/each}
<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} />
</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" />
</ButtonCircle>
</div>

View File

@@ -1,7 +1,9 @@
<script lang="ts">
import { browser } from '$app/env'
import { navigating, page } from '$app/stores'
import { onMount } from 'svelte'
import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
import dayjs from 'dayjs'
import advancedFormat from 'dayjs/plugin/advancedFormat.js'
import relativeTime from 'dayjs/plugin/relativeTime.js'
@@ -106,42 +108,53 @@
}
onMount(() => {
const timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart'
/**
* 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))
})
// 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(() => {
// Photos IntersectionObserver
observerPhotos = new IntersectionObserver(entries => {
entries.forEach(({ isIntersecting, target }: IntersectionObserverEntry) => {
@@ -173,6 +186,11 @@
const existingPhotos = photosListEl.querySelectorAll('.house')
existingPhotos.forEach(el => observerPhotos.observe(el))
// Transition in
requestAnimationFrame(() => {
timeline.play()
})
// Destroy
return () => {

View File

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

View File

@@ -1,7 +1,9 @@
<script lang="ts">
import { getContext, onMount } from 'svelte'
import { browser } from '$app/env'
import { page } from '$app/stores'
import { getContext, onMount } from 'svelte'
import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
// Components
import Metas from '$components/Metas.svelte'
import SplitText from '$components/SplitText.svelte'
@@ -24,33 +26,54 @@
let scrollY: number, innerHeight: number
onMount(() => {
// Setup animations
const timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
/**
* Transition: Anime timeline
*/
let timeline: AnimeTimelineInstance
if (browser) {
requestAnimationFrame(() => {
// 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)
})
}
// 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.333%', 0],
rotate (item: HTMLElement) {
// Get target CSS variable for rotation
const rotateEnd = getComputedStyle(item).getPropertyValue('--rotation')
return [-4, rotateEnd]
},
opacity: [0, 1],
duration: 1200,
delay: anime.stagger(75),
}, 0)
onMount(() => {
requestAnimationFrame(() => {
timeline.play()
})
})
</script>

View File

@@ -1,4 +1,5 @@
<script lang="ts">
import { browser } from '$app/env'
import { page } from '$app/stores'
import { goto } from '$app/navigation'
import { getContext, onMount } from 'svelte'
@@ -7,6 +8,7 @@
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime.js'
import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
import { map, lerp, throttle } from '$utils/functions'
// Components
import Metas from '$components/Metas.svelte'
@@ -239,35 +241,43 @@
}
onMount(() => {
/**
* Entering animation
*/
const timeline = anime.timeline({
duration: 1600,
easing: 'easeOutQuart',
/**
* 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)
})
// 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(() => {
/**
* Observers
*/
@@ -280,7 +290,7 @@
isIntersecting && observerPhotos.unobserve(target)
})
}, {
threshold: 0.3,
threshold: 0.25,
rootMargin: '0px 0px 10%'
})
@@ -302,6 +312,11 @@
const existingPhotos = photosGridEl.querySelectorAll('.photo')
existingPhotos.forEach(el => observerPhotos.observe(el))
// Transition in
requestAnimationFrame(() => {
timeline.play()
})
// Destroy
return () => {

View File

@@ -1,5 +1,9 @@
<script lang="ts"> import { fade } from 'svelte/transition'
import { browser } from '$app/env'
import { onMount } from 'svelte'
import dayjs from 'dayjs'
import anime from 'animejs'
import type { AnimeTimelineInstance } from 'animejs'
// Components
import Metas from '$components/Metas.svelte'
import Image from '$components/atoms/Image.svelte'
@@ -7,7 +11,56 @@
import InteractiveGlobe from '$components/organisms/InteractiveGlobe.svelte';
import EmailForm from '$components/molecules/EmailForm.svelte';
export let data: 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>
<Metas
@@ -17,11 +70,13 @@
/>
<main class="subscribe">
<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."
/>
<div class="subscribe__top">
<Heading
text={data.newsletter_page_text}
/>
<EmailForm />
<EmailForm />
</div>
<section class="subscribe__issues">
<h2 class="title-small">Past Issues</h2>
@@ -58,6 +113,10 @@
export async function load ({ page, fetch, session, stuff }) {
const res = await fetchAPI(`
query {
settings {
newsletter_page_text
}
newsletter (limit: -1, sort: "-issue") {
issue
title
@@ -72,6 +131,7 @@
return {
props: {
data: data.settings,
issues: data.newsletter,
}
}

View File

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

View File

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

View File

@@ -109,11 +109,27 @@
color: rgba($color-tertiary, 0.6);
}
a:hover {
color: #fff;
color: $color-tertiary;
}
li {
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 {
display: inline-block;