[wip] Switch from Anime to Motion One for page animations
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import type { Easing } from 'motion'
|
||||
|
||||
|
||||
/**
|
||||
* Ease: Quart Out Array
|
||||
*/
|
||||
export const quartOut = [.165, .84, .44, 1]
|
||||
export const quartOut: Easing = [.165, .84, .44, 1]
|
||||
|
||||
|
||||
/**
|
||||
|
||||
45
src/animations/reveal.ts
Normal file
45
src/animations/reveal.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { animate, inView, stagger } from 'motion'
|
||||
import { quartOut } from '$animations/easings'
|
||||
|
||||
const defaultOptions = {
|
||||
stagger: null,
|
||||
delay: 0,
|
||||
duration: 1.6,
|
||||
easing: quartOut,
|
||||
}
|
||||
|
||||
export default (node: Element | any, {
|
||||
enable = true,
|
||||
children = undefined,
|
||||
animation = [],
|
||||
options = defaultOptions,
|
||||
}: RevealOptions) => {
|
||||
if (!enable) return
|
||||
|
||||
// Define targets from children, if empty get node
|
||||
const targets = children ? node.querySelectorAll(children) : [node]
|
||||
|
||||
// If animation has opacity starting with 0, hide it first
|
||||
if (animation.opacity && animation.opacity[0] === 0) {
|
||||
targets.forEach((el: HTMLElement) => el.style.opacity = '0')
|
||||
}
|
||||
|
||||
// Create inView instance
|
||||
inView(node, ({ isIntersecting }) => {
|
||||
const anim = animate(
|
||||
targets,
|
||||
animation,
|
||||
{
|
||||
delay: options.stagger ? stagger(options.stagger, { start: options.delay }) : options.delay,
|
||||
duration: options.duration,
|
||||
easing: options.easing,
|
||||
}
|
||||
)
|
||||
anim.stop()
|
||||
|
||||
// Run animation if in view and tab is active
|
||||
isIntersecting && requestAnimationFrame(anim.play)
|
||||
}, {
|
||||
amount: options.threshold,
|
||||
})
|
||||
}
|
||||
11
src/app.d.ts
vendored
11
src/app.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
|
||||
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare namespace App {
|
||||
@@ -25,7 +26,7 @@ declare namespace svelte.JSX {
|
||||
/**
|
||||
* Custom Types
|
||||
*/
|
||||
declare type PhotoGridAbout = {
|
||||
declare interface PhotoGridAbout {
|
||||
id: string
|
||||
title: string
|
||||
slug: string
|
||||
@@ -62,7 +63,7 @@ declare interface smoothScrollOptions {
|
||||
/**
|
||||
* Swipe options
|
||||
*/
|
||||
interface SwipeOptions {
|
||||
declare interface SwipeOptions {
|
||||
travelX?: number
|
||||
travelY?: number
|
||||
timeframe?: number
|
||||
@@ -72,17 +73,17 @@ interface SwipeOptions {
|
||||
/**
|
||||
* Reveal Animation
|
||||
*/
|
||||
declare interface RevealOptions {
|
||||
declare type RevealOptions = {
|
||||
enable?: boolean
|
||||
options?: TransitionOptions
|
||||
children?: string | HTMLElement
|
||||
animation: any
|
||||
}
|
||||
// Options interface
|
||||
declare interface TransitionOptions {
|
||||
declare type TransitionOptions = {
|
||||
threshold?: number
|
||||
duration?: number
|
||||
stagger?: number
|
||||
delay?: number
|
||||
easing?: any
|
||||
easing?: string | Easing
|
||||
}
|
||||
@@ -33,28 +33,30 @@
|
||||
on:blur={() => sendHover(false)}
|
||||
>
|
||||
{#if url}
|
||||
<a href={url} sveltekit:noscroll>
|
||||
<Image
|
||||
{id}
|
||||
sizeKey="postcard"
|
||||
{sizes}
|
||||
ratio={1.5}
|
||||
{alt}
|
||||
{lazy}
|
||||
/>
|
||||
{#if title && location}
|
||||
<div class="photo-card__info">
|
||||
<Image
|
||||
id={location.country.flag.id}
|
||||
sizeKey="square-small"
|
||||
width={24}
|
||||
height={24}
|
||||
alt="Flag of {location.country.name}"
|
||||
/>
|
||||
<p>{title} - {city ? `${city}, ` : ''}{location.name}, {location.country.name}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</a>
|
||||
<div class="photo-card__content">
|
||||
<a href={url} sveltekit:noscroll>
|
||||
<Image
|
||||
{id}
|
||||
sizeKey="postcard"
|
||||
{sizes}
|
||||
ratio={1.5}
|
||||
{alt}
|
||||
{lazy}
|
||||
/>
|
||||
{#if title && location}
|
||||
<div class="photo-card__info">
|
||||
<Image
|
||||
id={location.country.flag.id}
|
||||
sizeKey="square-small"
|
||||
width={24}
|
||||
height={24}
|
||||
alt="Flag of {location.country.name}"
|
||||
/>
|
||||
<p>{title} - {city ? `${city}, ` : ''}{location.name}, {location.country.name}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
{:else}
|
||||
<Image
|
||||
{id}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import { flip } from 'svelte/animate'
|
||||
import { quartOut } from 'svelte/easing'
|
||||
import { reveal, fly } from '$animations/index'
|
||||
import reveal from '$animations/reveal'
|
||||
import { send, receive } from '$animations/crossfade'
|
||||
import { throttle } from '$utils/functions'
|
||||
import { sendEvent } from '$utils/analytics'
|
||||
@@ -67,14 +67,13 @@
|
||||
|
||||
<ul class="browse__locations"
|
||||
use:reveal={{
|
||||
animation: fly,
|
||||
children: '.location',
|
||||
animation: { y: ['20%', 0], opacity: [0, 1] },
|
||||
options: {
|
||||
children: 'li',
|
||||
stagger: 100,
|
||||
duration: 1200,
|
||||
from: '20%',
|
||||
stagger: 0.105,
|
||||
duration: 1,
|
||||
threshold: 0.3,
|
||||
},
|
||||
threshold: 0.3,
|
||||
}}
|
||||
>
|
||||
{#each filteredLocations as location (location)}
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { navigating, page } from '$app/stores'
|
||||
import { page, navigating } from '$app/stores'
|
||||
import { onMount } from 'svelte'
|
||||
import anime, { type AnimeTimelineInstance } from 'animejs'
|
||||
import { timeline } from 'motion'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
||||
import { getAssetUrlKey } from '$utils/helpers'
|
||||
import { quartOut } from '$animations/easings'
|
||||
import { fetchAPI } from '$utils/api'
|
||||
import { DURATION } from '$utils/contants'
|
||||
import { DELAY, DURATION } from '$utils/contants'
|
||||
import { photoFields } from '.'
|
||||
// Components
|
||||
import Metas from '$components/Metas.svelte'
|
||||
@@ -146,43 +147,51 @@
|
||||
/**
|
||||
* Animations
|
||||
*/
|
||||
// Transition in
|
||||
const timeline: AnimeTimelineInstance = anime.timeline({
|
||||
duration: 1600,
|
||||
easing: 'easeOutQuart',
|
||||
autoplay: false,
|
||||
const animationDelay = $navigating ? DURATION.PAGE_IN : 0
|
||||
const animation = timeline([
|
||||
// Title word
|
||||
['.location-page__intro .word', {
|
||||
y: ['110%', 0],
|
||||
}, {
|
||||
at: 0.2 + animationDelay,
|
||||
}],
|
||||
|
||||
// Illustration
|
||||
['.location-page__illustration', {
|
||||
scale: [1.06, 1],
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
at: 0.4 + animationDelay,
|
||||
duration: 2.4,
|
||||
}],
|
||||
|
||||
// Title of
|
||||
['.location-page__intro .of', {
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
at: 0.95 + animationDelay,
|
||||
duration: 1.2,
|
||||
}],
|
||||
|
||||
// Description
|
||||
['.location-page__description', {
|
||||
y: ['10%', 0],
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
at: 0.9 + animationDelay,
|
||||
duration: 1.2,
|
||||
}]
|
||||
], {
|
||||
delay: DELAY.PAGE_LOADING / 1000,
|
||||
defaultOptions: {
|
||||
duration: 1.6,
|
||||
easing: quartOut,
|
||||
},
|
||||
})
|
||||
animation.stop()
|
||||
|
||||
// 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,
|
||||
}, 950 + ($navigating ? DURATION.PAGE_IN : 0))
|
||||
|
||||
// Description
|
||||
timeline.add({
|
||||
targets: '.location-page__description',
|
||||
translateY: ['10%', 0],
|
||||
opacity: [0, 1],
|
||||
}, 900 + ($navigating ? DURATION.PAGE_IN : 0))
|
||||
|
||||
requestAnimationFrame(timeline.play)
|
||||
// Run animation
|
||||
requestAnimationFrame(animation.play)
|
||||
|
||||
|
||||
// Destroy
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import anime, { type AnimeTimelineInstance } from 'animejs'
|
||||
import { stagger, timeline } from 'motion'
|
||||
import { DELAY } from '$utils/contants'
|
||||
import { quartOut } from 'svelte/easing'
|
||||
// Components
|
||||
import Metas from '$components/Metas.svelte'
|
||||
import PageTransition from '$components/PageTransition.svelte'
|
||||
@@ -19,39 +21,40 @@
|
||||
/**
|
||||
* Animations
|
||||
*/
|
||||
// Setup animations
|
||||
const timeline: AnimeTimelineInstance = anime.timeline({
|
||||
duration: 1600,
|
||||
easing: 'easeOutQuart',
|
||||
autoplay: false,
|
||||
const animation = timeline([
|
||||
// Heading
|
||||
['.heading .text', {
|
||||
y: [24, 0],
|
||||
opacity: [0, 1],
|
||||
}],
|
||||
|
||||
// Categories
|
||||
['.credits__category', {
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
at: 0,
|
||||
delay: stagger(0.35, { start: 0.5 }),
|
||||
}],
|
||||
|
||||
// Names
|
||||
['.credits__category > ul > li', {
|
||||
y: [24, 0],
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
at: 1.1,
|
||||
delay: stagger(0.35),
|
||||
}],
|
||||
], {
|
||||
delay: DELAY.PAGE_LOADING / 1000,
|
||||
defaultOptions: {
|
||||
duration: 1.6,
|
||||
easing: quartOut,
|
||||
},
|
||||
})
|
||||
animation.stop()
|
||||
|
||||
anime.set('.heading .text, .credits__category > ul > li', {
|
||||
opacity: 0,
|
||||
translateY: 24,
|
||||
})
|
||||
anime.set('.credits__category', {
|
||||
opacity: 0,
|
||||
})
|
||||
|
||||
// Elements
|
||||
timeline.add({
|
||||
targets: '.heading .text, .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)
|
||||
|
||||
// Transition in
|
||||
requestAnimationFrame(timeline.play)
|
||||
// Run animation
|
||||
requestAnimationFrame(animation.play)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import anime, { type AnimeTimelineInstance } from 'animejs'
|
||||
import { timeline, stagger } from 'motion'
|
||||
import { DELAY } from '$utils/contants'
|
||||
import { sleep, smoothScroll } from '$utils/functions'
|
||||
import { smoothScroll } from '$utils/functions'
|
||||
import { reveal, fade as animeFade } from '$animations/index'
|
||||
import { quartOut } from '$animations/easings'
|
||||
// Components
|
||||
import Metas from '$components/Metas.svelte'
|
||||
import PageTransition from '$components/PageTransition.svelte'
|
||||
@@ -30,36 +31,42 @@
|
||||
const { settings, locations }: any = getContext('global')
|
||||
|
||||
let scrollY: number, innerHeight: number
|
||||
let timeline: AnimeTimelineInstance
|
||||
|
||||
|
||||
onMount(() => {
|
||||
timeline = anime.timeline({
|
||||
duration: 1600,
|
||||
easing: 'easeOutQuart',
|
||||
autoplay: false,
|
||||
})
|
||||
/**
|
||||
* Animations
|
||||
*/
|
||||
const animation = timeline([
|
||||
// Reveal text
|
||||
['.homepage__headline', {
|
||||
y: [16, 0],
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
at: 0.75,
|
||||
}],
|
||||
|
||||
// Reveal text
|
||||
timeline.add({
|
||||
targets: '.homepage__headline',
|
||||
translateY: [16, 0],
|
||||
opacity: [0, 1],
|
||||
}, 750)
|
||||
|
||||
// Animate collage photos
|
||||
timeline.add({
|
||||
targets: '.collage .photo-card',
|
||||
translateY: ['33.33%', 0],
|
||||
rotate (item: HTMLElement) {
|
||||
return [-4, getComputedStyle(item).getPropertyValue('--rotation')]
|
||||
// Animate collage photos
|
||||
['.collage .photo-card', {
|
||||
y: ['33.33%', 0],
|
||||
rotate: [-4, 0],
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
at: 0,
|
||||
duration: 1.2,
|
||||
delay: stagger(0.075),
|
||||
}]
|
||||
], {
|
||||
delay: DELAY.PAGE_LOADING / 1000,
|
||||
defaultOptions: {
|
||||
duration: 1.6,
|
||||
easing: quartOut,
|
||||
},
|
||||
opacity: [0, 1],
|
||||
duration: 1200,
|
||||
delay: anime.stagger(75),
|
||||
}, 0)
|
||||
})
|
||||
animation.stop()
|
||||
|
||||
sleep(DELAY.PAGE_LOADING).then(timeline.play)
|
||||
// Run animation
|
||||
requestAnimationFrame(animation.play)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
import { goto } from '$app/navigation'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { fly } from 'svelte/transition'
|
||||
import { quartOut } from 'svelte/easing'
|
||||
import dayjs from 'dayjs'
|
||||
import { quartOut as quartOutSvelte } from 'svelte/easing'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
||||
import anime, { type AnimeTimelineInstance } from 'animejs'
|
||||
import { stagger, timeline } from 'motion'
|
||||
import { DELAY } from '$utils/contants'
|
||||
import { fetchAPI } from '$utils/api'
|
||||
import { quartOut } from '$animations/easings'
|
||||
import { map, lerp, throttle } from '$utils/functions'
|
||||
// Components
|
||||
import Metas from '$components/Metas.svelte'
|
||||
@@ -282,37 +284,30 @@
|
||||
const existingPhotos = photosGridEl.querySelectorAll('.photo')
|
||||
existingPhotos.forEach(el => observerPhotos.observe(el))
|
||||
|
||||
|
||||
/**
|
||||
* Animations
|
||||
*/
|
||||
// Transition in
|
||||
const timeline: AnimeTimelineInstance = anime.timeline({
|
||||
duration: 1600,
|
||||
easing: 'easeOutQuart',
|
||||
autoplay: false,
|
||||
const animation = timeline([
|
||||
// Reveal text
|
||||
['.photos-page__intro .discover, .photos-page__intro .filters__bar', {
|
||||
y: [16, 0],
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
at: 0.4,
|
||||
delay: stagger(0.25),
|
||||
}]
|
||||
], {
|
||||
delay: DELAY.PAGE_LOADING / 1000,
|
||||
defaultOptions: {
|
||||
duration: 1.6,
|
||||
easing: quartOut,
|
||||
},
|
||||
})
|
||||
animation.stop()
|
||||
|
||||
// Reveal text
|
||||
timeline.add({
|
||||
targets: '.photos-page__intro .discover',
|
||||
translateY: [16, 0],
|
||||
opacity: [0, 1],
|
||||
}, 900)
|
||||
|
||||
// Filters
|
||||
timeline.add({
|
||||
targets: '.photos-page__intro .filters',
|
||||
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)
|
||||
|
||||
// Play animation
|
||||
requestAnimationFrame(timeline.play)
|
||||
// Run animation
|
||||
requestAnimationFrame(animation.play)
|
||||
|
||||
|
||||
// Destroy
|
||||
@@ -351,9 +346,8 @@
|
||||
class:is-transitioning={filtersTransitioning}
|
||||
class:is-visible={filtersVisible}
|
||||
>
|
||||
<span class="text-label filters__label">Filter photos</span>
|
||||
|
||||
<div class="filters__bar">
|
||||
<span class="text-label filters__label">Filter photos</span>
|
||||
<ul>
|
||||
<li>
|
||||
<Select
|
||||
@@ -418,7 +412,7 @@
|
||||
{#if filtered}
|
||||
<button class="reset button-link"
|
||||
on:click={resetFiltered}
|
||||
transition:fly={{ y: 4, duration: 600, easing: quartOut }}
|
||||
transition:fly={{ y: 4, duration: 600, easing: quartOutSvelte }}
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import { stagger, timeline } from 'motion'
|
||||
import dayjs from 'dayjs'
|
||||
import anime, { type AnimeTimelineInstance } from 'animejs'
|
||||
import { DELAY } from '$utils/contants'
|
||||
import { quartOut } from '$animations/easings'
|
||||
// Components
|
||||
import Metas from '$components/Metas.svelte'
|
||||
import PageTransition from '$components/PageTransition.svelte'
|
||||
@@ -22,37 +24,36 @@
|
||||
/**
|
||||
* Animations
|
||||
*/
|
||||
// Setup animations
|
||||
const timeline: AnimeTimelineInstance = anime.timeline({
|
||||
duration: 1600,
|
||||
easing: 'easeOutQuart',
|
||||
autoplay: false,
|
||||
const animation = timeline([
|
||||
// Elements
|
||||
['.heading .text, .subscribe__top .newsletter-form, .subscribe__issues', {
|
||||
y: [24, 0],
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
at: 0.5,
|
||||
delay: stagger(0.35),
|
||||
}],
|
||||
|
||||
// Reveal each issue
|
||||
['.subscribe__issues > ul > li', {
|
||||
y: [16, 0],
|
||||
opacity: [0, 1],
|
||||
}, {
|
||||
duration: 1,
|
||||
at: 1.5,
|
||||
delay: stagger(0.15),
|
||||
}],
|
||||
], {
|
||||
delay: DELAY.PAGE_LOADING / 1000,
|
||||
defaultOptions: {
|
||||
duration: 1.6,
|
||||
easing: quartOut,
|
||||
},
|
||||
})
|
||||
animation.stop()
|
||||
|
||||
anime.set('.heading .text, .subscribe__top .newsletter-form, .subscribe__issues', {
|
||||
opacity: 0,
|
||||
translateY: 24,
|
||||
})
|
||||
|
||||
// Elements
|
||||
timeline.add({
|
||||
targets: '.heading .text, .subscribe__top .newsletter-form, .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)
|
||||
|
||||
// Transition in
|
||||
requestAnimationFrame(timeline.play)
|
||||
// Run animation
|
||||
requestAnimationFrame(animation.play)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -75,22 +76,24 @@
|
||||
<h2 class="title-small">Past Issues</h2>
|
||||
<ul>
|
||||
{#each issues as { issue, title, date_sent, link, thumbnail: { id } }}
|
||||
<li class="issue">
|
||||
<a href={link} target="_blank" rel="external noreferrer noopener" tabindex="0">
|
||||
<Image
|
||||
id={id}
|
||||
sizeKey="issue-thumbnail-small"
|
||||
width={160} height={112}
|
||||
alt="Issue {issue} thumbnail"
|
||||
/>
|
||||
<dl>
|
||||
<dt>Issue #{issue}</dt>
|
||||
<dd>
|
||||
<p>{title}</p>
|
||||
<time>{dayjs(date_sent).format('DD/MM/YYYY')}</time>
|
||||
</dd>
|
||||
</dl>
|
||||
</a>
|
||||
<li>
|
||||
<div class="issue">
|
||||
<a href={link} target="_blank" rel="external noreferrer noopener" tabindex="0">
|
||||
<Image
|
||||
id={id}
|
||||
sizeKey="issue-thumbnail-small"
|
||||
width={160} height={112}
|
||||
alt="Issue {issue} thumbnail"
|
||||
/>
|
||||
<dl>
|
||||
<dt>Issue #{issue}</dt>
|
||||
<dd>
|
||||
<p>{title}</p>
|
||||
<time>{dayjs(date_sent).format('DD/MM/YYYY')}</time>
|
||||
</dd>
|
||||
</dl>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
.photo-card {
|
||||
& > * {
|
||||
border-radius: 8px;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
cursor: zoom-in;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 16px 12px rgba(#000, 0.15), 0 26px 52px rgba(#000, 0.2);
|
||||
transition: transform 0.7s var(--ease-quart);
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
// Image
|
||||
:global(picture) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: $color-primary-tertiary20;
|
||||
}
|
||||
:global(img) {
|
||||
display: block;
|
||||
width: calc(100% + 2px);
|
||||
height: calc(100% + 2px);
|
||||
object-fit: cover;
|
||||
transition: opacity 0.7s var(--ease-quart);
|
||||
|
||||
:global(img) {
|
||||
display: block;
|
||||
width: calc(100% + 2px);
|
||||
height: calc(100% + 2px);
|
||||
object-fit: cover;
|
||||
transition: opacity 0.7s var(--ease-quart);
|
||||
}
|
||||
}
|
||||
|
||||
// Informations
|
||||
@@ -92,7 +94,8 @@
|
||||
transition: opacity 0.8s var(--ease-quart);
|
||||
}
|
||||
|
||||
// Slightly zoom in and show info on hover
|
||||
|
||||
// Slightly zoom in, show gradient and info on hover
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
transform: scale(1.0375) rotate(2deg) translateZ(0);
|
||||
@@ -109,6 +112,7 @@
|
||||
transition-delay: 180ms;
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -20,12 +20,15 @@
|
||||
height: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
transform: rotate(var(--rotation)) translateZ(0);
|
||||
|
||||
@include bp (sm) {
|
||||
height: clamp(156px, 18vw, 400px);
|
||||
}
|
||||
|
||||
& > :global(*) {
|
||||
transform: rotate(var(--rotation)) translateZ(0);
|
||||
}
|
||||
|
||||
// First row
|
||||
// Mobile: Top left
|
||||
&:nth-child(1) {
|
||||
|
||||
@@ -254,7 +254,6 @@
|
||||
** Filters
|
||||
*/
|
||||
.filters {
|
||||
position: relative;
|
||||
max-width: 982px;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
@@ -265,6 +264,7 @@
|
||||
|
||||
// Bar
|
||||
&__bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -364,7 +364,7 @@
|
||||
|
||||
@include bp (sm) {
|
||||
position: absolute;
|
||||
left: 64px;
|
||||
left: 32px;
|
||||
top: 52%;
|
||||
transform: translateY(-50%);
|
||||
margin-bottom: 0;
|
||||
|
||||
Reference in New Issue
Block a user