Add hover effect on Location

This commit is contained in:
2021-09-30 23:30:13 +02:00
parent 624f02d84d
commit 7777d26907
6 changed files with 313 additions and 126 deletions

View File

@@ -1,4 +1,8 @@
<script lang="ts">
import { getContext } from 'svelte'
import { spring } from 'svelte/motion'
import dayjs from 'dayjs'
import { lerp } from '$utils/functions'
// Components
import Image from '$components/atoms/Image.svelte'
import Badge from '$components/atoms/Badge.svelte'
@@ -6,18 +10,67 @@
export let location: any
export let index: number
const { name, slug, country, last_updated } = location
const { settings: { limit_new }}: any = getContext('global')
const { name, slug, country, date_updated } = location
// Location date limit
const dateNowOffset = dayjs().subtract(limit_new, 'day')
const dateLocationUpdated = dayjs(date_updated)
const isNew = dateLocationUpdated.isAfter(dateNowOffset)
let locationEl: HTMLElement
let photoIndex = 0
const offset = spring({ x: 0, y: 0 }, {
stiffness: 0.075,
damping: 0.9
})
// Moving cursor over
const handleMouseMove = ({ clientX }: MouseEvent) => {
const { width, left } = locationEl.getBoundingClientRect()
const moveProgress = (clientX - left) / width // 0 to 1
// Move horizontally
offset.update($c => ({
x: lerp(-56, 56, moveProgress),
y: 0
}))
// Change photo index from mouse position percentage
photoIndex = Math.round(lerp(0, 3, moveProgress))
}
// Leaving mouseover
const handleMouseLeave = (event: MouseEvent) => {
offset.update($c => ({
x: $c.x,
y: 40
}))
}
</script>
<div class="location" role="listitem">
<a href="/{country.slug}/{slug}" rel="prefetch" sveltekit-noscroll>
<div class="location" role="listitem" bind:this={locationEl}
style="--offset-x: {$offset.x}px; --offset-y: {$offset.y}px; --rotate: {$offset.x * 0.15}deg"
>
<a href="/{country.slug}/{slug}"
on:mousemove={handleMouseMove}
on:mouseleave={handleMouseLeave}
sveltekit-noscroll
>
<Image id={country.flag.id} alt="Flag of {country.name}" width={32} height={32} />
<h3 class="location__name">
{name}
</h3>
<span class="text-label location__country">{country.name}</span>
{#if index < 2}
<dl>
<dt class="location__name">
{name}
</dt>
<dd class="location__country text-label">
{country.name}
</dd>
</dl>
{#if isNew}
<Badge text="New" />
{/if}
</a>
<div class="location__photos">
<span>{photoIndex}</span>
</div>
</div>

View File

@@ -1,10 +1,42 @@
.location {
position: relative;
text-align: center;
& > * {
a {
display: flex;
flex-direction: column;
justify-content: center;
padding: 24px;
text-decoration: none;
// Background circle
&:after {
opacity: 0;
content: "";
display: block;
position: absolute;
pointer-events: none;
top: 50%;
left: 50%;
width: 256px;
height: 256px;
transform: translate3d(-50%, -50%, 0) scale(0.8);
transform-origin: 50% 50%;
background-color: rgba($color-tertiary, 0.1);
border-radius: 100%;
transition: transform 0.75s var(--ease-quart), opacity 0.75s var(--ease-quart);
}
// Hover
&:hover {
strong {
color: $color-tertiary;
}
&:after {
opacity: 1;
transform: translate3d(-50%, -50%, 0);
}
}
}
// Flag
@@ -13,21 +45,53 @@
margin: 0 auto;
border-radius: 100%;
}
// Link
a {
text-decoration: none;
}
// Location name
&__name {
display: block;
color: $color-secondary;
margin: 20px 0 -2px;
margin: 20px 0 8px;
font-size: rem(48px);
font-family: $font-serif;
font-weight: 300;
line-height: 1.2;
transition: color 0.75s var(--ease-quart);
}
// Country
&__country {
color: $color-tertiary;
}
// Badge
.badge {
position: absolute;
left: 50%;
bottom: -32px;
transform: translateX(-50%);
}
// Photos
&__photos {
opacity: 0;
position: absolute;
z-index: 2;
top: -10%;
left: 50%;
width: 240px;
height: 160px;
background: #444;
border-radius: 6px;
transform: translate3d(calc(-50% + var(--offset-x)), calc(-50% + var(--offset-y)), 0) rotate(var(--rotate));
overflow: hidden;
box-shadow: 0 8px 8px rgba(#000, 0.1), 0 16px 28px rgba(#000, 0.12);
pointer-events: none;
transition: opacity 0.5s var(--ease-quart);
}
&:hover {
.location__photos {
opacity: 1;
}
}
}

View File

@@ -21,11 +21,11 @@
flex-flow: row wrap;
justify-content: center;
max-width: 1200px;
margin: 128px auto 80px;
margin: 104px auto 80px;
justify-items: center;
.location {
margin: 0 40px 80px;
margin: 0 22px 56px;
}
}
}

6
src/utils/functions.ts Normal file
View File

@@ -0,0 +1,6 @@
/**
* Linear Interpolation
*/
export const lerp = (start: number, end: number, amt: number): number => {
return (1 - amt) * start + amt * end
}