Compare commits
No commits in common. "5b11664a3f51c573aeccab015a3dc5fe991ba651" and "7c331406a5f6e8f99efe7ecf5bb43281c345eb1c" have entirely different histories.
5b11664a3f
...
7c331406a5
@ -11,6 +11,7 @@ h3 {
|
|||||||
|
|
||||||
article>h1 {
|
article>h1 {
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
article>h2 {
|
article>h2 {
|
||||||
|
BIN
public/projects/karl/favicon.png
(Stored with Git LFS)
BIN
public/projects/karl/favicon.png
(Stored with Git LFS)
Binary file not shown.
@ -19,6 +19,7 @@
|
|||||||
_img.addEventListener("load", () => {
|
_img.addEventListener("load", () => {
|
||||||
img.classList.remove("thumb-loading");
|
img.classList.remove("thumb-loading");
|
||||||
_img.style.opacity = "1";
|
_img.style.opacity = "1";
|
||||||
|
console.log("loaded");
|
||||||
});
|
});
|
||||||
if (_img?.alt) altText = _img.alt;
|
if (_img?.alt) altText = _img.alt;
|
||||||
else altText = "";
|
else altText = "";
|
||||||
@ -61,12 +62,10 @@
|
|||||||
style={`--height:${height}px`}
|
style={`--height:${height}px`}
|
||||||
>
|
>
|
||||||
{#if title}
|
{#if title}
|
||||||
<div class="flex items-center p-x-4 p-y-6 bg justify-between">
|
<div class="flex items-center p-x-4 bg justify-between">
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
|
|
||||||
<div
|
<div class="overflow-hidden rounded-md bg-light gap-2 flex p-1">
|
||||||
class="overflow-hidden rounded-md bg-light gap-2 flex p-2 border border-light"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
class="flex-1 i-tabler-arrow-left"
|
class="flex-1 i-tabler-arrow-left"
|
||||||
aria-label="previous image"
|
aria-label="previous image"
|
||||||
@ -81,10 +80,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="images border-t-1 border-b-1 border-neutral" bind:this={slot}>
|
<div class="images" bind:this={slot}>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 flex items-center place-content-between bg">
|
<div class="p-2 flex place-content-between bg">
|
||||||
<p>
|
<p>
|
||||||
{#if images?.length}
|
{#if images?.length}
|
||||||
{index + 1}/{images?.length}
|
{index + 1}/{images?.length}
|
||||||
@ -98,7 +97,7 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.wrapper {
|
.wrapper {
|
||||||
grid-template-rows: 1fr 50px;
|
grid-template-rows: 1fr 40px;
|
||||||
transition: height 0.3s;
|
transition: height 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,11 +106,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wrapper.title {
|
.wrapper.title {
|
||||||
grid-template-rows: 50px 1fr 50px;
|
grid-template-rows: 40px 1fr 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loaded {
|
.loaded {
|
||||||
height: calc(var(--height) + 50px);
|
height: calc(var(--height) + 40px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.not-loaded .images :global(picture):first-child {
|
.not-loaded .images :global(picture):first-child {
|
||||||
@ -119,7 +118,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wrapper.title.loaded {
|
.wrapper.title.loaded {
|
||||||
height: calc(var(--height) + 100px);
|
height: calc(var(--height) + 80px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.images :global(picture) {
|
.images :global(picture) {
|
||||||
@ -127,10 +126,6 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.images :global(.thumb-loading)::before {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.images :global(.active) {
|
.images :global(.active) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block !important;
|
display: block !important;
|
||||||
|
@ -12,7 +12,7 @@ const { headings } = Astro.props;
|
|||||||
|
|
||||||
<div class="toc-wrapper lg:fixed lg:left-6 lg:top-6">
|
<div class="toc-wrapper lg:fixed lg:left-6 lg:top-6">
|
||||||
<details
|
<details
|
||||||
class="py-2 lg:px-4 rounded-xl lg:border lg:border-neutral flex flex-col lg:bg gap-2"
|
class="py-2 px-4 rounded-xl border border-neutral flex flex-col bg gap-2"
|
||||||
>
|
>
|
||||||
<summary class="text-lg cursor-pointer select-none"
|
<summary class="text-lg cursor-pointer select-none"
|
||||||
>{t("toc.title")}</summary
|
>{t("toc.title")}</summary
|
||||||
|
55
src/components/Thumbhash.astro
Normal file
55
src/components/Thumbhash.astro
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<script>
|
||||||
|
import { thumbHashToRGBA, rgbaToDataURL } from "thumbhash";
|
||||||
|
|
||||||
|
function show(img: HTMLImageElement) {
|
||||||
|
img.style.opacity = "1";
|
||||||
|
img.style.filter = "blur(0px)";
|
||||||
|
setTimeout(() => {
|
||||||
|
if (img.parentNode) {
|
||||||
|
(img.parentNode as HTMLPictureElement).style.background = "";
|
||||||
|
}
|
||||||
|
}, 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Thumbhash script loaded");
|
||||||
|
|
||||||
|
document.querySelectorAll("[data-thumbhash]").forEach((entry) => {
|
||||||
|
const parent = entry?.parentNode as HTMLPictureElement;
|
||||||
|
const img = entry as HTMLImageElement;
|
||||||
|
|
||||||
|
if (parent?.nodeName !== "PICTURE") return;
|
||||||
|
|
||||||
|
const hash = img.getAttribute("data-thumbhash");
|
||||||
|
if (!hash) return;
|
||||||
|
|
||||||
|
// its only browser, why you have to be mad
|
||||||
|
const decodedString = window.atob(hash);
|
||||||
|
|
||||||
|
// Create Uint8Array from decoded string
|
||||||
|
const buffer = new Uint8Array(decodedString.length);
|
||||||
|
for (let i = 0; i < decodedString.length; i++) {
|
||||||
|
buffer[i] = decodedString.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = thumbHashToRGBA(buffer);
|
||||||
|
const dataURL = rgbaToDataURL(image.w, image.h, image.rgba);
|
||||||
|
|
||||||
|
parent.style.background = `url(${dataURL})`;
|
||||||
|
parent.style.backgroundSize = "cover";
|
||||||
|
|
||||||
|
if (img.complete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.style.opacity = "0";
|
||||||
|
img.style.filter = "blur(5px)";
|
||||||
|
img.style.transition = "opacity 0.6s ease, filter 0.8s ease";
|
||||||
|
|
||||||
|
const sources = parent.querySelectorAll("source");
|
||||||
|
|
||||||
|
img.onload = () => show(img);
|
||||||
|
for (const source of sources) {
|
||||||
|
source.addEventListener("load", () => show(img));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
@ -5,7 +5,6 @@ cover: ./images/Indicatrices_of_Distortion.png
|
|||||||
license: "CC-BY-SA:4.0"
|
license: "CC-BY-SA:4.0"
|
||||||
featured: true
|
featured: true
|
||||||
toc: true
|
toc: true
|
||||||
icon: /projects/karl/favicon.png
|
|
||||||
links:
|
links:
|
||||||
[
|
[
|
||||||
["live", "https://max-richter.dev/karl"],
|
["live", "https://max-richter.dev/karl"],
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
import Layout from "@layouts/Layout.astro";
|
import Layout from "@layouts/Layout.astro";
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
import { useTranslatedPath } from "@i18n/utils";
|
import { useTranslatedPath } from "@i18n/utils";
|
||||||
import SmallCard from "@components/SmallCard.astro";
|
|
||||||
import SmallGrid from "@components/SmallGrid.astro";
|
|
||||||
|
|
||||||
const collections = ["blog", "photos", "projects"] as const;
|
const collections = ["blog", "photos", "projects"] as const;
|
||||||
|
|
||||||
@ -50,15 +48,18 @@ const posts = allPosts.filter((post) => {
|
|||||||
|
|
||||||
<Layout title="Max Richter">
|
<Layout title="Max Richter">
|
||||||
<article>
|
<article>
|
||||||
<h1>Tag: {tag}</h1>
|
<h1>Tags</h1>
|
||||||
<SmallGrid>
|
{posts.length}
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
{
|
{
|
||||||
posts.map((post) => {
|
posts.map((post) => {
|
||||||
return (
|
return (
|
||||||
<SmallCard post={post} />
|
<a href={tp("/" + post.collection + "/" + post.slug)}>
|
||||||
|
{post.slug}
|
||||||
|
</a>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</SmallGrid>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
Loading…
Reference in New Issue
Block a user