diff --git a/src/animations/Carousel.js b/src/animations/Carousel.js new file mode 100644 index 0000000..50f4ba4 --- /dev/null +++ b/src/animations/Carousel.js @@ -0,0 +1,56 @@ +import anime from 'animejs' +import ScrollOut from 'scroll-out' +import { animDuration } from '../utils/store' + + +/* +** Transition In +*/ +export const animateIn = () => { + // Card: Active + const cardActive = ScrollOut({ + once: true, + targets: '.gallery__photo--active', + onShown (el) { + anime({ + targets: el, + top: [32, 0], + duration: 1200, + delay: 650, + easing: 'easeOutQuart' + }) + } + }) + + // Card: Prev + const cardPrev = ScrollOut({ + once: true, + targets: '.gallery__photo--prev', + onShown (el) { + anime({ + targets: el, + top: [5, 0], + left: [-64, 0], + duration: 1200, + delay: 690, + easing: 'easeOutQuart' + }) + } + }) + + // Card: Prev + const cardNext = ScrollOut({ + once: true, + targets: '.gallery__photo--next', + onShown (el) { + anime({ + targets: el, + top: [5, 0], + left: [48, 0], + duration: 1200, + delay: 710, + easing: 'easeOutQuart' + }) + } + }) +} \ No newline at end of file diff --git a/src/animations/Locations.js b/src/animations/Locations.js new file mode 100644 index 0000000..0c471a1 --- /dev/null +++ b/src/animations/Locations.js @@ -0,0 +1,47 @@ +import anime from 'animejs' +import ScrollOut from 'scroll-out' +import { animDuration } from '../utils/store' + + +/* +** Transition In +*/ +export const animateIn = () => { + // Each location + const locations = ScrollOut({ + targets: '#locations_list .location', + onShown (el) { + // Timeline + const tl = anime.timeline({ + autoplay: false, + duration: 600, + delay: anime.stagger(200), + easing: 'easeOutQuart' + }) + + // Image + tl.add({ + targets: el.querySelector('img'), + scale: [1.3, 1], + opacity: [0, 1], + duration: 1800, + delay: 100 + }) + + // Name + tl.add({ + targets: el.querySelector('h3'), + translateY: ['100%', 0] + }, 300) + + // Country + tl.add({ + targets: el.querySelector('p'), + translateY: ['100%', 0] + }, 550) + + // Play + tl.play() + } + }) +} diff --git a/src/animations/TitleSite.js b/src/animations/TitleSite.js new file mode 100644 index 0000000..a2efdb9 --- /dev/null +++ b/src/animations/TitleSite.js @@ -0,0 +1,34 @@ +import anime from 'animejs' +import ScrollOut from 'scroll-out' +import { animDuration } from '../utils/store' + + +/* +** Transition In +*/ +export const animateIn = callback => { + // On scroll animation + const title = ScrollOut({ + once: true, + targets: '.title-location', + onShown (el) { + // Each letters + anime({ + targets: el.querySelectorAll('span'), + translateY: ['100%', 0], + delay: anime.stagger(40), + duration: 1000, + easing: 'easeOutQuart' + }) + + // Word in between + anime({ + targets: el.querySelectorAll('em span'), + opacity: [0, 1], + delay: anime.stagger(80, { start: 400 }), + duration: 1000, + easing: 'easeOutQuart' + }) + } + }) +} diff --git a/src/animations/Transition.js b/src/animations/Transition.js new file mode 100644 index 0000000..18cad46 --- /dev/null +++ b/src/animations/Transition.js @@ -0,0 +1,61 @@ +import anime from 'animejs' +import { animDuration, animDurationLong } from '../utils/store' + + +/* +** Transition In +*/ +export const animateIn = () => { + // Panel itself + const transition = anime({ + targets: '#transition', + height: ['100%', '100%'], + opacity: [0, 1], + duration: 200, + delay: 0, + easing: 'easeInOutQuart' + }) + // Globe icon + const globe = anime({ + targets: '#transition svg', + opacity: [0, 1], + duration: 200, + delay: 0, + easing: 'easeInOutQuart' + }) + +} + + +/* +** Transition Out +*/ +export const animateOut = callback => { + // Panel itself + const transition = anime({ + targets: '#transition', + height: ['100%', 0], + duration: animDurationLong, + delay: 800, + easing: 'easeInOutQuart', + complete: callback + }) + + // Title + const title = anime({ + targets: '#transition .title-location', + opacity: 0, + duration: 600, + delay: 1400, + easing: 'easeInOutQuart' + }) + + // Globe icon + const globe = anime({ + targets: '#transition svg', + opacity: 0, + duration: 600, + delay: 1400, + easing: 'easeInOutQuart' + }) +} diff --git a/src/utils/animations.js b/src/animations/crossfade.js similarity index 83% rename from src/utils/animations.js rename to src/animations/crossfade.js index a61dfec..23f2a90 100644 --- a/src/utils/animations.js +++ b/src/animations/crossfade.js @@ -1,10 +1,8 @@ -// import anime from 'animejs' import { crossfade } from 'svelte/transition' import { quartOut } from 'svelte/easing' - // Crossfade transition -export const [crossfadeSend, crossfadeReceive] = crossfade({ +export const [send, receive] = crossfade({ duration: d => Math.sqrt(d * 200), fallback(node, params) { diff --git a/src/animations/index.js b/src/animations/index.js new file mode 100644 index 0000000..d690e20 --- /dev/null +++ b/src/animations/index.js @@ -0,0 +1,121 @@ +import anime from 'animejs' +import ScrollOut from 'scroll-out' +import { animDuration } from '../utils/store' +import { debounce } from '../utils/functions' + + +/* +** Transition In +*/ +export const animateIn = () => { + // Title: Houses + const titleHouses = ScrollOut({ + once: true, + targets: '#title-houses', + onShown (el) { + // Reveal + anime({ + targets: el.querySelectorAll('span'), + translateY: ['-70%', 0], + delay: anime.stagger(80), + duration: animDuration, + easing: 'easeOutQuart' + }) + + // Parallax on scroll + const translate = anime.timeline({ + autoplay: false, + duration: animDuration + }) + translate.add({ + targets: el, + translateX: ['-3%', '-17%'], + easing: 'easeOutQuart', + duration: animDuration + }) + window.addEventListener('scroll', fn.debounce(event => { + translate.seek(translate.duration * (window.scrollY / 1000)) + }), 50) + } + }) + + // Intro: Description + const introDescription = ScrollOut({ + once: true, + targets: '#intro-description', + onShown (el) { + anime({ + targets: el.querySelectorAll('p, a'), + opacity: [0, 1], + translateY: [8, 0], + duration: animDuration, + delay: anime.stagger(200, { start: 400 }), + easing: 'easeOutQuart' + }) + } + }) + + // Intro: Carousel + const introCarousel = ScrollOut({ + once: true, + targets: '#intro-carousel', + onShown(el) { + anime({ + targets: el, + opacity: [0, 1], + translateY: [24, 0], + duration: animDuration, + delay: 650, + easing: 'easeOutQuart' + }) + } + }) + + // Title: Of + const titleOf = ScrollOut({ + once: true, + targets: '#title-of', + onShown (el) { + anime({ + targets: el.querySelectorAll('span'), + translateY: ['100%', 0], + delay: anime.stagger(70), + duration: animDuration, + easing: 'easeOutQuart' + }) + } + }) + + // Title: World + const titleWorld = ScrollOut({ + once: true, + targets: '#title-world', + onShown (el, ctx) { + anime({ + targets: el.querySelectorAll('span'), + translateY: ['100%', 0], + delay: anime.stagger(70), + duration: animDuration, + easing: 'easeOutQuart' + }) + + // Parallax on scroll + const translate = anime.timeline({ + autoplay: false, + duration: animDuration + }) + translate.add({ + targets: el, + translateX: ['4%', '-4%'], + easing: 'easeOutQuart', + duration: animDuration + }) + + if (ctx.visible) { + window.addEventListener('scroll', debounce(event => { + translate.seek(translate.duration * (window.scrollY / 1000)) + }), 35) + } + } + }) +} diff --git a/src/animations/page.js b/src/animations/page.js new file mode 100644 index 0000000..3baba56 --- /dev/null +++ b/src/animations/page.js @@ -0,0 +1,25 @@ +import anime from 'animejs' +import ScrollOut from 'scroll-out' +import { animDuration, animDurationLong } from '../utils/store' + + +/* +** Transition In +*/ +export const animateIn = () => { + // Simple fade + const page = ScrollOut({ + once: true, + targets: '.page', + onShown (el) { + anime({ + targets: '.page__part', + opacity: [0, 1], + translateY: [8, 0], + duration: 1800, + delay: anime.stagger(220, { start: animDurationLong * 0.3 }), + easing: 'easeOutQuart' + }) + } + }) +} diff --git a/src/animations/place.js b/src/animations/place.js new file mode 100644 index 0000000..38e00d3 --- /dev/null +++ b/src/animations/place.js @@ -0,0 +1,60 @@ +import anime from 'animejs' +// import ScrollOut from 'scroll-out' +import { animDuration } from '../utils/store' + + +/* +** Transition In +*/ +export const animateIn = () => { + const tl = anime.timeline({ + duration: 1800, + easing: 'easeOutQuart' + }) + + // Title: Houses + tl.add({ + targets: '.place__title_houses', + translateY: ['150%', 0], + duration: animDuration + }) + // Title: Of + tl.add({ + targets: '.place__title_of', + opacity: [0, 1], + duration: 800 + }, 550) + // Title: Place name + tl.add({ + targets: '.place__title_name', + translateY: ['150%', 0], + duration: animDuration + }, 200) + + // Switcher link + tl.add({ + targets: '.place__title .button-control', + scale: [0.95, 1], + opacity: [0, 1], + duration: animDuration + }, 700) + + // Illustration + tl.add({ + targets: '.place__illustration', + scale: [1.075, 1], + opacity: [0, 1], + duration: animDuration + }, 200) + + // Description + tl.add({ + targets: '.place__description', + opacity: [0, 1], + translateY: [24, 0], + duration: animDuration + }, 800) + + // Play + tl.play() +} diff --git a/src/atoms/TitleSite.svelte b/src/atoms/TitleSite.svelte index 8ed73bc..6d9e89e 100644 --- a/src/atoms/TitleSite.svelte +++ b/src/atoms/TitleSite.svelte @@ -1,17 +1,33 @@
-
+
{@html lettersToSpan('Houses')}
- of the + + of + the + -
+
{@html lettersToSpan('World')}
diff --git a/src/molecules/InteractiveGlobe.svelte b/src/molecules/InteractiveGlobe.svelte index d868f70..6c5c824 100644 --- a/src/molecules/InteractiveGlobe.svelte +++ b/src/molecules/InteractiveGlobe.svelte @@ -40,7 +40,7 @@ // const globe = document.querySelector('.globe img') // const places = document.querySelectorAll('.globe .pin--place') - // // Init function + // Init function // init = () => { // mapWidth = globe.getBoundingClientRect().width // mapHeight = globe.getBoundingClientRect().height @@ -59,7 +59,7 @@ // init() // } - // // init() + // init() }) diff --git a/src/molecules/Location.svelte b/src/molecules/Location.svelte index fb9e7a3..4f108a8 100644 --- a/src/molecules/Location.svelte +++ b/src/molecules/Location.svelte @@ -3,7 +3,7 @@ export let location -
+
Flag of {location.country.name}
diff --git a/src/molecules/Photo.svelte b/src/molecules/Photo.svelte index 24acc34..9b99bd4 100644 --- a/src/molecules/Photo.svelte +++ b/src/molecules/Photo.svelte @@ -3,9 +3,6 @@ import { fly } from 'svelte/transition' import { quartOut } from 'svelte/easing' import { site, currentLocation } from '../utils/store' - - // Dependencies - import * as basicScroll from 'basicscroll' import { getThumbnail, formatDate } from '../utils/functions' // Props and variables @@ -29,27 +26,27 @@ */ onMount(() => { // Parallax on photo when the image has been loaded - const parallaxNumber = basicScroll.default.create({ - elem: photoElement.querySelector('.photo__number'), - direct: photoElement, - from: 'top-bottom', - to: 'bottom-top', - props: { - '--translate': { - from: '-75%', - to: '-25%' - } - } - }) - parallaxNumber.start() - parallaxNumber.calculate() - parallaxNumber.update() + // const parallaxNumber = basicScroll.default.create({ + // elem: photoElement.querySelector('.photo__number'), + // direct: photoElement, + // from: 'top-bottom', + // to: 'bottom-top', + // props: { + // '--translate': { + // from: '-75%', + // to: '-25%' + // } + // } + // }) + // parallaxNumber.start() + // parallaxNumber.calculate() + // parallaxNumber.update() })
diff --git a/src/organisms/Carousel.svelte b/src/organisms/Carousel.svelte index 986fff8..82dc260 100644 --- a/src/organisms/Carousel.svelte +++ b/src/organisms/Carousel.svelte @@ -6,6 +6,9 @@ const dispatch = createEventDispatcher() const { page } = stores() + // Animations + import { animateIn } from '../animations/carousel' + // Components import IconArrow from '../atoms/IconArrow' @@ -89,6 +92,9 @@ ** Run code on browser only */ onMount(() => { + // Entering transition + animateIn() + // Hover function hover = event => { const button = event.currentTarget.querySelector('button') diff --git a/src/organisms/Locations.svelte b/src/organisms/Locations.svelte index ffbdffa..b61b528 100644 --- a/src/organisms/Locations.svelte +++ b/src/organisms/Locations.svelte @@ -1,17 +1,17 @@ @@ -62,8 +60,8 @@
{#each filteredLocations as location (location.id)}
diff --git a/src/routes/_error.svelte b/src/routes/_error.svelte index 62babce..758f948 100644 --- a/src/routes/_error.svelte +++ b/src/routes/_error.svelte @@ -2,9 +2,6 @@ import { onMount } from 'svelte' import { site } from '../utils/store' - // Dependencies - import AOS from 'aos' - // Components import IconArrow from '../atoms/IconArrow' import TitleSite from '../atoms/TitleSite' @@ -21,10 +18,7 @@ ** Run code on browser only */ onMount(() => { - // Scroll apparitions - if (process.browser) { - AOS.init() - } + }) diff --git a/src/routes/_layout.svelte b/src/routes/_layout.svelte index 064b45b..78b502a 100644 --- a/src/routes/_layout.svelte +++ b/src/routes/_layout.svelte @@ -1,11 +1,5 @@ + + + diff --git a/src/routes/choose.svelte b/src/routes/choose.svelte index 96498c6..8f44e16 100644 --- a/src/routes/choose.svelte +++ b/src/routes/choose.svelte @@ -3,12 +3,11 @@ import { site, currentLocation, - currentPhotos + currentPhotos, + pageReady, + pageTransition } from '../utils/store' - // Depencencies - import AOS from 'aos' - // Components import IconArrow from '../atoms/IconArrow' import TitleSite from '../atoms/TitleSite' @@ -17,6 +16,11 @@ import Footer from '../organisms/Footer' import SocialMetas from '../utils/SocialMetas' + // Animations + import { animateIn } from '../animations/page' + pageTransition.onAnimationEnd = animateIn + + // Reset current location if existing $: { if ($currentLocation) currentLocation.set() @@ -25,13 +29,11 @@ /* - ** Run code on browser only + ** Run code when mounted */ onMount(() => { - // Scroll apparitions - if (process.browser) { - AOS.init() - } + // Page is loaded + pageReady.set(true) }) @@ -57,7 +59,7 @@
-
+

{$site.explore_globe}

diff --git a/src/routes/credits.svelte b/src/routes/credits.svelte index accf7d7..86f9efb 100644 --- a/src/routes/credits.svelte +++ b/src/routes/credits.svelte @@ -1,9 +1,6 @@ @@ -41,7 +37,7 @@
-
+ -
+

{$site.credits_text}

-
- {#each credits as category} +
+ {#each $site.credits_list as category}

{category.name}

{#each category.credits as person} diff --git a/src/routes/index.svelte b/src/routes/index.svelte index c999e76..2ade781 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -1,7 +1,5 @@ @@ -100,14 +75,14 @@
-
+

{@html lettersToSpan('Houses')}

-
+

{$site.description}

@@ -117,11 +92,13 @@
- +
-
+
{@html lettersToSpan('of')}
@@ -134,7 +111,7 @@
-

+

{@html lettersToSpan('World')}

diff --git a/src/routes/location/[country]/[location].svelte b/src/routes/location/[country]/[location].svelte index 2d21bf4..d4ecd5d 100644 --- a/src/routes/location/[country]/[location].svelte +++ b/src/routes/location/[country]/[location].svelte @@ -1,12 +1,5 @@ @@ -95,6 +98,7 @@ /> +

diff --git a/src/routes/viewer/[country]/[location]/[photo].svelte b/src/routes/viewer/[country]/[location]/[photo].svelte index d0fee5a..d2ed6aa 100644 --- a/src/routes/viewer/[country]/[location]/[photo].svelte +++ b/src/routes/viewer/[country]/[location]/[photo].svelte @@ -1,11 +1,6 @@ - Houses Of – Photos of {location.name}, {location.country.name} - + {$site.seo_name} – Photos of {$currentLocation.name}, {$currentLocation.country.name} + - - @@ -129,7 +126,7 @@ - + @@ -137,7 +134,7 @@

+ import { stores } from '@sapper/app' + import { pageReady, animDurationLong, pageTransition } from './store' + const { page } = stores() + + // Components + import TitleSite from '../atoms/TitleSite' + import IconGlobe from '../atoms/IconGlobe' + + // Animations + import { animateIn, animateOut } from '../animations/Transition' + + + /* + ** PAGE LOADING PROCESS + ** 1. Set pageReady to false + ** 1. Runs the Loader transition In + ** ?. The next page changes the value of pageReady when mounted (or later) + ** 2. The Loader detects the value change of pageReady + ** 3. Hide the loader with transition Out + ** 4. The Loader runs the page transition In via pageTransition + */ + let firstLoad = true + + // 1. Watch page change + page.subscribe(() => { + // Run the loader animation (only after first load) + if (!firstLoad && process.browser) { + animateIn() + } + // Set pageReady to false (?) + pageReady.set(false) + }) + + // 2. Watch when loaded changes + pageReady.subscribe(loaded => { + if (loaded) { + setTimeout(() => { + // Scroll back to top of page + window.scrollTo(0,0) + // 3. Hide the loader + // Also sets firstLoad to false in order to show the second icon afterwards + animateOut(() => firstLoad = false) + }, 200) // This duration allows to not come over the transition In + // [OU ALORS] les pages changent la valeur de loaded plus tard + + // 4. Run the page's transition in, but a little bit before the end of the loader + setTimeout(() => { + pageTransition.onAnimationEnd() + }, animDurationLong * 0.666 + 200) + } + }) + + + diff --git a/src/utils/store.js b/src/utils/store.js index 1f4ab6c..19123c5 100644 --- a/src/utils/store.js +++ b/src/utils/store.js @@ -25,4 +25,15 @@ export let currentLocation = writable() export let currentPhotos = writable() // State -export let loaded = writable(false) +// export let ready = writable(false) +export let pageReady = writable(false) +export const pageTransition = { + onAnimationEnd () {} +} + + +/* ========================================================================== + Animation related +========================================================================== */ +export const animDuration = 1400 +export const animDurationLong = 1800