✨ Finish to replace Anime with Motion One for page animations
Page intro animation and reveal that has now been simplified as Motion One manages an inView option (that uses IntersectionObserver)
This commit is contained in:
@@ -16,7 +16,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@studio-freight/lenis": "^0.1.13",
|
"@studio-freight/lenis": "^0.1.13",
|
||||||
"animejs": "^3.2.1",
|
|
||||||
"dayjs": "^1.11.5",
|
"dayjs": "^1.11.5",
|
||||||
"embla-carousel": "^7.0.0",
|
"embla-carousel": "^7.0.0",
|
||||||
"focus-visible": "^5.2.0",
|
"focus-visible": "^5.2.0",
|
||||||
@@ -30,7 +29,6 @@
|
|||||||
"@sveltejs/adapter-node": "^1.0.0-next.86",
|
"@sveltejs/adapter-node": "^1.0.0-next.86",
|
||||||
"@sveltejs/adapter-vercel": "^1.0.0-next.66",
|
"@sveltejs/adapter-vercel": "^1.0.0-next.66",
|
||||||
"@sveltejs/kit": "^1.0.0-next.405",
|
"@sveltejs/kit": "^1.0.0-next.405",
|
||||||
"@types/animejs": "^3.1.5",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
||||||
"@typescript-eslint/parser": "^5.33.0",
|
"@typescript-eslint/parser": "^5.33.0",
|
||||||
"browserslist": "^4.21.3",
|
"browserslist": "^4.21.3",
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -6,10 +6,8 @@ specifiers:
|
|||||||
'@sveltejs/adapter-node': ^1.0.0-next.86
|
'@sveltejs/adapter-node': ^1.0.0-next.86
|
||||||
'@sveltejs/adapter-vercel': ^1.0.0-next.66
|
'@sveltejs/adapter-vercel': ^1.0.0-next.66
|
||||||
'@sveltejs/kit': ^1.0.0-next.405
|
'@sveltejs/kit': ^1.0.0-next.405
|
||||||
'@types/animejs': ^3.1.5
|
|
||||||
'@typescript-eslint/eslint-plugin': ^5.33.0
|
'@typescript-eslint/eslint-plugin': ^5.33.0
|
||||||
'@typescript-eslint/parser': ^5.33.0
|
'@typescript-eslint/parser': ^5.33.0
|
||||||
animejs: ^3.2.1
|
|
||||||
browserslist: ^4.21.3
|
browserslist: ^4.21.3
|
||||||
cssnano: ^5.1.13
|
cssnano: ^5.1.13
|
||||||
dayjs: ^1.11.5
|
dayjs: ^1.11.5
|
||||||
@@ -37,7 +35,6 @@ specifiers:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@studio-freight/lenis': 0.1.13
|
'@studio-freight/lenis': 0.1.13
|
||||||
animejs: 3.2.1
|
|
||||||
dayjs: 1.11.5
|
dayjs: 1.11.5
|
||||||
embla-carousel: 7.0.0
|
embla-carousel: 7.0.0
|
||||||
focus-visible: 5.2.0
|
focus-visible: 5.2.0
|
||||||
@@ -51,7 +48,6 @@ devDependencies:
|
|||||||
'@sveltejs/adapter-node': 1.0.0-next.86
|
'@sveltejs/adapter-node': 1.0.0-next.86
|
||||||
'@sveltejs/adapter-vercel': 1.0.0-next.66
|
'@sveltejs/adapter-vercel': 1.0.0-next.66
|
||||||
'@sveltejs/kit': 1.0.0-next.405_svelte@3.49.0+vite@3.0.7
|
'@sveltejs/kit': 1.0.0-next.405_svelte@3.49.0+vite@3.0.7
|
||||||
'@types/animejs': 3.1.5
|
|
||||||
'@typescript-eslint/eslint-plugin': 5.33.0_njno5y7ry2l2lcmiu4tywxkwnq
|
'@typescript-eslint/eslint-plugin': 5.33.0_njno5y7ry2l2lcmiu4tywxkwnq
|
||||||
'@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq
|
'@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq
|
||||||
browserslist: 4.21.3
|
browserslist: 4.21.3
|
||||||
@@ -489,10 +485,6 @@ packages:
|
|||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/animejs/3.1.5:
|
|
||||||
resolution: {integrity: sha512-4i3i1YuNaNEPoHBJY78uzYu8qKIwyx96G04tnVtNhRMQC9I1Xhg6fY9GeWmZAzudaesKKrPkQgTCthT1zSGYyg==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/json-schema/7.0.11:
|
/@types/json-schema/7.0.11:
|
||||||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -693,10 +685,6 @@ packages:
|
|||||||
uri-js: 4.4.1
|
uri-js: 4.4.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/animejs/3.2.1:
|
|
||||||
resolution: {integrity: sha512-sWno3ugFryK5nhiDm/2BKeFCpZv7vzerWUcUPyAZLDhMek3+S/p418ldZJbJXo5ZUOpfm2kP2XRO4NJcULMy9A==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ansi-regex/5.0.1:
|
/ansi-regex/5.0.1:
|
||||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
interface AnimationsQueueItem {
|
|
||||||
node: Node
|
|
||||||
animation: Function
|
|
||||||
delay: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RevealQueue {
|
|
||||||
items: AnimationsQueueItem[] = []
|
|
||||||
queuedItems: AnimationsQueueItem[] = []
|
|
||||||
timer = null
|
|
||||||
observer = null
|
|
||||||
|
|
||||||
constructor () {
|
|
||||||
if (typeof IntersectionObserver === 'undefined') return
|
|
||||||
|
|
||||||
this.observer = new IntersectionObserver(entries => {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
this.observer.unobserve(entry.target)
|
|
||||||
const item = this.findItemFromNode(entry.target)
|
|
||||||
this.queuedItems.push(item)
|
|
||||||
|
|
||||||
if (this.timer === null) {
|
|
||||||
this.run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an animation in queue
|
|
||||||
add (node: Node, animation: Function, delay: number) {
|
|
||||||
this.items.push({
|
|
||||||
node,
|
|
||||||
animation,
|
|
||||||
delay,
|
|
||||||
})
|
|
||||||
this.observer.observe(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove node from queue and unobserve from IO
|
|
||||||
remove (node: Node) {
|
|
||||||
this.observer.unobserve(node)
|
|
||||||
this.items = this.items.filter(v => v.node !== node)
|
|
||||||
this.queuedItems = this.queuedItems.filter(v => v.node !== node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run animation
|
|
||||||
run () {
|
|
||||||
if (this.queuedItems.length === 0) {
|
|
||||||
this.timer = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const item = this.queuedItems[0]
|
|
||||||
item.animation()
|
|
||||||
this.remove(item.node)
|
|
||||||
this.timer = window.setTimeout(this.run.bind(this), item.delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find item from node
|
|
||||||
findItemFromNode (node: Node) {
|
|
||||||
return this.items.find(i => i.node === node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,7 @@ export const [send, receive] = crossfade({
|
|||||||
const style = getComputedStyle(node)
|
const style = getComputedStyle(node)
|
||||||
const transform = style.transform === 'none' ? '' : style.transform
|
const transform = style.transform === 'none' ? '' : style.transform
|
||||||
const sd = 1 - start
|
const sd = 1 - start
|
||||||
|
|
||||||
return {
|
return {
|
||||||
duration,
|
duration,
|
||||||
easing,
|
easing,
|
||||||
|
|||||||
@@ -1,326 +0,0 @@
|
|||||||
import anime, { type AnimeParams } from 'animejs'
|
|
||||||
import type { TransitionConfig } from 'svelte/transition'
|
|
||||||
|
|
||||||
|
|
||||||
// Options interface
|
|
||||||
interface TransitionOptions {
|
|
||||||
direct?: boolean
|
|
||||||
children?: string
|
|
||||||
targets?: Element
|
|
||||||
from?: number | string
|
|
||||||
to?: number | string
|
|
||||||
opacity?: boolean
|
|
||||||
rotate?: any
|
|
||||||
rotateX?: number
|
|
||||||
rotateRandom?: boolean
|
|
||||||
duration?: number
|
|
||||||
stagger?: number
|
|
||||||
scale?: number[]
|
|
||||||
delay?: number
|
|
||||||
easing?: string
|
|
||||||
clear?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Effect: Fly
|
|
||||||
* @returns Anime.js animation
|
|
||||||
*/
|
|
||||||
export const fly = (
|
|
||||||
node: Element,
|
|
||||||
{
|
|
||||||
direct = false,
|
|
||||||
targets = node,
|
|
||||||
children,
|
|
||||||
from = 16,
|
|
||||||
to = 0,
|
|
||||||
opacity = true,
|
|
||||||
rotate = false,
|
|
||||||
rotateX = 0,
|
|
||||||
rotateRandom = false,
|
|
||||||
duration = 1600,
|
|
||||||
stagger,
|
|
||||||
scale = null,
|
|
||||||
delay = 0,
|
|
||||||
easing = 'easeOutQuart',
|
|
||||||
clear = false
|
|
||||||
}: TransitionOptions
|
|
||||||
): TransitionConfig => {
|
|
||||||
const anim = anime({
|
|
||||||
autoplay: !direct,
|
|
||||||
targets: children ? node.querySelectorAll(children) : targets,
|
|
||||||
...(opacity && { opacity: [0, 1] }),
|
|
||||||
...(scale && { scale }),
|
|
||||||
...(rotate && {
|
|
||||||
rotate:
|
|
||||||
// If Array, use it, otherwise use the value up to 0
|
|
||||||
Array.isArray(rotate)
|
|
||||||
? rotate
|
|
||||||
: [rotateRandom ? anime.random(-rotate, rotate) : rotate, 0]
|
|
||||||
}),
|
|
||||||
...(rotateX && { rotateX: [rotateX, 0] }),
|
|
||||||
translateY: [from, to],
|
|
||||||
translateZ: 0,
|
|
||||||
duration,
|
|
||||||
easing,
|
|
||||||
delay: stagger ? anime.stagger(stagger, { start: delay }) : delay,
|
|
||||||
complete: ({ animatables }) => {
|
|
||||||
// Remove styles on end
|
|
||||||
if (clear) {
|
|
||||||
animatables.forEach((el: AnimeParams) => {
|
|
||||||
el.target.style.transform = ''
|
|
||||||
opacity && (el.target.style.opacity = '')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return direct ? anim : {
|
|
||||||
tick: (t: number, u: number) => anim
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Effect: Fade
|
|
||||||
* @returns Anime.js animation
|
|
||||||
*/
|
|
||||||
export const fade = (
|
|
||||||
node: Element,
|
|
||||||
{
|
|
||||||
direct = false,
|
|
||||||
targets = node,
|
|
||||||
children,
|
|
||||||
from = 0,
|
|
||||||
to = 1,
|
|
||||||
duration = 1600,
|
|
||||||
stagger,
|
|
||||||
delay = 0,
|
|
||||||
easing = 'easeInOutQuart',
|
|
||||||
clear = false
|
|
||||||
}: TransitionOptions
|
|
||||||
): TransitionConfig => {
|
|
||||||
|
|
||||||
const anim = anime({
|
|
||||||
autoplay: !direct,
|
|
||||||
targets: children ? node.querySelectorAll(children) : targets,
|
|
||||||
opacity: [from, to],
|
|
||||||
duration,
|
|
||||||
easing,
|
|
||||||
delay: stagger ? anime.stagger(stagger, { start: delay }) : delay,
|
|
||||||
complete: ({ animatables }) => {
|
|
||||||
// Remove styles on end
|
|
||||||
if (clear) {
|
|
||||||
animatables.forEach((el: AnimeParams) => {
|
|
||||||
el.target.style.opacity = ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return direct ? anim : {
|
|
||||||
tick: (t: number, u: number) => anim
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Effect: Scale
|
|
||||||
* @returns Anime.js animation
|
|
||||||
*/
|
|
||||||
export const scale = (
|
|
||||||
node: Element,
|
|
||||||
{
|
|
||||||
direct = false,
|
|
||||||
from = 0,
|
|
||||||
to = 1,
|
|
||||||
duration = 1200,
|
|
||||||
delay = 0,
|
|
||||||
easing = 'easeOutQuart',
|
|
||||||
clear = false
|
|
||||||
}: TransitionOptions
|
|
||||||
): TransitionConfig => {
|
|
||||||
const anim = anime({
|
|
||||||
autoplay: !direct,
|
|
||||||
targets: node,
|
|
||||||
scaleY: [from, to],
|
|
||||||
translateZ: 0,
|
|
||||||
duration,
|
|
||||||
easing,
|
|
||||||
delay,
|
|
||||||
complete: ({ animatables }) => {
|
|
||||||
// Remove styles on end
|
|
||||||
if (clear) {
|
|
||||||
animatables.forEach((el: AnimeParams) => {
|
|
||||||
el.target.style.transform = ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return direct ? anim : {
|
|
||||||
tick: (t: number, u: number) => anim
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Effect: Words reveal
|
|
||||||
* @description Anime.js animation
|
|
||||||
*/
|
|
||||||
export const words = (
|
|
||||||
node: Element,
|
|
||||||
{
|
|
||||||
direct = false,
|
|
||||||
children = 'span',
|
|
||||||
from = '45%',
|
|
||||||
to = 0,
|
|
||||||
duration = 1200,
|
|
||||||
stagger = 60,
|
|
||||||
rotate = 0,
|
|
||||||
delay = 0,
|
|
||||||
opacity = true,
|
|
||||||
easing = 'easeOutQuart',
|
|
||||||
clear = false
|
|
||||||
}: TransitionOptions
|
|
||||||
): TransitionConfig => {
|
|
||||||
const anim = anime({
|
|
||||||
autoplay: !direct,
|
|
||||||
targets: node.querySelectorAll(children),
|
|
||||||
...(opacity && { opacity: [0, 1] }),
|
|
||||||
translateY: [from, to],
|
|
||||||
...(rotate && { rotateX: [rotate, 0] }),
|
|
||||||
translateZ: 0,
|
|
||||||
duration,
|
|
||||||
easing,
|
|
||||||
delay: stagger ? anime.stagger(stagger, { start: delay }) : delay,
|
|
||||||
complete: ({ animatables }) => {
|
|
||||||
// Remove styles on end
|
|
||||||
if (clear) {
|
|
||||||
animatables.forEach((el: AnimeParams) => {
|
|
||||||
el.target.style.transform = ''
|
|
||||||
opacity && (el.target.style.opacity = '')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return direct ? anim : {
|
|
||||||
tick: (t: number, u: number) => anim
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run animation on reveal
|
|
||||||
* @description IntersectionObserver triggering an animation function or a callback
|
|
||||||
*/
|
|
||||||
export const reveal = (
|
|
||||||
node: Element | any,
|
|
||||||
{
|
|
||||||
enable = true,
|
|
||||||
targets = node,
|
|
||||||
animation,
|
|
||||||
options = {},
|
|
||||||
callback,
|
|
||||||
callbackTrigger,
|
|
||||||
once = true,
|
|
||||||
threshold = 0.2,
|
|
||||||
rootMargin = '0px 0px 0px',
|
|
||||||
queue = null,
|
|
||||||
queueDelay = 0,
|
|
||||||
}: revealOptions
|
|
||||||
) => {
|
|
||||||
let observer: IntersectionObserver
|
|
||||||
|
|
||||||
// Kill if IntersectionObserver is not supported
|
|
||||||
if (typeof IntersectionObserver === 'undefined' || !enable) return
|
|
||||||
|
|
||||||
// Use animation with provided node selector
|
|
||||||
if (animation) {
|
|
||||||
const anim = animation(node, {
|
|
||||||
...options,
|
|
||||||
direct: true,
|
|
||||||
autoplay: false
|
|
||||||
})
|
|
||||||
|
|
||||||
// If a queue exists, let it run animations
|
|
||||||
if (queue) {
|
|
||||||
queue.add(node, anim.play, queueDelay)
|
|
||||||
|
|
||||||
return {
|
|
||||||
destroy () {
|
|
||||||
queue.remove(node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
observer = new IntersectionObserver(entries => {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
anim && anim.play()
|
|
||||||
once && observer.unobserve(entry.target)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, { threshold, rootMargin })
|
|
||||||
|
|
||||||
observer.observe(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom callback
|
|
||||||
else if (callback) {
|
|
||||||
observer = new IntersectionObserver(entries => {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
const cb = callback(entry)
|
|
||||||
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
// Run callback
|
|
||||||
callbackTrigger && callbackTrigger(cb)
|
|
||||||
|
|
||||||
// Run IntersectionObserver only once
|
|
||||||
once && observer.unobserve(entry.target)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, { threshold })
|
|
||||||
|
|
||||||
const elements = typeof targets === 'string' ? node.querySelectorAll(targets) : targets
|
|
||||||
|
|
||||||
if (elements) {
|
|
||||||
// Observe each element
|
|
||||||
if (elements.length > 0) {
|
|
||||||
elements.forEach((target: Element) => {
|
|
||||||
observer.observe(target)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Directly observe
|
|
||||||
else {
|
|
||||||
observer.observe(elements)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
return {
|
|
||||||
// Destroy
|
|
||||||
destroy () {
|
|
||||||
observer && observer.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface revealOptions {
|
|
||||||
animation?: Function
|
|
||||||
options?: TransitionOptions
|
|
||||||
queue?: any
|
|
||||||
callback?: Function
|
|
||||||
callbackTrigger?: any
|
|
||||||
targets?: string | Element
|
|
||||||
enable?: boolean
|
|
||||||
once?: boolean
|
|
||||||
threshold?: number,
|
|
||||||
rootMargin?: string,
|
|
||||||
queueDelay?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export { RevealQueue } from './RevealQueue'
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { map } from '$utils/functions'
|
import { map } from '$utils/functions'
|
||||||
import { reveal, fly } from '$animations/index'
|
import reveal from '$animations/reveal'
|
||||||
|
|
||||||
export let tag: string
|
export let tag: string
|
||||||
export let label: string = undefined
|
export let label: string = undefined
|
||||||
@@ -46,17 +46,15 @@
|
|||||||
|
|
||||||
|
|
||||||
const revealOptions = animate ? {
|
const revealOptions = animate ? {
|
||||||
animation: fly,
|
|
||||||
options: {
|
|
||||||
children: '.char',
|
children: '.char',
|
||||||
stagger: 60,
|
animation: { y: ['-105%', 0] },
|
||||||
duration: 1600,
|
options: {
|
||||||
from: '-105%',
|
stagger: 0.06,
|
||||||
opacity: false,
|
duration: 1.6,
|
||||||
delay: 200,
|
delay: 0.2,
|
||||||
},
|
|
||||||
threshold: 0.2,
|
threshold: 0.2,
|
||||||
} : {}
|
},
|
||||||
|
} : null
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window
|
<svelte:window
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SplitText from '$components/SplitText.svelte'
|
import SplitText from '$components/SplitText.svelte'
|
||||||
import { reveal, fly } from '$animations/index'
|
import reveal from '$animations/reveal'
|
||||||
import { DURATION } from '$utils/contants'
|
import { DURATION } from '$utils/contants'
|
||||||
|
|
||||||
export let variant: string = 'lines'
|
export let variant: string = 'lines'
|
||||||
@@ -14,16 +14,14 @@
|
|||||||
{#if tag === 'h1'}
|
{#if tag === 'h1'}
|
||||||
<h1 class="site-title site-title--{variant}"
|
<h1 class="site-title site-title--{variant}"
|
||||||
use:reveal={{
|
use:reveal={{
|
||||||
animation: fly,
|
|
||||||
options: {
|
|
||||||
children: '.char',
|
children: '.char',
|
||||||
stagger: 40,
|
animation: { y: ['105%', 0] },
|
||||||
duration: 1000,
|
options: {
|
||||||
from: '110%',
|
stagger: 0.04,
|
||||||
opacity: false,
|
duration: 1,
|
||||||
delay: DURATION.PAGE_IN
|
delay: DURATION.PAGE_IN / 1000,
|
||||||
},
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SplitText text="Houses" mode="chars" class="pink mask" />
|
<SplitText text="Houses" mode="chars" class="pink mask" />
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores'
|
import { page } from '$app/stores'
|
||||||
import { getContext } from 'svelte'
|
import { getContext } from 'svelte'
|
||||||
import { fly, reveal } from '$animations/index'
|
import reveal from '$animations/reveal'
|
||||||
// Components
|
// Components
|
||||||
import Icon from '$components/atoms/Icon.svelte'
|
import Icon from '$components/atoms/Icon.svelte'
|
||||||
|
|
||||||
@@ -42,14 +42,12 @@
|
|||||||
class:is-open={isOpen}
|
class:is-open={isOpen}
|
||||||
class:is-over={isOver}
|
class:is-over={isOver}
|
||||||
use:reveal={{
|
use:reveal={{
|
||||||
animation: fly,
|
animation: { y: [24, 0], opacity: [0, 1] },
|
||||||
options: {
|
options: {
|
||||||
from: 24,
|
duration: 1,
|
||||||
to: 0,
|
delay: 0.6,
|
||||||
duration: 1000,
|
threshold: 0,
|
||||||
easing: 'easeOutQuart',
|
},
|
||||||
delay: 600,
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button class="switcher__button" title="{!isOpen ? 'Open' : 'Close'} menu" tabindex="0"
|
<button class="switcher__button" title="{!isOpen ? 'Open' : 'Close'} menu" tabindex="0"
|
||||||
|
|||||||
@@ -3,10 +3,13 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { navigating } from '$app/stores'
|
||||||
import { getContext, onMount } from 'svelte'
|
import { getContext, onMount } from 'svelte'
|
||||||
import anime, { type AnimeTimelineInstance } from 'animejs'
|
import { stagger, timeline } from 'motion'
|
||||||
import { cartOpen } from '$utils/stores/shop'
|
import { cartOpen } from '$utils/stores/shop'
|
||||||
import { smoothScroll } from '$utils/functions'
|
import { smoothScroll } from '$utils/functions'
|
||||||
|
import { DELAY } from '$utils/contants'
|
||||||
|
import { quartOut } from '$animations/easings'
|
||||||
// Components
|
// Components
|
||||||
import Image from '$components/atoms/Image.svelte'
|
import Image from '$components/atoms/Image.svelte'
|
||||||
import ButtonCart from '$components/atoms/ButtonCart.svelte'
|
import ButtonCart from '$components/atoms/ButtonCart.svelte'
|
||||||
@@ -37,46 +40,52 @@
|
|||||||
/**
|
/**
|
||||||
* Animations
|
* Animations
|
||||||
*/
|
*/
|
||||||
// Setup animations
|
const animation = timeline([
|
||||||
const timeline: AnimeTimelineInstance = anime.timeline({
|
|
||||||
duration: 1600,
|
|
||||||
easing: 'easeOutQuart',
|
|
||||||
autoplay: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Hero image
|
// Hero image
|
||||||
timeline.add({
|
['.shop-page__background', {
|
||||||
targets: '.shop-page__background',
|
|
||||||
scale: [1.06, 1],
|
scale: [1.06, 1],
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
duration: 2400,
|
}, {
|
||||||
}, 400)
|
at: 0.4,
|
||||||
|
duration: 2.4,
|
||||||
// Hero title
|
}],
|
||||||
timeline.add({
|
|
||||||
targets: '.shop-page__title h1',
|
|
||||||
translateY: [32, 0],
|
|
||||||
opacity: [0, 1],
|
|
||||||
}, 500)
|
|
||||||
|
|
||||||
// Intro top elements
|
// Intro top elements
|
||||||
timeline.add({
|
['.shop-page__intro .top > *', {
|
||||||
targets: '.shop-page__intro .top > *',
|
y: [-100, 0],
|
||||||
translateY: [-100, 0],
|
opacity: [0, 1],
|
||||||
delay: anime.stagger(250),
|
}, {
|
||||||
}, 400)
|
at: 0.4,
|
||||||
|
delay: stagger(0.25),
|
||||||
|
}],
|
||||||
|
|
||||||
|
// Hero title
|
||||||
|
['.shop-page__title h1', {
|
||||||
|
y: [32, 0],
|
||||||
|
opacity: [0, 1],
|
||||||
|
}, {
|
||||||
|
at: 0.5,
|
||||||
|
}],
|
||||||
|
|
||||||
// Intro navbar
|
// Intro navbar
|
||||||
timeline.add({
|
['.shop-page__nav .container > *, .shop-page__intro .button-cart', {
|
||||||
targets: '.shop-page__nav .container > *, .shop-page__intro .button-cart',
|
y: [100, 0],
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
translateY: [100, 0],
|
}, {
|
||||||
translateZ: 0,
|
at: 0.7,
|
||||||
delay: anime.stagger(250),
|
delay: stagger(0.25),
|
||||||
}, 700)
|
}]
|
||||||
|
], {
|
||||||
|
delay: $navigating ? DELAY.PAGE_LOADING / 1000 : 0,
|
||||||
|
defaultOptions: {
|
||||||
|
duration: 1.6,
|
||||||
|
easing: quartOut,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
animation.stop()
|
||||||
|
|
||||||
// Transition in
|
// Run animation
|
||||||
requestAnimationFrame(timeline.play)
|
requestAnimationFrame(animation.play)
|
||||||
|
|
||||||
|
|
||||||
// Destroy
|
// Destroy
|
||||||
|
|||||||
@@ -4,17 +4,18 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser } from '$app/env'
|
import { browser } from '$app/env'
|
||||||
import { page } from '$app/stores'
|
import { page, navigating } from '$app/stores'
|
||||||
import { goto } from '$app/navigation'
|
import { goto } from '$app/navigation'
|
||||||
import { onMount, tick } from 'svelte'
|
import { onMount, tick } from 'svelte'
|
||||||
import { fade, scale } from 'svelte/transition'
|
import { fade, scale } from 'svelte/transition'
|
||||||
import { quartOut } from 'svelte/easing'
|
import { quartOut } from 'svelte/easing'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import { stagger, timeline } from 'motion'
|
||||||
import { fetchAPI, getAssetUrlKey } from '$utils/api'
|
import { fetchAPI, getAssetUrlKey } from '$utils/api'
|
||||||
import { previousPage } from '$utils/stores'
|
import { previousPage } from '$utils/stores'
|
||||||
|
import { DELAY } from '$utils/contants'
|
||||||
import { throttle } from '$utils/functions'
|
import { throttle } from '$utils/functions'
|
||||||
import { swipe } from '$utils/interactions/swipe'
|
import { swipe } from '$utils/interactions/swipe'
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import anime, { type AnimeTimelineInstance } from 'animejs'
|
|
||||||
// Components
|
// Components
|
||||||
import Metas from '$components/Metas.svelte'
|
import Metas from '$components/Metas.svelte'
|
||||||
import SplitText from '$components/SplitText.svelte'
|
import SplitText from '$components/SplitText.svelte'
|
||||||
@@ -219,76 +220,74 @@
|
|||||||
/**
|
/**
|
||||||
* Animations
|
* Animations
|
||||||
*/
|
*/
|
||||||
// Setup animations
|
const animation = timeline([
|
||||||
const timeline: AnimeTimelineInstance = anime.timeline({
|
// First photo
|
||||||
duration: 1600,
|
['.photo-page__picture.is-1', {
|
||||||
easing: 'easeOutQuart',
|
y: [24, 0],
|
||||||
autoplay: false,
|
opacity: [0, 1],
|
||||||
})
|
}, {
|
||||||
|
duration: 0.9,
|
||||||
anime.set('.photo-page__picture', {
|
}],
|
||||||
opacity: 0,
|
// Other photos
|
||||||
})
|
['.photo-page__picture:not(.is-1)', {
|
||||||
anime.set('.photo-page__picture.is-1', {
|
x: ['-150%', 0],
|
||||||
translateY: 24,
|
opacity: [0, 1],
|
||||||
})
|
}, {
|
||||||
|
at: 0.4,
|
||||||
// Photos
|
delay: stagger(0.1),
|
||||||
timeline.add({
|
opacity: { duration: 0.25 },
|
||||||
targets: '.photo-page__picture.is-1',
|
}],
|
||||||
opacity: 1,
|
|
||||||
translateY: 0,
|
|
||||||
duration: 900,
|
|
||||||
}, 250)
|
|
||||||
timeline.add({
|
|
||||||
targets: '.photo-page__picture:not(.is-1)',
|
|
||||||
opacity: 1,
|
|
||||||
translateX (element: HTMLElement) {
|
|
||||||
const x = getComputedStyle(element).getPropertyValue('--offset-x').trim()
|
|
||||||
return [`-${x}`, 0]
|
|
||||||
},
|
|
||||||
delay: anime.stagger(55)
|
|
||||||
}, 350)
|
|
||||||
|
|
||||||
// Prev/Next buttons
|
// Prev/Next buttons
|
||||||
timeline.add({
|
['.photo-page__controls .prev', {
|
||||||
targets: '.photo-page__controls button',
|
x: [-16, 0],
|
||||||
translateX (item: HTMLElement) {
|
|
||||||
let direction = item.classList.contains('prev') ? -1 : 1
|
|
||||||
return [16 * direction, 0]
|
|
||||||
},
|
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
}, 450)
|
}, {
|
||||||
|
at: 0.45,
|
||||||
|
}],
|
||||||
|
['.photo-page__controls .next', {
|
||||||
|
x: [16, 0],
|
||||||
|
opacity: [0, 1],
|
||||||
|
}, {
|
||||||
|
at: 0.45,
|
||||||
|
}],
|
||||||
|
|
||||||
// Infos
|
// Infos
|
||||||
timeline.add({
|
['.photo-page__info > *', {
|
||||||
targets: '.photo-page__info > *',
|
y: [24, 0],
|
||||||
translateY: [24, 0],
|
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
delay: anime.stagger(200)
|
}, {
|
||||||
}, 400)
|
at: 0.4,
|
||||||
|
delay: stagger(0.3)
|
||||||
|
}],
|
||||||
|
|
||||||
|
|
||||||
anime.set('.photo-page__index', {
|
|
||||||
opacity: 0
|
|
||||||
})
|
|
||||||
// Index
|
// Index
|
||||||
timeline.add({
|
['.photo-page__index', {
|
||||||
targets: '.photo-page__index',
|
opacity: [0, 1],
|
||||||
opacity: 1,
|
}, {
|
||||||
duration: 900,
|
at: 0.6,
|
||||||
}, 600)
|
delay: stagger(0.2),
|
||||||
|
duration: 0.9,
|
||||||
|
}],
|
||||||
// Fly each number
|
// Fly each number
|
||||||
timeline.add({
|
['.photo-page__index .char', {
|
||||||
targets: '.photo-page__index .char',
|
y: ['300%', 0],
|
||||||
translateY: ['100%', 0],
|
}, {
|
||||||
delay: anime.stagger(200),
|
at: 1.1,
|
||||||
duration: 1000,
|
delay: stagger(0.2),
|
||||||
}, 1100)
|
duration: 1,
|
||||||
|
}],
|
||||||
|
], {
|
||||||
|
delay: $navigating ? DELAY.PAGE_LOADING / 1000 : 0,
|
||||||
|
defaultOptions: {
|
||||||
|
duration: 1.6,
|
||||||
|
easing: quartOut,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
animation.stop()
|
||||||
|
|
||||||
// Transition in
|
// Run animation
|
||||||
requestAnimationFrame(timeline.play)
|
requestAnimationFrame(animation.play)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -146,13 +146,12 @@
|
|||||||
/**
|
/**
|
||||||
* Animations
|
* Animations
|
||||||
*/
|
*/
|
||||||
const animationDelay = $navigating ? DURATION.PAGE_IN : 0
|
|
||||||
const animation = timeline([
|
const animation = timeline([
|
||||||
// Title word
|
// Title word
|
||||||
['.location-page__intro .word', {
|
['.location-page__intro .word', {
|
||||||
y: ['110%', 0],
|
y: ['110%', 0],
|
||||||
}, {
|
}, {
|
||||||
at: 0.2 + animationDelay,
|
at: 0.2,
|
||||||
}],
|
}],
|
||||||
|
|
||||||
// Illustration
|
// Illustration
|
||||||
@@ -160,7 +159,7 @@
|
|||||||
scale: [1.06, 1],
|
scale: [1.06, 1],
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
}, {
|
}, {
|
||||||
at: 0.4 + animationDelay,
|
at: 0.4,
|
||||||
duration: 2.4,
|
duration: 2.4,
|
||||||
}],
|
}],
|
||||||
|
|
||||||
@@ -168,7 +167,7 @@
|
|||||||
['.location-page__intro .of', {
|
['.location-page__intro .of', {
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
}, {
|
}, {
|
||||||
at: 0.95 + animationDelay,
|
at: 0.95,
|
||||||
duration: 1.2,
|
duration: 1.2,
|
||||||
}],
|
}],
|
||||||
|
|
||||||
@@ -177,11 +176,11 @@
|
|||||||
y: ['10%', 0],
|
y: ['10%', 0],
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
}, {
|
}, {
|
||||||
at: 0.9 + animationDelay,
|
at: 0.9,
|
||||||
duration: 1.2,
|
duration: 1.2,
|
||||||
}]
|
}]
|
||||||
], {
|
], {
|
||||||
delay: DELAY.PAGE_LOADING / 1000,
|
delay: $navigating ? DELAY.PAGE_LOADING / 1000 : 0,
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
duration: 1.6,
|
duration: 1.6,
|
||||||
easing: quartOut,
|
easing: quartOut,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
import { navigating } from '$app/stores'
|
||||||
import { stagger, timeline } from 'motion'
|
import { stagger, timeline } from 'motion'
|
||||||
import { DELAY } from '$utils/contants'
|
import { DELAY } from '$utils/contants'
|
||||||
import { quartOut } from 'svelte/easing'
|
import { quartOut } from 'svelte/easing'
|
||||||
@@ -45,7 +46,7 @@
|
|||||||
delay: stagger(0.35),
|
delay: stagger(0.35),
|
||||||
}],
|
}],
|
||||||
], {
|
], {
|
||||||
delay: DELAY.PAGE_LOADING / 1000,
|
delay: $navigating ? DELAY.PAGE_LOADING / 1000 : 0,
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
duration: 1.6,
|
duration: 1.6,
|
||||||
easing: quartOut,
|
easing: quartOut,
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores'
|
import { page, navigating } from '$app/stores'
|
||||||
import { getContext, onMount } from 'svelte'
|
import { getContext, onMount } from 'svelte'
|
||||||
import { timeline, stagger } from 'motion'
|
import { timeline, stagger } from 'motion'
|
||||||
import { DELAY } from '$utils/contants'
|
import { DELAY } from '$utils/contants'
|
||||||
import { smoothScroll } from '$utils/functions'
|
import { smoothScroll } from '$utils/functions'
|
||||||
import { reveal, fade as animeFade } from '$animations/index'
|
import reveal from '$animations/reveal'
|
||||||
import { quartOut } from '$animations/easings'
|
import { quartOut } from '$animations/easings'
|
||||||
// Components
|
// Components
|
||||||
import Metas from '$components/Metas.svelte'
|
import Metas from '$components/Metas.svelte'
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
delay: stagger(0.075),
|
delay: stagger(0.075),
|
||||||
}]
|
}]
|
||||||
], {
|
], {
|
||||||
delay: DELAY.PAGE_LOADING / 1000,
|
delay: $navigating ? DELAY.PAGE_LOADING / 1000 : 0,
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
duration: 1.6,
|
duration: 1.6,
|
||||||
easing: quartOut,
|
easing: quartOut,
|
||||||
@@ -81,9 +81,9 @@
|
|||||||
<PageTransition name="homepage">
|
<PageTransition name="homepage">
|
||||||
<section class="homepage__intro"
|
<section class="homepage__intro"
|
||||||
use:reveal={{
|
use:reveal={{
|
||||||
animation: animeFade,
|
animation: { opacity: [0, 1] },
|
||||||
options: {
|
options: {
|
||||||
duration: 1000,
|
duration: 1,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores'
|
import { page, navigating } from '$app/stores'
|
||||||
import { goto } from '$app/navigation'
|
import { goto } from '$app/navigation'
|
||||||
import { getContext, onMount } from 'svelte'
|
import { getContext, onMount } from 'svelte'
|
||||||
import { fly } from 'svelte/transition'
|
import { fly } from 'svelte/transition'
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { quartOut as quartOutSvelte } from 'svelte/easing'
|
import { quartOut as quartOutSvelte } from 'svelte/easing'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
||||||
import { stagger, timeline } from 'motion'
|
import { stagger, timeline } from 'motion'
|
||||||
import { DELAY } from '$utils/contants'
|
import { DELAY } from '$utils/contants'
|
||||||
@@ -294,11 +294,11 @@
|
|||||||
y: [16, 0],
|
y: [16, 0],
|
||||||
opacity: [0, 1],
|
opacity: [0, 1],
|
||||||
}, {
|
}, {
|
||||||
at: 0.4,
|
at: 0.5,
|
||||||
delay: stagger(0.25),
|
delay: stagger(0.3),
|
||||||
}]
|
}]
|
||||||
], {
|
], {
|
||||||
delay: DELAY.PAGE_LOADING / 1000,
|
delay: $navigating ? DELAY.PAGE_LOADING / 1000 : 0,
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
duration: 1.6,
|
duration: 1.6,
|
||||||
easing: quartOut,
|
easing: quartOut,
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { navigating } from '$app/stores'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { stagger, timeline } from 'motion'
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import { stagger, timeline } from 'motion'
|
||||||
import { DELAY } from '$utils/contants'
|
import { DELAY } from '$utils/contants'
|
||||||
import { quartOut } from '$animations/easings'
|
import { quartOut } from '$animations/easings'
|
||||||
// Components
|
// Components
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
delay: stagger(0.15),
|
delay: stagger(0.15),
|
||||||
}],
|
}],
|
||||||
], {
|
], {
|
||||||
delay: DELAY.PAGE_LOADING / 1000,
|
delay: $navigating ? DELAY.PAGE_LOADING / 1000 : 0,
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
duration: 1.6,
|
duration: 1.6,
|
||||||
easing: quartOut,
|
easing: quartOut,
|
||||||
|
|||||||
Reference in New Issue
Block a user