Fix the locations list filters and add reveal transitions

Typically add a delay between two click (Limiter) to click again. Thanks Nico!
This commit is contained in:
2020-02-26 15:56:22 +01:00
parent 042440188e
commit c9b1f63c64
5 changed files with 110 additions and 47 deletions

View File

@@ -1,2 +1,23 @@
// import anime from 'animejs' // import anime from 'animejs'
import { crossfade } from 'svelte/transition'
import { quartOut } from 'svelte/easing'
// Crossfade transition
export const [crossfadeSend, crossfadeReceive] = crossfade({
duration: d => Math.sqrt(d * 200),
fallback(node, params) {
const style = getComputedStyle(node)
const transform = style.transform === 'none' ? '' : style.transform
return {
duration: 600,
easing: quartOut,
css: t => `
transform: ${transform} scale(${t});
opacity: ${t}
`
}
}
})

View File

@@ -1,15 +1,16 @@
<script> <script>
export let name // Props
export let slug export let location
export let country
const src = country.flag.full_url
</script> </script>
<div class="location"> <div class="location" data-aos="location">
<a href="/location/{country.slug}/{slug}"> <a href="/location/{location.country.slug}/{location.slug}">
<img {src} alt={'Flag of ' + country.name} /> <img src={location.country.flag.full_url} alt="Flag of {location.country.name}">
<h3 class="location__city">{name}</h3> <div class="anim-mask mask-city">
<p class="location__country style-caps">{country.name}</p> <h3 class="location__city">{location.name}</h3>
</div>
<div class="anim-mask mask-country">
<p class="location__country style-caps">{location.country.name}</p>
</div>
</a> </a>
</div> </div>

View File

@@ -1,15 +1,20 @@
<script> <script>
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { flip } from 'svelte/animate' import { flip } from 'svelte/animate'
import { crossfade } from 'svelte/transition'
import { quintOut } from 'svelte/easing'
import { locations, countries, continents } from '../store' import { locations, countries, continents } from '../store'
import { crossfadeReceive, crossfadeSend } from '../animations'
import * as fn from '../functions'
// Dependencies
import AOS from 'aos'
// Components // Components
import Button from '../atoms/Button' import Button from '../atoms/Button'
import Location from '../molecules/Location' import Location from '../molecules/Location'
// Variables // Variables
const transitionDuration = 800
let clickOrigin = Date.now()
let filterLocations let filterLocations
let continentsToDisplay = [] let continentsToDisplay = []
let continentsFiltered = [] let continentsFiltered = []
@@ -20,31 +25,24 @@
continentsFiltered = [...continentsToDisplay] continentsFiltered = [...continentsToDisplay]
// Filter by continent // Filter by continent
const toggleContinents = (event, continent) => continentsFiltered = (!continent) ? [...continentsToDisplay] : [continent] // detects if click difference if too short
const toggleContinents = (event, continent) => {
// Crossfade animation if (Date.now() - clickOrigin < transitionDuration) {
const [send, receive] = crossfade({ return
duration: d => Math.sqrt(d * 200),
fallback(node, params) {
const style = getComputedStyle(node)
const transform = style.transform === 'none' ? '' : style.transform
return {
duration: 600,
easing: quintOut,
css: t => `
transform: ${transform} scale(${t});
opacity: ${t}
`
} }
continentsFiltered = (!continent) ? [...continentsToDisplay] : [continent]
clickOrigin = Date.now()
} }
})
/* /*
** Run code on browser only ** Run code on browser only
*/ */
onMount(() => { onMount(() => {
// Scroll apparitions
if (process.browser) {
AOS.init()
}
}) })
</script> </script>
@@ -56,29 +54,23 @@
<ul class="browse__continents" id="continents"> <ul class="browse__continents" id="continents">
{#if continentsToDisplay.length > 1} {#if continentsToDisplay.length > 1}
<li on:click={e => toggleContinents(e)}> <li on:click={e => toggleContinents(e)}>
<Button type="button" text="All" className="button-outline {(continentsFiltered.length <= 1) ? 'disabled' : ''}" /> <Button type="button" text="All" class="button-outline {(continentsFiltered.length <= 1) ? 'disabled' : ''}" />
</li> </li>
{/if} {/if}
{#each continentsToDisplay as continent} {#each continentsToDisplay as continent}
<li on:click={e => toggleContinents(e, continent)}> <li on:click={e => toggleContinents(e, continent)}>
<Button type="button" text={continent.name} className="button-outline {(!continentsFiltered.includes(continent)) ? 'disabled' : ''}" /> <Button type="button" text={continent.name} class="button-outline {(!continentsFiltered.includes(continent)) ? 'disabled' : ''}" />
</li> </li>
{/each} {/each}
</ul> </ul>
<div class="browse__locations" id="locations_list"> <div class="browse__locations" id="locations_list">
{#each filteredLocations as location (location.id)} {#each filteredLocations as location (location.id)}
<div <div animate:flip="{{ duration: transitionDuration }}"
animate:flip="{{duration: 600}}" in:crossfadeReceive="{{ key: location.id }}"
in:receive="{{key: location.id}}" out:crossfadeSend="{{ key: location.id }}"
out:send="{{key: location.id}}"
> >
<!-- Animation: Grid with auto-min is fucking up the transition --> <Location {location} />
<Location
name={location.name}
slug={location.slug}
country={location.country}
/>
</div> </div>
{/each} {/each}
</div> </div>

View File

@@ -101,6 +101,40 @@
} }
// Location reveal
[data-aos="location"] {
img {
opacity: 0;
transform: scale(1.15);
transition: opacity 0.6s $ease-quart, transform 0.6s $ease-quart;
will-change: opacity, transform;
}
h3, p {
transform: translateY(150%);
transition: transform 0.6s $ease-quart;
will-change: transform;
}
h3 {
transition: all 0.6s $ease-quart;
transition-delay: 100ms;
}
p {
transition-delay: 200ms;
}
&.aos-animate {
img {
opacity: 1;
transform: scale(1);
}
h3, p {
transform: translateY(0);
}
}
}
/* ========================================================================== /* ==========================================================================
KEYFRAMES ANIMATIONS KEYFRAMES ANIMATIONS

View File

@@ -37,12 +37,13 @@
// Locations // Locations
&__locations { &__locations {
min-height: 200px;
margin-top: 112px; margin-top: 112px;
@include breakpoint (sm) { @include breakpoint (sm) {
display: flex; display: flex;
// display: grid; // display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); // grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-column-gap: 120px; grid-column-gap: 120px;
grid-row-gap: pxVW(120); grid-row-gap: pxVW(120);
justify-content: center; justify-content: center;
@@ -59,7 +60,12 @@
margin-bottom: 48px; margin-bottom: 48px;
@include breakpoint (sm) { @include breakpoint (sm) {
margin-bottom: 0; margin-left: pxVW(72);
margin-right: pxVW(72);
}
@include breakpoint (xl) {
margin-left: 72px;
margin-right: 72px;
} }
a { a {
@@ -82,17 +88,26 @@
// City // City
&__city { &__city {
margin: 24px 0 16px;
font-family: $font-serif; font-family: $font-serif;
font-size: rem(32px); font-size: rem(32px);
color: $color-secondary; color: $color-secondary;
transition: color 85ms ease-in-out;
@include breakpoint (sm) { @include breakpoint (sm) {
font-size: rem(48px); font-size: rem(48px);
}
}
.mask-city {
height: 40px;
margin: 24px 0 16px;
@include breakpoint (sm) {
height: 64px;
margin-bottom: 24px; margin-bottom: 24px;
} }
} }
.mask-country {
height: 16px;
}
// Shape on hover // Shape on hover
&:after { &:after {