Finish About page

Add entering and scroll animations
This commit is contained in:
2022-08-18 23:20:48 +02:00
parent 97fcf8c60c
commit 2539834ce1
4 changed files with 373 additions and 38 deletions

View File

@@ -3,28 +3,31 @@
</style>
<script lang="ts">
import { navigating } from '$app/stores'
import { onMount, afterUpdate } from 'svelte'
import type { PageData } from './$types'
import { scroll, animate, inView, type ScrollOptions, timeline, stagger } from 'motion'
import { map } from '$utils/functions'
import { scroll, animate, inView, type ScrollOptions } from 'motion'
import { getAssetUrlKey } from '$utils/api'
import { DELAY } from '$utils/contants'
import { quartOut } from '$animations/easings'
// Components
import Metas from '$components/Metas.svelte'
import PageTransition from '$components/PageTransition.svelte'
import AboutGridPhoto from '$components/atoms/AboutGridPhoto.svelte'
import Image from '$components/atoms/Image.svelte'
import Button from '$components/atoms/Button.svelte'
import AboutGridPhoto from '$components/atoms/AboutGridPhoto.svelte'
import Heading from '$components/molecules/Heading.svelte'
import ProcessStep from '$components/molecules/ProcessStep.svelte'
import ShopModule from '$components/organisms/ShopModule.svelte'
import NewsletterModule from '$components/organisms/NewsletterModule.svelte'
export let data: PageData
// console.log(data)
const { about, photos } = data
let scrollY: number, innerWidth: number, innerHeight: number
let purposeEl: HTMLElement
let stepsEl: HTMLElement
let photosGridEl: HTMLElement
let purposeEl: HTMLElement, stepsEl: HTMLElement, photosGridEl: HTMLElement
let photoFirstEl: HTMLElement, photoUsEl: HTMLElement
let photosGridOffset: number = photosGridEl && photosGridEl.offsetTop
$: parallaxPhotos = photosGridEl && map(scrollY, photosGridOffset - innerHeight, photosGridOffset + innerHeight / 1.5, 0, innerHeight * 0.15, true)
@@ -32,20 +35,120 @@
? [0, 2, 5, 7, 9, 12, 17, 20, 22, 26, 30, 32, 34]
: [0]
const introText = about.intro_text
.replace('<strong>',
`<a href="/${about.intro_firstlocation.country.slug}/${about.intro_firstlocation.slug}" sveltekit:noscroll sveltekit:prefetch>
<img src="${getAssetUrlKey(about.intro_firstlocation.country.flag.id, 'square-small-jpg')}" width="32" height="32" alt="${about.intro_firstlocation.country.flag.title}">
<strong>
`)
.replace('</strong>', '</strong></a>')
onMount(() => {
/**
* Animations
*/
const animation = timeline([
// Heading
['.heading .text', {
y: [24, 0],
opacity: [0, 1],
z: 0,
}, {
at: 0.5,
}],
// First photo
[photoFirstEl, {
y: ['10%', 0],
rotate: [0, getComputedStyle(photoFirstEl).getPropertyValue('--rotate')],
opacity: [0, 1],
z: 0,
}, {
at: 0.75,
opacity: {
duration: 1
}
}],
// Portrait photo
[photoUsEl, {
y: ['10%', 0],
x: [0, '5%'],
rotate: [0, 5],
opacity: [0, 1],
z: 0,
}, {
at: 1,
opacity: {
duration: 1
}
}],
// Text
['.about__introduction .text', {
y: [32, 0],
opacity: [0, 1],
z: 0,
}, {
at: 1.2,
}],
], {
delay: $navigating ? DELAY.PAGE_LOADING / 1000 : 0,
defaultOptions: {
duration: 1.6,
easing: quartOut,
},
})
animation.stop()
// Run animation
requestAnimationFrame(animation.play)
/**
* Intro parallax
*/
// First photo
scroll(animate(photoFirstEl.querySelector('figure'), {
y: ['-5%', '5%'],
x: [0, '-3%'],
rotate: [-3, 0],
z: 0,
}), {
target: photoFirstEl,
offset: ["-200%", "150%"]
})
// Portrait photo
scroll(animate(photoUsEl.querySelector('figure'), {
y: [0, '5%'],
x: [0, '-2%'],
rotate: [-2, 0],
z: 0,
}), {
target: photoUsEl,
offset: ["-250%", "150%"]
})
/**
* Purpose reveal
*/
inView(purposeEl, ({ target, isIntersecting }) => {
target.classList.toggle('is-visible', isIntersecting)
}, { amount: 0.75 })
}, { amount: 0.6 })
// Parallax
scroll(animate(purposeEl.querySelector('picture img'), {
y: [0, '40%'],
}))
/**
* Steps scroll animation
*/
const cards = stepsEl.querySelectorAll('.step')
const cardsAmount = data.about.process_steps.length
const cardsAmount = about.process_steps.length
cards.forEach((card: HTMLElement, i: number) => {
const index = i + 1
@@ -86,37 +189,90 @@
<PageTransition name="about">
<Heading
text={data.about.description}
text={about.description}
/>
<section class="about__introduction">
<div class="container grid">
<div class="photo-first" bind:this={photoFirstEl}>
<figure>
<Image
class="shadow-box-dark"
id={about.intro_firstphoto.id}
alt={about.intro_firstphoto.title}
sizeKey="photo-list"
sizes={{
small: { width: 400 },
medium: { width: 600 },
large: { width: 800 },
}}
ratio={1.5}
/>
<figcaption class="text-info">{about.intro_firstphoto_caption}</figcaption>
</figure>
</div>
<div class="photo-us" bind:this={photoUsEl}>
<figure>
<Image
class="shadow-box-dark"
id={about.intro_portraits.id}
alt={about.intro_portraits.title}
sizeKey="square"
sizes={{
small: { width: 250 }
}}
ratio={1}
/>
</figure>
</div>
<div class="text text-normal">
{@html introText}
</div>
</div>
</section>
<section class="about__purpose" bind:this={purposeEl}>
<div class="container-wide">
<div class="text title-xl" role="heading">
{@html data.about.purpose_text}
{@html about.purpose_text}
</div>
<div class="background" />
<div class="background">
<picture class="background__illustration">
<source media="(min-width: 1200px)" srcset={getAssetUrlKey(about.intro_firstlocation.illustration_desktop_2x.id, 'illustration-desktop-2x')}>
<source media="(min-width: 768px)" srcset={getAssetUrlKey(about.intro_firstlocation.illustration_desktop.id, 'illustration-desktop-1x')}>
<img
src={getAssetUrlKey(about.intro_firstlocation.illustration_mobile.id, 'illustration-mobile')}
width={320}
height={824}
alt="Illustration for {about.intro_firstlocation.name}"
decoding="async"
/>
</picture>
</div>
</div>
</section>
<section class="about__process">
<div class="container grid">
<div class="title">
<h2 class="title-big">{data.about.process_title}</h2>
<p class="text-normal">{data.about.process_subtitle}</p>
<h2 class="title-big">{about.process_title}</h2>
<p class="text-normal">{about.process_subtitle}</p>
</div>
<div class="steps" bind:this={stepsEl}
style:--cards-amount={data.about.process_steps.length}
style:--cards-amount={about.process_steps.length}
>
{#each data.about.process_steps as step, index}
{#each about.process_steps as step, index}
<ProcessStep {...step} index={index} />
{/each}
</div>
<div class="intention">
<p class="intention__content title-medium">
{data.about.process_intention}
{about.process_intention}
</p>
</div>
</div>
@@ -125,7 +281,7 @@
<section class="about__photos" bind:this={photosGridEl}>
<div class="container-wide">
<div class="photos-grid" style:--parallax-y="{parallaxPhotos}px">
{#each data.photos as { image: { id }, title }, index}
{#each photos as { image: { id }, title }, index}
<AboutGridPhoto class="about-grid-photo"
{id}
alt={title}
@@ -138,10 +294,10 @@
<div class="about__bottom container grid">
<section class="about__interest grid">
<h2 class="title-xl">{data.about.contact_title}</h2>
<h2 class="title-xl">{about.contact_title}</h2>
<div class="blocks">
{#each data.about.contact_blocks as { title, text, link, button }}
{#each about.contact_blocks as { title, text, link, button }}
<div class="block">
<h3 class="text-label">{title}</h3>
<p class="text-normal">{text}</p>