⚠️ Rework completely how transitions works
- Use Svelte/Sapper native if and transitions to show either the page content or the loader, then load each page animationIn - Code is safe on SSR side, using process.browser on this if - The <main> element is on position absolute to fade nicely the different pages - Code cleaning
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
export let href = '#'
|
||||
export let type = 'a'
|
||||
export let text = ''
|
||||
export let noScroll = null
|
||||
</script>
|
||||
|
||||
{#if type === 'button'}
|
||||
@@ -14,7 +13,7 @@
|
||||
</button>
|
||||
|
||||
{:else}
|
||||
<a {href} class={$$props.class ? $$props.class : 'button'} sapper-noscroll={noScroll} on:click>
|
||||
<a {href} class={$$props.class ? $$props.class : 'button'} on:click>
|
||||
<slot></slot>
|
||||
<div class="text" data-text={text}>
|
||||
<span>{text}</span>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<script>
|
||||
export let href = '#'
|
||||
export let text = ''
|
||||
export let noScroll = null
|
||||
</script>
|
||||
|
||||
<a href={href} class="link-change" sapper-noscroll={noScroll}>
|
||||
<a href={href} class="link-change">
|
||||
{text}
|
||||
<span class="icon">
|
||||
<slot></slot>
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
export let text = ''
|
||||
export let target = null
|
||||
export let rel = null
|
||||
export let noScroll = null
|
||||
</script>
|
||||
|
||||
<a class="link-translate" {href} {target} {rel} sapper-noscroll={noScroll}>
|
||||
<a class="link-translate" {href} {target} {rel}>
|
||||
<slot />
|
||||
<div class="text" data-text={text}>
|
||||
<span>{text}</span>
|
||||
|
||||
@@ -6,28 +6,30 @@
|
||||
import { animateIn } from 'animations/TitleSite'
|
||||
|
||||
// Props and variables
|
||||
export let init = false
|
||||
let scope
|
||||
let mounted = false
|
||||
let scope
|
||||
|
||||
|
||||
/*
|
||||
** Run code on component mount
|
||||
** Run code when mounted
|
||||
*/
|
||||
onMount(() => {
|
||||
animateIn(scope, init)
|
||||
animateIn(scope)
|
||||
mounted = true
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="title-location title-location--inline" bind:this={scope} style="opacity: {mounted ? 1 : 0}">
|
||||
<div class="title-location title-location--inline"
|
||||
bind:this={scope}
|
||||
style="opacity: {mounted ? 1 : 0}"
|
||||
>
|
||||
<div role="heading" aria-level="1" aria-label="Houses">
|
||||
<div class="anim-mask">
|
||||
{@html charsToSpan('Houses')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<em>
|
||||
<em class="anim-mask">
|
||||
<span>of</span>
|
||||
<span>the</span>
|
||||
</em>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
|
||||
/*
|
||||
** Run code on browser only
|
||||
** Run code when mounted
|
||||
*/
|
||||
onMount(() => {
|
||||
// Get layout setting from storage
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</script>
|
||||
|
||||
<div class="location">
|
||||
<a href="/location/{country.slug}/{slug}" rel="prefetch" sapper-noscroll>
|
||||
<a href="/location/{country.slug}/{slug}" rel="prefetch">
|
||||
<img src={country.flag.full_url} alt="Flag of {country.name}">
|
||||
<div class="anim-mask mask-city">
|
||||
<h3 class="location__city">{name}</h3>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
<div class="photo__image wrap">
|
||||
<div class="align">
|
||||
<a href="/viewer/{location.country.slug}/{location.slug}/{photo.slug}" sapper-noscroll>
|
||||
<a href="/viewer/{location.country.slug}/{location.slug}/{photo.slug}">
|
||||
<picture class="photo__picture">
|
||||
<source media="(min-width: 992px)" data-srcset={getThumbnail(private_hash, 1300)}>
|
||||
<source media="(min-width: 768px)" data-srcset={getThumbnail(private_hash, 992)}>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<div class="switcher {type}">
|
||||
<div class="switcher__text" class:empty={!location}>
|
||||
<a href="/" sapper-noscroll>
|
||||
<a href="/">
|
||||
{#if !location}
|
||||
<span class="top">Houses</span>
|
||||
<span class="bottom">
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
|
||||
<div class="button-control button-control--dashed switcher__icon">
|
||||
<a href="/choose" aria-label="Change the location" sapper-noscroll>
|
||||
<a href="/choose" aria-label="Change the location">
|
||||
<IconGlobe
|
||||
color={type.includes('side') ? '#333' : '#fff'}
|
||||
width={type.includes('side') ? 18 : 24}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import { stores } from '@sapper/app'
|
||||
import { currentLocation, fullscreen } from 'utils/store'
|
||||
import { getThumbnail, formatDate } from 'utils/functions'
|
||||
const dispatch = createEventDispatcher()
|
||||
const { page } = stores()
|
||||
|
||||
// Dependencies
|
||||
import SwipeListener from 'swipe-listener'
|
||||
@@ -15,13 +17,9 @@
|
||||
import Counter from 'atoms/Counter'
|
||||
import PaginationDots from 'molecules/PaginationDots'
|
||||
|
||||
// Props
|
||||
// Props and Variables
|
||||
export let photos
|
||||
export let viewer = false
|
||||
|
||||
// Variables
|
||||
const dispatch = createEventDispatcher()
|
||||
const { page } = stores()
|
||||
let scope
|
||||
let swiped
|
||||
let currentIndex = 0
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<li>
|
||||
<ul>
|
||||
<li>
|
||||
<LinkTranslate href="/credits" text="Credits" rel="prefetch" noScroll="true" />
|
||||
<LinkTranslate href="/credits" text="Credits" rel="prefetch" />
|
||||
</li>
|
||||
{#if $site}
|
||||
<li class="instagram">
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
|
||||
/*
|
||||
** Run code on component mount
|
||||
** Run code when mounted
|
||||
*/
|
||||
onMount(() => {
|
||||
// Entering transition
|
||||
|
||||
@@ -73,14 +73,11 @@
|
||||
locations,
|
||||
pageReady
|
||||
} from 'utils/store'
|
||||
const { page } = stores()
|
||||
|
||||
// Components
|
||||
import Transition from 'utils/Transition'
|
||||
import AnalyticsTracker from 'utils/AnalyticsTracker'
|
||||
|
||||
// Variables
|
||||
const { page } = stores()
|
||||
|
||||
|
||||
/*
|
||||
** Manipulate data
|
||||
@@ -92,7 +89,9 @@
|
||||
$countries.forEach(country => {
|
||||
const continent = $continents.find(cont => cont.id === country.continent.id)
|
||||
continent.countries = []
|
||||
!continent.countries.includes(country) && continent.countries.push(country)
|
||||
if (!continent.countries.includes(country)) {
|
||||
continent.countries.push(country)
|
||||
}
|
||||
})
|
||||
|
||||
// Replace each location's country by the database
|
||||
@@ -107,12 +106,12 @@
|
||||
<link rel="canonical" href={`https://${$page.host}${$page.path}`} />
|
||||
</svelte:head>
|
||||
|
||||
<main class="housesof" style="opacity: { $pageReady ? 1 : 0}">
|
||||
<main class="housesof"
|
||||
class:is-transitioning={!$pageReady}
|
||||
in:fade={{ duration: 600 }}
|
||||
out:fade={{ duration: 600 }}
|
||||
>
|
||||
<slot></slot>
|
||||
</main>
|
||||
|
||||
{#if process.env.CONFIG.TRANSITION === 'true'}
|
||||
<Transition />
|
||||
{/if}
|
||||
|
||||
<AnalyticsTracker {stores} id={process.env.CONFIG.GA_TRACKER_ID} />
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
site,
|
||||
currentLocation,
|
||||
currentPhotos,
|
||||
pageReady,
|
||||
pageTransition
|
||||
pageReady
|
||||
} from 'utils/store'
|
||||
|
||||
// Components
|
||||
@@ -15,11 +14,11 @@
|
||||
import Globe from 'molecules/InteractiveGlobe'
|
||||
import Locations from 'organisms/Locations'
|
||||
import Footer from 'organisms/Footer'
|
||||
import Transition from 'utils/Transition'
|
||||
import SocialMetas from 'utils/SocialMetas'
|
||||
|
||||
// Animations
|
||||
import { animateIn } from 'animations/page'
|
||||
pageTransition.onAnimationEnd = animateIn
|
||||
|
||||
// Variables
|
||||
const { page } = stores()
|
||||
@@ -52,7 +51,8 @@
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<section class="page explore">
|
||||
<Transition {animateIn}>
|
||||
<section class="page explore">
|
||||
<div class="wrap">
|
||||
<div class="page__top">
|
||||
<a href="/" class="button-control button-control--pink dir-left" rel="prefetch">
|
||||
@@ -71,6 +71,7 @@
|
||||
<Globe />
|
||||
|
||||
<Locations />
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
<Footer />
|
||||
</Transition>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { stores } from '@sapper/app'
|
||||
import { site, pageReady, pageTransition } from 'utils/store'
|
||||
import { site, pageReady } from 'utils/store'
|
||||
|
||||
// Components
|
||||
import IconArrow from 'atoms/IconArrow'
|
||||
@@ -10,10 +10,10 @@
|
||||
import InteractiveGlobe from 'molecules/InteractiveGlobe'
|
||||
import Footer from 'organisms/Footer'
|
||||
import SocialMetas from 'utils/SocialMetas'
|
||||
import Transition from 'utils/Transition'
|
||||
|
||||
// Animations
|
||||
import { animateIn } from 'animations/page'
|
||||
pageTransition.onAnimationEnd = animateIn
|
||||
|
||||
// Variables
|
||||
const { page } = stores()
|
||||
@@ -39,7 +39,8 @@
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<section class="page">
|
||||
<Transition>
|
||||
<section class="page">
|
||||
<div class="wrap">
|
||||
<div class="page__top page__part">
|
||||
<a href="/" class="button-control button-control--pink dir-left">
|
||||
@@ -80,4 +81,5 @@
|
||||
<InteractiveGlobe type="part" />
|
||||
|
||||
<Footer />
|
||||
</section>
|
||||
</section>
|
||||
</Transition>
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
site,
|
||||
currentLocation,
|
||||
currentPhotos,
|
||||
pageReady,
|
||||
pageTransition
|
||||
pageReady
|
||||
} from 'utils/store'
|
||||
import { charsToSpan } from 'utils/functions'
|
||||
|
||||
@@ -53,7 +52,6 @@
|
||||
|
||||
// Animations
|
||||
import { animateIn } from 'animations/index'
|
||||
pageTransition.onAnimationEnd = animateIn
|
||||
|
||||
// Props and variables
|
||||
export let photos = ''
|
||||
@@ -86,7 +84,8 @@
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<section class="intro">
|
||||
<Transition animateIn={animateIn}>
|
||||
<section class="intro">
|
||||
<div class="anim-mask">
|
||||
<div class="anim title-parallax" id="title-houses">
|
||||
<h1 class="title-massive" aria-label="Houses">
|
||||
@@ -111,9 +110,9 @@
|
||||
<Fullscreen />
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section class="explore explore--homepage">
|
||||
<section class="explore explore--homepage">
|
||||
<div class="of" id="title-of" aria-label="of">
|
||||
<div class="anim-mask">
|
||||
{@html charsToSpan('of')}
|
||||
@@ -133,6 +132,7 @@
|
||||
</div>
|
||||
|
||||
<Locations />
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
<Footer />
|
||||
</Transition>
|
||||
@@ -29,8 +29,7 @@
|
||||
locations,
|
||||
currentLocation,
|
||||
currentPhotos,
|
||||
pageReady,
|
||||
pageTransition
|
||||
pageReady
|
||||
} from 'utils/store'
|
||||
import { formatDate, relativeTime, getThumbnail } from 'utils/functions'
|
||||
|
||||
@@ -47,10 +46,10 @@
|
||||
import Pagination from 'organisms/Pagination'
|
||||
import Footer from 'organisms/Footer'
|
||||
import SocialMetas from 'utils/SocialMetas'
|
||||
import Transition from 'utils/Transition'
|
||||
|
||||
// Animations
|
||||
import { animateIn } from 'animations/place'
|
||||
pageTransition.onAnimationEnd = animateIn
|
||||
|
||||
// Props and variables
|
||||
export let photos
|
||||
@@ -108,8 +107,8 @@
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
|
||||
<section class="place">
|
||||
<Transition {animateIn}>
|
||||
<section class="place">
|
||||
<div class="place__title">
|
||||
<h1 class="title-location title-location--big" aria-label="Houses of {location.name}">
|
||||
<span class="place__title_top anim-mask">
|
||||
@@ -121,7 +120,7 @@
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<a href="/choose" class="button-control button-control--big button-control--dashed" aria-label="Change the location" sapper-noscroll>
|
||||
<a href="/choose" class="button-control button-control--big button-control--dashed" aria-label="Change the location">
|
||||
<span class="center">
|
||||
<IconGlobe width="44" color="#fff" />
|
||||
<span>Change</span>
|
||||
@@ -140,7 +139,7 @@
|
||||
{#if description}
|
||||
<p>
|
||||
Houses Of
|
||||
<LinkChange href="/choose" text={location.name} noScroll="true">
|
||||
<LinkChange href="/choose" text={location.name}>
|
||||
<IconGlobeSmall width="14" color="#999" />
|
||||
</LinkChange>
|
||||
{description}
|
||||
@@ -164,9 +163,9 @@
|
||||
style="--url-desktop: url({illustration_desktop.full_url}); --url-mobile: url({illustration_mobile.full_url});"
|
||||
/>
|
||||
{/if}
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section class="photos photos--{layoutSetting || 'list'}">
|
||||
<section class="photos photos--{layoutSetting || 'list'}">
|
||||
<div class="photos__sidewrap wrap">
|
||||
<aside class="photos__side">
|
||||
<Switcher type="switcher--side" />
|
||||
@@ -201,6 +200,7 @@
|
||||
<p style="text-align: center; color: #333;">No photo for {location.name}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
<Footer />
|
||||
</Transition>
|
||||
|
||||
@@ -35,8 +35,7 @@
|
||||
locations,
|
||||
currentLocation,
|
||||
currentPhotos,
|
||||
pageReady,
|
||||
pageTransition
|
||||
pageReady
|
||||
} from 'utils/store'
|
||||
import { getThumbnail } from 'utils/functions'
|
||||
|
||||
@@ -45,11 +44,11 @@
|
||||
import IconCross from 'atoms/IconCross'
|
||||
import Carousel from 'organisms/Carousel'
|
||||
import Fullscreen from 'organisms/Fullscreen'
|
||||
import Transition from 'utils/Transition'
|
||||
import SocialMetas from 'utils/SocialMetas'
|
||||
|
||||
// Animations
|
||||
import { animateIn } from 'animations/viewer'
|
||||
pageTransition.onAnimationEnd = animateIn
|
||||
|
||||
// Props
|
||||
export let photos
|
||||
@@ -100,7 +99,8 @@
|
||||
|
||||
<svelte:window bind:innerWidth={windowWidth} />
|
||||
|
||||
<section class="viewer">
|
||||
<Transition {animateIn}>
|
||||
<section class="viewer">
|
||||
<div class="viewer__top">
|
||||
<p class="tip">Tap for fullscreen</p>
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
<IconCross color="#fff" width="18" class="icon" hidden="true" />
|
||||
</a>
|
||||
</div>
|
||||
<a href="/" bind:this={gotoLink} aria-hidden="true" hidden class="hidden" sapper-noscroll> </a>
|
||||
<a href="/" bind:this={gotoLink} aria-hidden="true" hidden class="hidden"> </a>
|
||||
</div>
|
||||
|
||||
<Carousel
|
||||
@@ -126,4 +126,5 @@
|
||||
/>
|
||||
|
||||
<Fullscreen />
|
||||
</section>
|
||||
</section>
|
||||
</Transition>
|
||||
|
||||
@@ -141,17 +141,22 @@ button {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@include breakpoint (sm) {
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
em {
|
||||
margin: 8px 0 4px;
|
||||
|
||||
@include breakpoint (sm) {
|
||||
margin: 2px 16px 0 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint (sm) {
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
span {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
// Main page
|
||||
.housesof {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
transition: opacity 0.4s $ease-quart;
|
||||
will-change: opacity;
|
||||
|
||||
&.is-transitioning {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +1,54 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { fly, fade } from 'svelte/transition'
|
||||
import { quartInOut, quartOut } from 'svelte/easing'
|
||||
import { stores } from '@sapper/app'
|
||||
import {
|
||||
pageReady,
|
||||
pageTransition,
|
||||
transitionLong,
|
||||
transitionPanelIn,
|
||||
transitionDelay
|
||||
} from './store'
|
||||
import { pageReady, firstLoad } from 'utils/store'
|
||||
const { page } = stores()
|
||||
|
||||
// Animations
|
||||
import { panelBackgroundOut } from 'animations/Transition'
|
||||
|
||||
// Components
|
||||
import TitleSite from 'atoms/TitleSite'
|
||||
import IconGlobe from 'atoms/IconGlobe'
|
||||
|
||||
// Animations
|
||||
import { animateIn, animateOut } from 'animations/Transition'
|
||||
|
||||
|
||||
// Check if path is excluded
|
||||
const isExcluded = path => path.includes(['/viewer/'])
|
||||
|
||||
/*
|
||||
** 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
|
||||
*/
|
||||
// Props and Variables
|
||||
export let animateIn = scope => {}
|
||||
let scope
|
||||
let firstLoad = true
|
||||
let previousPage = ''
|
||||
let show = false
|
||||
|
||||
// 1. Watch page change
|
||||
page.subscribe(page => {
|
||||
// Run transition if page is not excluded
|
||||
if (!isExcluded(previousPage) || !isExcluded(page.path)) {
|
||||
// Run the loader animation (first load only)
|
||||
if (!firstLoad) {
|
||||
animateIn(scope)
|
||||
}
|
||||
|
||||
// Reset pageReady when changing page
|
||||
pageReady.set(false)
|
||||
}
|
||||
|
||||
// Update page for viewer navigation checking
|
||||
previousPage = page.path
|
||||
})
|
||||
|
||||
// 2. Watch when loaded changes
|
||||
// Listen for when a route is mounted
|
||||
pageReady.subscribe(loaded => {
|
||||
if (loaded) {
|
||||
// 3. Hide the loader and set firstLoad to false (in order to show the second icon afterwards)
|
||||
animateOut(scope, () => firstLoad = false)
|
||||
|
||||
// Scroll back to top of page
|
||||
setTimeout(() => window.scrollTo(0,0), transitionDelay)
|
||||
|
||||
// 4. Run page entering animation
|
||||
pageTransition.onAnimationEnd()
|
||||
setTimeout(() => {
|
||||
show = true
|
||||
firstLoad.set(false)
|
||||
setTimeout(() => animateIn(scope), 1)
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="transition" id="transition" aria-hidden="true" bind:this={scope}>
|
||||
<div class="transition__loader">
|
||||
{#if firstLoad}
|
||||
<TitleSite init="true" />
|
||||
{#if show || !process.browser}
|
||||
<slot></slot>
|
||||
|
||||
{:else}
|
||||
<div class="transition" id="transition" aria-hidden="true" bind:this={scope}>
|
||||
<div class="transition__loader"
|
||||
in:fly={{ y: 24, duration: 800, easing: quartOut }}
|
||||
out:fly={{ y: -window.innerHeight/2, duration: 1400, easing: quartInOut }}
|
||||
>
|
||||
{#if $firstLoad}
|
||||
<TitleSite />
|
||||
{:else}
|
||||
<IconGlobe width="44" color="#fff" animated="true" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="transition__background" />
|
||||
<div class="transition__background"
|
||||
in:fade={{ duration: 600, easing: quartInOut }}
|
||||
out:panelBackgroundOut={{ duration: 1400 }}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Svelte
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
// Define environment
|
||||
@@ -25,17 +24,14 @@ export let currentLocation = writable()
|
||||
export let currentPhotos = writable()
|
||||
|
||||
// State
|
||||
export let pageReady = writable(false)
|
||||
export const pageTransition = {
|
||||
onAnimationEnd () {}
|
||||
}
|
||||
export let pageReady = writable(false, () => {})
|
||||
export let firstLoad = writable(true)
|
||||
export let fullscreen = writable()
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
Animation related
|
||||
========================================================================== */
|
||||
export const transitionDelay = 1400
|
||||
export const transitionNormal = 1400
|
||||
export const transitionLong = 1800
|
||||
export const transitionPanelIn = 700
|
||||
export const animDelay = 800
|
||||
export const animDuration = 1400
|
||||
export const animDurationLong = 1800
|
||||
|
||||
Reference in New Issue
Block a user