Compare commits

..

3 commits

16 changed files with 227 additions and 28 deletions

View file

@ -1,21 +1,100 @@
<script> <script>
import "$lib/css/base.css"; import "$lib/css/base.css";
import "$lib/css/hover-card.css" import "$lib/css/hover-card.css";
import anime from "animejs";
import SvgIcon from "@jamescoyle/svelte-icon";
import { mdiEye } from "@mdi/js";
import { popupComponent } from "$lib/ts/popup.ts";
import HoverCardPopup from "$lib/components/popup/hover-card.svelte";
import text from "$lib/json/hover-card.json";
export let picture; export let picture;
export let title = "Title"; export let title = "Title";
export let description = "This is a description, you can add some explaination to your picture in here!"; export let description =
"This is a description, you can add some explaination to your picture in here!";
let divHoverCardOverlay;
let divHoverCardMetadata = {
div: null,
title: null,
description: null,
tip: null,
};
function animateCard(isEntering) {
const opacityValues = [0, 1];
let translateValues = [-30, 0];
if (!isEntering) {
opacityValues.reverse();
translateValues.reverse();
}
anime({
targets: [divHoverCardOverlay, divHoverCardMetadata.div],
opacity: opacityValues,
duration: 400,
easing: "easeInOutQuad",
});
anime({
targets: [divHoverCardMetadata.title, divHoverCardMetadata.tip],
opacity: opacityValues,
translateX: translateValues,
delay: 100,
duration: 500,
easing: "easeInOutQuad",
});
translateValues = translateValues.map((e) => -e);
anime({
targets: [divHoverCardMetadata.description],
opacity: opacityValues,
translateY: translateValues,
delay: 100,
duration: 500,
easing: "easeInOutQuad",
});
}
function showPopup() {
popupComponent.set({
component: HoverCardPopup,
props: { title: "Hello", description: "This is a custom Popup" },
});
}
</script> </script>
<div class="hover-card-container"> <div
class="hover-card-container"
role="button"
tabindex="0"
on:mouseenter={() => {
animateCard(true);
}}
on:mouseleave={() => {
animateCard(false);
}}
on:click={showPopup}
on:keydown={(e) => e.key === "Enter" && showPopup()}
>
<div class="hover-card"> <div class="hover-card">
<img alt="hover-card" src={picture} /> <img alt="hover-card" src={picture} />
</div> </div>
<div class="hover-card-overlay"></div> <div class="hover-card-overlay" bind:this={divHoverCardOverlay}></div>
<div class="hover-card flex-row"> <div class="hover-card flex-col">
<div class="hover-card-metadata margin-bot-force"> <div
<h1>{title}</h1> class="hover-card-tip flex-row"
<p>{description}</p> bind:this={divHoverCardMetadata.tip}
>
<SvgIcon type="mdi" path={mdiEye}></SvgIcon>
<span>{text.seemore}</span>
</div>
<div
class="hover-card-metadata margin-bot-force"
bind:this={divHoverCardMetadata.div}
>
<h1 bind:this={divHoverCardMetadata.title}>{title}</h1>
<p bind:this={divHoverCardMetadata.description}>{description}</p>
</div> </div>
</div> </div>
</div> </div>

View file

@ -0,0 +1,26 @@
<script>
import "$lib/css/popup.css";
import { popupComponent } from "$lib/ts/popup.ts";
let component = null;
popupComponent.subscribe((value) => {
component = value;
});
function close() {
popupComponent.set(null);
}
</script>
{#if component}
<div
class="popup"
role="button"
tabindex="0"
on:click={close}
on:keydown={(e) => e.key === "Escape" && close()}
>
<svelte:component this={component.component} {...component.props} />
</div>
{/if}

View file

@ -0,0 +1,11 @@
<script>
import "$lib/css/hover-card.css"
export let title=""
export let description=""
</script>
<div class="hover-card-popup-container">
<h1>{title}</h1>
<p>{description}</p>
</div>

View file

@ -44,6 +44,7 @@ h2 {
--background-dark: linear-gradient(180deg, rgba(248, 241, 241, 1) 0%, rgba(224, 217, 217, 1) 100%); --background-dark: linear-gradient(180deg, rgba(248, 241, 241, 1) 0%, rgba(224, 217, 217, 1) 100%);
--shadow: rgba(79, 50, 93, 0.25) 0px 30px 60px -12px, rgba(0, 0, 0, 0.3) 0px 18px 36px -18px; --shadow: rgba(79, 50, 93, 0.25) 0px 30px 60px -12px, rgba(0, 0, 0, 0.3) 0px 18px 36px -18px;
--shadow-hover: rgba(43, 33, 48, 0.5) 0px 22px 70px 4px; --shadow-hover: rgba(43, 33, 48, 0.5) 0px 22px 70px 4px;
--shadow-active: #2c1235a0 0px 20px 30px -10px;
--z-index-last: -1; --z-index-last: -1;
--z-index-normal: 0; --z-index-normal: 0;
--z-index-front: 1; --z-index-front: 1;

View file

@ -3,13 +3,13 @@
border: none; border: none;
cursor: pointer; cursor: pointer;
background-image: linear-gradient(to right, var(--navbar-dark) 0%, var(--navbar-light) 100%); background-image: linear-gradient(to right, var(--navbar-dark) 0%, var(--navbar-light) 100%);
box-shadow: 0px 8px 18px -1px #33283760;
padding: 0.2rem; padding: 0.2rem;
transition: var(--transition); transition: var(--transition);
box-shadow: var(--shadow);
} }
.button:active { .button:active {
box-shadow: 0px 4px 10px 0px #2c1235b0; box-shadow: var(--shadow-active);
transform: translateY(3px); transform: translateY(3px);
} }

View file

@ -18,7 +18,7 @@
box-shadow: #00000040 0px 8px 18px -1px; box-shadow: #00000040 0px 8px 18px -1px;
transition: all .1s ease 0s; transition: all .1s ease 0s;
background-color: var(--color-background); background-color: var(--color-background);
z-index: 2; z-index: var(--z-index-normal);
margin-right: 1rem; margin-right: 1rem;
margin-top: 2rem; margin-top: 2rem;
margin-bottom: 2rem; margin-bottom: 2rem;

View file

@ -15,13 +15,13 @@
background: linear-gradient(0deg, #00000000, #261C2C40 50%, #261c2c80 100%); background: linear-gradient(0deg, #00000000, #261C2C40 50%, #261c2c80 100%);
height: calc(var(--navbar-height)*2); height: calc(var(--navbar-height)*2);
width: 100%; width: 100%;
z-index: 0; z-index: var(--z-index-normal);
} }
.cover-img-text { .cover-img-text {
grid-area: overlay; grid-area: overlay;
padding: 4rem; padding: 4rem;
z-index: 1; z-index: var(--z-index-normal);
max-width: 50rem; max-width: 50rem;
} }
@ -44,7 +44,7 @@
.cover-img-darken { .cover-img-darken {
grid-area: overlay; grid-area: overlay;
background-color: rgb(0, 0, 0, 0.35); background-color: rgb(0, 0, 0, 0.35);
z-index: 0; z-index: var(--z-index-normal);
border-bottom-left-radius: 2rem; border-bottom-left-radius: 2rem;
border-bottom-right-radius: 2rem; border-bottom-right-radius: 2rem;
} }

View file

@ -20,6 +20,11 @@
box-shadow: var(--shadow-hover); box-shadow: var(--shadow-hover);
} }
.hover-card-container:active {
transform: translateY(0.3rem) scale(100%);
box-shadow: var(--shadow-active);
}
.hover-card-container img { .hover-card-container img {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -36,16 +41,45 @@
grid-area: overlay; grid-area: overlay;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-image: linear-gradient(0deg, #120e14 0%, #120e14dc 30%, #120e1491 35%, rgba(0,0,0,0) 45%); background-image: linear-gradient(0deg, #120e14 0%, #120e14dc 30%, #120e1491 35%, rgba(0, 0, 0, 0) 45%);
opacity: 0;
}
.hover-card-tip {
color: var(--color-background);
background-color: #120e14b0;
width: fit-content;
margin: 0.4rem;
padding: 0.4rem;
padding-right: 0.6rem;
padding-left: 0.6rem;
border-radius: 3rem;
box-shadow: var(--shadow);
opacity: 0;
}
.hover-card-tip svg {
width: 1.4rem;
height: auto;
}
.hover-card-tip span {
font-family: 'JetBrains Mono';
font-weight: 400;
font-size: 0.8rem;
margin-left: 0.5rem;
} }
.hover-card-metadata { .hover-card-metadata {
width: 100%; width: 100%;
height: 7rem; height: 7rem;
color: var(--color-background); color: var(--color-background);
opacity: 0;
} }
.hover-card-metadata h1 { .hover-card-metadata h1 {
/* Animation */
opacity: 0;
font-family: 'JetBrains Mono'; font-family: 'JetBrains Mono';
font-weight: 800; font-weight: 800;
font-size: 1.3rem; font-size: 1.3rem;
@ -62,6 +96,8 @@
} }
.hover-card-metadata p { .hover-card-metadata p {
/* Animation */
opacity: 0;
font-family: 'JetBrains Mono'; font-family: 'JetBrains Mono';
font-weight: 400; font-weight: 400;
font-style: italic; font-style: italic;
@ -75,4 +111,16 @@
text-overflow: ellipsis; text-overflow: ellipsis;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
line-clamp: 2; line-clamp: 2;
}
/* Pop-up Hover Card */
.hover-card-popup-container {
width: calc(100% - 12rem);
height: calc(100% - 12rem);
margin: 5rem;
padding: 1rem;
background-image: var(--background-dark);
border-radius: 1rem;
box-shadow: var(--shadow);
} }

View file

@ -12,7 +12,7 @@ nav {
height: var(--navbar-height); height: var(--navbar-height);
width: 100%; width: 100%;
position: fixed; position: fixed;
z-index: 5; z-index: var(--z-index-navbar);
transition: var(--transition); transition: var(--transition);
padding-top: 0.5rem; padding-top: 0.5rem;
} }
@ -59,7 +59,7 @@ a {
.navbar-content { .navbar-content {
grid-area: overlay; grid-area: overlay;
z-index: 1; z-index: var(--z-index-normal);
width: inherit; width: inherit;
display: flex; display: flex;
} }

15
src/lib/css/popup.css Normal file
View file

@ -0,0 +1,15 @@
:root {
--popup-blur: blur(1rem);
}
.popup {
width: 100dvw;
height: 100dvh;
position: fixed;
left: 0;
top: 0;
background-color: #150f1aa0;
z-index: var(--z-index-popup);
-webkit-backdrop-filter: var(--popup-blur);
backdrop-filter: var(--popup-blur);
}

View file

@ -40,7 +40,7 @@
box-shadow: var(--shadow); box-shadow: var(--shadow);
border-radius: 1rem; border-radius: 1rem;
position: absolute !important; position: absolute !important;
z-index: 1; z-index: var(--z-index-normal);
transform: translateX(-1rem); transform: translateX(-1rem);
display: none; display: none;
opacity: 0; opacity: 0;

View file

@ -0,0 +1,3 @@
{
"seemore": "Voir plus"
}

View file

@ -50,13 +50,19 @@
}, },
"photos": [ "photos": [
{ {
"url": "https://share.etheryo.fr/rando/2024.07.28/IMG20240728150532.jpg" "url": "https://share.etheryo.fr/rando/2024.07.28/IMG20240728150532.jpg",
"title": "Tarnished Peak (afterwar)",
"description": "Photo prise à Ben More, Écosse"
}, },
{ {
"url": "https://share.etheryo.fr/rando/2024.07.28/IMG20240728142327.jpg" "url": "https://share.etheryo.fr/rando/2024.07.28/IMG20240728142327.jpg",
"title": "Whitewashed",
"description": "Photo prise à Ben More, Écosse"
}, },
{ {
"url": "https://share.etheryo.fr/rando/2023.11.01/Groupe%20Ombre%20Lac%20Montagne%20Rouge%20Bleu.jpg" "url": "https://share.etheryo.fr/rando/2023.11.01/Groupe%20Ombre%20Lac%20Montagne%20Rouge%20Bleu.jpg",
"title": "Compagnons",
"description": "Photo prise au Lac d'Oô, France"
} }
] ]
} }

3
src/lib/ts/popup.ts Normal file
View file

@ -0,0 +1,3 @@
import { writable } from "svelte/store";
export const popupComponent = writable(null);

View file

@ -1,14 +1,17 @@
<script> <script>
import { page } from '$app/stores'; import { page } from "$app/stores";
import '$lib/css/base.css'; import "$lib/css/base.css";
import '$lib/css/navbar.css'; import "$lib/css/navbar.css";
import "$lib/css/popup.css";
import Navbar from '$lib/components/navbar.svelte'; import Navbar from "$lib/components/navbar.svelte";
import Footer from '$lib/components/footer.svelte'; import Footer from "$lib/components/footer.svelte";
import Popup from "$lib/components/popup.svelte";
// $: pageUrl = $page.url.pathname; // $: pageUrl = $page.url.pathname;
</script> </script>
<Popup />
<Navbar /> <Navbar />
<div class="base"> <div class="base">
<slot /> <slot />

View file

@ -70,7 +70,11 @@
</div> </div>
<div class="flex w-100 justify-center"> <div class="flex w-100 justify-center">
{#each hubjson.photos as photo} {#each hubjson.photos as photo}
<HoverCard picture={photo.url} /> <HoverCard
picture={photo.url}
title={photo.title}
description={photo.description}
/>
{/each} {/each}
</div> </div>
</div> </div>