Files
housesof/apps/website/src/components/atoms/ScrollingTitle.svelte

74 lines
1.9 KiB
Svelte

<style lang="scss">
.scrolling-title {
display: inline-block;
transform: translate3d(var(--parallax-x), 0, 0);
transition: transform 1.2s var(--ease-quart);
will-change: transform;
}
</style>
<script lang="ts">
import cx from 'classix'
import { map } from 'utils/math'
import reveal from '$animations/reveal'
export let tag: string
export let label: string = undefined
export let parallax: number = undefined
export let offsetStart: number = undefined
export let offsetEnd: number = undefined
export let animate = true
let scrollY: number
let innerWidth: number
let innerHeight: number
let titleEl: HTMLElement
let isLarger: boolean
// Define default values
$: if (titleEl && !offsetStart && !offsetEnd) {
offsetStart = titleEl.offsetTop - innerHeight * (innerWidth < 768 ? 0.2 : 0.75)
offsetEnd = titleEl.offsetTop + innerHeight * (innerWidth < 768 ? 0.5 : 0.5)
}
// Check if title is larger than viewport to translate it
$: isLarger = titleEl && titleEl.offsetWidth >= innerWidth
// Calculate the parallax value
$: if (titleEl) {
const toTranslate = 100 - (innerWidth / titleEl.offsetWidth * 100)
parallax = isLarger ? map(scrollY, offsetStart, offsetEnd, 0, -toTranslate, true) : 0
}
$: classes = cx(
'scrolling-title',
'title-huge',
$$props.class
)
const revealOptions = animate ? {
children: '.char',
animation: { y: ['-105%', 0] },
options: {
stagger: 0.06,
duration: 1.6,
delay: 0.2,
threshold: 0.2,
},
} : null
</script>
<svelte:window
bind:scrollY
bind:innerWidth bind:innerHeight
/>
<svelte:element this={tag}
bind:this={titleEl}
class={classes} aria-label={label}
style:--parallax-x="{parallax}%"
use:reveal={revealOptions}
>
<slot />
</svelte:element>