feat: track images with git lfs

This commit is contained in:
2024-03-27 01:51:42 +01:00
parent f0129ecc76
commit 31b24de86c
142 changed files with 5133 additions and 169 deletions

View File

@ -0,0 +1,178 @@
<script context="module" lang="ts">
let mouse = { x: 0, y: 0 };
let visible = writable(false);
const maxDistance = globalThis.innerWidth / 3;
let frameId: number;
function update() {
frameId = requestAnimationFrame(update);
for (const eye of eyes) {
const dx = eye.x - mouse.x;
const dy = eye.y - mouse.y;
const angle = Math.atan2(dy, dx);
const distance = Math.hypot(dx, dy);
if (distance < maxDistance) {
eye.rotation = angle * (180 / Math.PI) - 90;
eye.distance = -Math.min(17, distance);
eye.el.style.setProperty("--distance", `${eye.distance}px`);
eye.el.style.setProperty(
"--rotation",
`${Math.floor(eye.rotation)}deg`,
);
eye.el.classList.add("active");
} else {
eye.el.classList.remove("active");
}
}
}
const eyes: {
x: number;
y: number;
rotation: number;
distance: number;
el: HTMLDivElement;
pupil: HTMLDivElement;
}[] = [];
function register(eye: HTMLDivElement) {
const { x, y, width, height } = eye.getBoundingClientRect();
eyes.push({
x: x + width / 2,
y: y + height / 2,
rotation: 0,
distance: 0,
el: eye,
pupil: eye.children[0] as HTMLDivElement,
});
if (frameId) cancelAnimationFrame(frameId);
update();
}
function unregister(eye: HTMLDivElement) {
const index = eyes.findIndex((e) => e.el === eye);
eyes.splice(index, 1);
if (eyes.length === 0) cancelAnimationFrame(frameId);
}
function handleMouseMove(ev: MouseEvent) {
mouse.x = ev.clientX;
mouse.y = ev.clientY;
}
</script>
<script lang="ts">
import { onMount } from "svelte";
import { writable } from "svelte/store";
import { scale } from "svelte/transition";
let eye: HTMLDivElement;
let _old_eye: HTMLDivElement;
$: if (eye !== _old_eye) {
if (_old_eye) {
unregister(_old_eye);
}
if (eye) {
register(eye);
}
_old_eye = eye;
}
onMount(() => {
return () => {
unregister(eye);
};
});
</script>
<svelte:window on:mousemove={handleMouseMove} />
<div
class="googley-eyes"
on:click={() => ($visible = !$visible)}
role="button"
tabindex="0"
aria-label="Toggle Googley Eyes"
aria-hidden="true"
on:keydown={(ev) => (ev.key === "Enter" ? ($visible = !$visible) : "")}
>
{#if $visible}
<div class="eye" bind:this={eye} transition:scale>
<div class="pupil"></div>
</div>
{/if}
</div>
<style>
.googley-eyes {
position: relative;
width: 100%;
height: 100%;
display: flex;
width: 50px;
height: 50px;
justify-content: center;
align-items: center;
}
.eye {
position: relative;
overflow: hidden;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: white;
display: flex;
justify-content: center;
align-items: center;
transform: rotate(var(--rotation));
box-shadow: 0px 0px 30px #00000094;
outline: solid 1px #737373;
}
.eye::before {
content: "";
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: rgba(200, 200, 200, 0.01);
transform: rotate(calc(var(--rotation) * -1));
box-shadow:
5px 5px 10px #ffffff70 inset,
1px 1px 4px #ffffff96 inset,
-2px -2px 10px black inset,
2px 2px 5px #00000078;
}
.googley-eyes > :global(.active > .pupil) {
transform: translateY(var(--distance)) scale(0.7);
}
.pupil {
position: absolute;
width: 50%;
height: 50%;
border-radius: 50%;
transition: transform cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s;
}
.pupil::after {
content: "";
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: black;
box-shadow:
-5px -5px 10px #ffffff70 inset,
-1px -1px 4px #ffffff96 inset,
2px 2px 2px black inset,
-2px -2px 5px #00000078;
transform: translate(-50%, -50%) rotate(calc(var(--rotation) * -1 + 180deg));
top: 50%;
left: 50%;
}
</style>

View File

@ -0,0 +1,46 @@
---
import type { ImageMetadata } from "astro";
import { Image as AstroImage } from "astro:assets";
interface Props {
src: ImageMetadata;
alt: string;
maxWidth?: number;
}
const { src, alt, maxWidth } = Astro.props;
const image = typeof src === "string" ? await import(src) : src;
const sizes = [
{
width: 240,
media: "(max-width: 360px)",
},
{
width: 540,
media: "(max-width: 720px)",
},
{
width: 720,
media: "(max-width: 1600px)",
},
{
width: image.width,
},
].filter((size) => !maxWidth || size.width <= maxWidth);
---
<AstroImage
src={image}
alt={alt}
widths={sizes.map((size) => size.width)}
sizes={sizes
.map((size) => `${size.media || "100vw"} ${size.width}px`)
.join(", ")}
/>
<style>
img {
border-radius: 0.5rem;
}
</style>

View File

@ -2,11 +2,18 @@
import { locales, defaultLocale, getLocale } from "astro-i18n-aut";
import { useTranslations } from "../i18n/utils";
const reg = new RegExp(`^\/(${Object.keys(locales).join("|")})\/`);
function translatePath(lang: string) {
const p = Astro.url.pathname.replace(reg, "").replace(/^\//, "");
if (lang === defaultLocale) return `/${p}`;
return `/${lang}/${p}`;
const split = Astro.url.pathname.split("/").filter((s) => s.length);
if (split[0] in locales) {
split.shift();
}
if (lang === defaultLocale) {
return `/${split.join("/")}`;
}
return `/${[lang, ...split].join("/")}`;
}
const locale = getLocale(Astro.url);

View File

@ -1,8 +1,9 @@
<svg
width="51"
height="51"
width="50"
height="50"
viewBox="0 0 51 51"
fill="none"
style="height: 100%; width: auto"
xmlns="http://www.w3.org/2000/svg"
>
<defs>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

86
src/components/Max.astro Normal file
View File

@ -0,0 +1,86 @@
---
import MaxImg from "./Max.png";
import Image from "./Image.astro";
import GoogleyEye from "./GoogleyEye.svelte";
import { useTranslations } from "@i18n/utils";
import { getLocale } from "astro-i18n-aut";
const locale = getLocale(Astro.url);
const t = useTranslations(locale);
---
<section class="googley-eye-target">
<div class="image">
<Image src={MaxImg} alt="its mee" maxWidth={700} />
<div class="eye right">
<GoogleyEye client:load />
</div>
<div class="eye left">
<GoogleyEye client:load />
</div>
</div>
<div class="content">
<h1>{t("home.title")}</h1>
<p>{t("home.subtitle")}</p>
</div>
</section>
<style>
.image {
position: relative;
height: 130%;
align-self: end;
overflow: hidden;
border-bottom-left-radius: 20px;
display: flex;
align-items: flex-end;
}
.image > :global(img) {
height: 100%;
object-fit: contain;
object-position: top;
}
section {
position: relative;
margin-top: 100px;
height: 180px;
background-color: var(--background);
border-radius: 0px 20px 0px 20px;
border: solid thin var(--outline);
display: grid;
grid-template-columns: 300px 1fr;
background: var(--background-gradient);
background: linear-gradient(
150deg,
var(--neutral400) 0%,
var(--neutral500) 100%
);
}
.eye {
position: absolute;
}
.eye.left {
top: 25%;
right: 30%;
}
.eye.right {
top: 27%;
right: 15%;
}
.content {
padding: 30px;
padding-left: 15px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
</style>

BIN
src/components/Max.png (Stored with Git LFS) Executable file

Binary file not shown.

View File

@ -3,17 +3,43 @@ import { getLocale } from "astro-i18n-aut";
import { useTranslations, useTranslatedPath } from "../i18n/utils";
import Logo from "./Logo.astro";
function isActive(path) {
return Astro.url.pathname === path ? "active" : "";
}
const lang = getLocale(Astro.url);
const t = useTranslations(lang);
const translatePath = useTranslatedPath(lang);
const paths = [
{
link: translatePath("/"),
component: Logo,
},
{
link: translatePath("/blog"),
text: t("nav.blog"),
},
{
link: translatePath("/projects"),
text: t("nav.projects"),
},
{
link: translatePath("/photos"),
text: t("nav.photos"),
},
{
link: translatePath("/videos"),
text: t("nav.videos"),
},
];
---
<style>
ul {
display: flex;
gap: 1rem;
list-style: none;
padding: 0;
height: 50px;
}
li {
display: flex;
@ -22,26 +48,71 @@ const translatePath = useTranslatedPath(lang);
a {
color: var(--text-color);
text-decoration: none;
max-height: 100%;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
text-transform: lowercase;
}
ul > li {
border: solid thin var(--outline);
border-left: none;
position: relative;
background: var(--background);
flex: 1;
}
ul > li.logo {
flex: unset;
border: none;
padding: 0px;
background: none;
height: 34px;
margin: 8px;
margin-left: 0px;
--fill: #cb5a5a;
--background-fill: none;
}
.logo > a {
height: 100%;
}
ul > li > a {
padding: 0px;
}
ul > li {
display: flex;
align-items: center;
}
ul > li.active {
background-color: var(--background-color);
}
ul > li:nth-child(2) {
border-radius: 0px 0px 0px 10px;
border-left: solid thin var(--outline);
margin-left: 20px;
}
ul > li:last-child {
border-radius: 0px 10px 0px 0px;
}
</style>
<ul>
<li>
<a
href={translatePath("/")}
style="--fill: red; --background-fill: transparent;"
>
<Logo />
</a>
</li>
<li>
<a href={translatePath("/blog")}>
{t("nav.blog")}
</a>
</li>
<li>
<a href={translatePath("/about")}>
{t("nav.about")}
</a>
</li>
{
paths.map(({ link, text, component }, index) => (
<li
class={`${component ? "logo" : ""} ${isActive(link) ? "active" : ""}`}
>
<a href={link}>{component ? <Logo /> : text}</a>
</li>
))
}
</ul>

View File

@ -0,0 +1,19 @@
---
interface Props {
id: string;
}
---
<iframe
width="420"
height="315"
src={`https://www.youtube.com/embed/${Astro.props.id}`}></iframe>
<style>
iframe {
border: none;
width: 100%;
height: 315px;
border-radius: 10px;
}
</style>