Add 3d Viewport component

Implement a Viewport component and associated update script that allows
rendering 3d content inside it.  This is based on the implementation
used at Furnavia for their 2025 website.
This commit is contained in:
Hornwitser 2024-08-10 00:00:26 +02:00
parent c8527f17f7
commit 49a329ac43
4 changed files with 47 additions and 0 deletions

View file

@ -10,6 +10,7 @@ export default function BasePage(props: BaseProps) {
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>{props.title}</title> <title>{props.title}</title>
<link rel="stylesheet" href="/style.css" /> <link rel="stylesheet" href="/style.css" />
<script type="module" defer="" src="viewport.js" />
</head> </head>
<body> <body>
<header class="header"> <header class="header">

View file

@ -0,0 +1,12 @@
/**
3d Viewport
*/
export default function Viewport(
props: {
children: unknown,
}
) {
return <div class="viewport in3d">
{ props.children }
</div>
}

View file

@ -64,6 +64,19 @@ ol, ul {
padding-inline-start: 1.25em; padding-inline-start: 1.25em;
} }
/* 3d viewport */
.in3d {
transform-style: preserve-3d;
}
.viewport {
pointer-events: none;
perspective: 300vmax;
perspective-origin: 50% var(--y-offset);
}
.viewport * {
pointer-events: initial;
}
/* Base Page Layout */ /* Base Page Layout */
body { body {
max-width: 50rem; max-width: 50rem;

21
web/viewport.js Normal file
View file

@ -0,0 +1,21 @@
function setViewportOffset(viewport) {
// Calculate the distance from the top of the element to the center of the screen.
// This is an accurate rendering, but we might want to change it for artistic effects.
const windowHeight = window.innerHeight;
const rect = viewport.getBoundingClientRect();
if (rect.top > windowHeight || rect.bottom < 0) {
return;
}
const yOffset = windowHeight / 2 - rect.top;
viewport.style.setProperty('--y-offset', yOffset+"px");
}
const viewports = document.querySelectorAll(".viewport");
function updateViewports() {
for (const viewport of viewports) {
setViewportOffset(viewport);
}
}
window.addEventListener("scroll", updateViewports, { passive: true });
window.addEventListener("resize", updateViewports, { passive: true });
updateViewports();