initial commit

This commit is contained in:
Andre Schaf 2026-03-29 20:46:18 +02:00
commit 1e9f6b7fe0
11 changed files with 482 additions and 0 deletions

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module schaf.dev
go 1.25.8

49
main.go Normal file
View file

@ -0,0 +1,49 @@
package main
import (
"fmt"
"net/http"
"path/filepath"
)
func main() {
fileServer := http.FileServer(neuteredFs{http.Dir("./static")})
http.Handle("/", fileServer)
err := http.ListenAndServe(":3000", nil)
if err != nil {
fmt.Println(err)
}
}
func (nfs neuteredFs) Open(path string) (http.File, error) {
f, err := nfs.fs.Open(path)
if err != nil {
return nil, err
}
s, err := f.Stat()
if err != nil {
return nil, err
}
if s.IsDir() {
index := filepath.Join(path, "index.html")
if _, err := nfs.fs.Open(index); err != nil {
closeErr := f.Close()
if closeErr != nil {
return nil, closeErr
}
return nil, err
}
}
return f, nil
}
type neuteredFs struct {
fs http.FileSystem
}

174
static/css/main.css Normal file
View file

@ -0,0 +1,174 @@
:root {
--side-length: 30vw;
--side-length-neg: -30vw;
--side-length-half: calc(var(--side-length) / 2);
--side-length-half-neg: calc(var(--side-length-neg) / 2);
--colour-bg-main: #d4c3a3;
--colour-bg-secondary: #4a4a4a;
--colour-primary: #f6e8d4;
--colour-text-main: #fff;
--colour-cube-1: #dd4814;
--colour-cube-2: #0497aa;
}
@font-face {
font-family: "Montserrat";
src: url(../fonts/montserrat/Montserrat-Medium.otf) format("truetype");
}
html {
font-family: Montserrat, Verdana, Tahoma;
color: var(--colour-text-main);
background-color: var(--colour-bg-main);
font-size: 16px;
color: var(--colour-primary);
}
body {
margin: 0;
display: flex;
flex-direction: column;
height: 100vh;
}
header, footer {
padding: 0 3rem;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--colour-bg-secondary);
height: 4rem;
}
header {
font-size: 2rem;
justify-content: flex-end;
}
footer {
height: 2.5rem;
}
main {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.wrap {
transition: opacity linear 3s;
perspective: 250vw;
perspective-origin: 500% 50vw;
width: var(--side-length);
height: var(--side-length);
}
.cube {
transform-style: preserve-3d;
width: var(--side-length);
height: var(--side-length);
animation: spin 50s infinite ease-in-out;
}
.wrap:hover {
opacity: 0.1;
}
.cube div {
position: absolute;
width: var(--side-length);
height: var(--side-length);
display: flex;
justify-content: center;
align-items: center;
}
.cube div div {
opacity: 0.8;
background-color: var(--colour-bg-secondary);
width: 90%;
height: 90%;
}
.cube div div:hover {
opacity: 0.2;
}
.front div, .back div {
animation: fb 50s infinite cubic-bezier(0.165, 0.84, 0.44, 1);
}
.left div, .right div {
animation: rl 50s infinite cubic-bezier(0.165, 0.84, 0.44, 1);
}
.top div, .bottom div {
animation: tb 50s infinite cubic-bezier(0.165, 0.84, 0.44, 1);
}
.back {
transform: translateZ(var(--side-length-half-neg)) rotateY(180deg);
}
.right {
transform: rotateY(-270deg) translateX(var(--side-length-half));
transform-origin: top right;
}
.left {
transform: rotateY(270deg) translateX(var(--side-length-half-neg));
transform-origin: center left;
}
.top {
transform: rotateX(-90deg) translateY(var(--side-length-half-neg));
transform-origin: top center;
}
.bottom {
transform: rotateX(90deg) translateY(var(--side-length-half));
transform-origin: bottom center;
}
.front {
transform: translateZ(var(--side-length-half));
}
.cube .front div {
opacity: 0.9;
transform: translateZ(var(--side-length-half));
background-color: #a39494;
}
.cube .bottom div {
background-color: #7d7d7d;
}
@keyframes spin {
0%, 10% { transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg); }
11%, 20% { transform: rotateX(0deg) rotateY(0deg) rotateZ(180deg); }
21%, 35% { transform: rotateX(90deg) rotateY(0deg) rotateZ(180deg); }
36%, 50% { transform: rotateX(90deg) rotateY(90deg) rotateZ(180deg); }
51%, 65% { transform: rotateX(90deg) rotateY(90deg) rotateZ(360deg); }
66%, 80% { transform: rotateX(180deg) rotateY(90deg) rotateZ(360deg); }
81%, 99% { transform: rotateX(180deg) rotateY(360deg) rotateZ(360deg); }
100% { transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg); }
}
@keyframes fb {
0%, 9% { border-radius: 0; }
10%, 11% { border-radius: 50% }
12%, 49% { border-radius: 0; }
50%, 51% { border-radius: 50%; }
52%, 64% { border-radius: 0; }
65%, 66% { border-radius: 50%; }
67%, 100% { border-radius: 0; }
}
@keyframes rl {
0%, 19% { border-radius: 0; }
20%, 21% { border-radius: 50%; }
22%, 98% { border-radius: 0; }
99%, 100% { border-radius: 50% }
}
@keyframes tb {
0%, 34% { border-radius: 0; }
35%, 36% { border-radius: 50%; }
37%, 79% { border-radius: 0; }
80%, 81% { border-radius: 50%; }
82%, 100% { border-radius: 0; }
}

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

33
static/index.html Normal file
View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>_schaf.dev</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="./css/main.css">
</head>
<body>
<header>
_schaf.dev
</header>
<main>
<div class="wrap">
<div class="cube">
<div class="front"><div></div></div>
<div class="back"><div></div></div>
<div class="top"><div></div></div>
<div class="bottom"><div></div></div>
<div class="left"><div></div></div>
<div class="right"><div></div></div>
</div>
</div>
</main>
<footer>
_made by a human
</footer>
</body>
</html>

BIN
static/pp/andre.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 KiB

BIN
static/pp/felix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 936 KiB

41
static/pp/index.html Normal file
View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="">
<style>
html {
width: 100vw;
height: 100vh;
overflow: hidden;
}
#container-canvas,
body {
margin: 0;
width: 100%;
height: 100%;
}
#pp-canvas canvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<!--[if lt IE 7]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="#">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<div id="container-canvas" style="width: 100%; height: 100%;">
<canvas id="pp-canvas" style="background-color:#333"></canvas>
</div>
<script src="./pp.js" async defer></script>
</body>
</html>

182
static/pp/pp.js Normal file
View file

@ -0,0 +1,182 @@
(function() {
var canvas = document.getElementById("pp-canvas");
var ctx = canvas.getContext("2d");
var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
var radius = Math.min(w, h) * 2;
var radiusPeople = radius / 6;
canvas.width = w;
canvas.height = h;
ctx.translate(w / 2, h / 2);
var sliceToColor = [
'#ECEAE0',
'#AC2832',
'#CB8C1D',
];
var sliceToRoom = [
'BAD',
'WOHNI',
'KÜCHE',
]
var imageSrc = [
'./felix.png',
'./tobias.png',
'./andre.png',
];
// Bad, Küche, Wohni
var pathRooms = [];
function drawBackground(ctx, radius) {
sliceToColor.forEach(function (color, index) {
drawSlice(index, sliceToColor.length, color);
});
}
function drawSlice(num, total, color) {
var sliceSize = 360 / total;
var start = Math.PI * 2 * (((num - 1) * sliceSize) / 360);
var end = Math.PI * 2 * ((num * sliceSize) / 360);
let path = new Path2D();
ctx.beginPath(path)
path.moveTo(0, 0);
path.arc(0, 0, radius, start + 0.52, end + 0.52, false);
path.lineTo(0, 0);
ctx.fillStyle = color;
path.closePath();
if (pathRooms.length < total) {
pathRooms.push(path);
}
ctx.fill(path);
}
var secondsLoop = 3 * 7 * 24 * 60 * 60;
function degrees_to_radians(degrees) {
var pi = Math.PI;
return degrees * (pi/180);
}
function drawPeople() {
var now = Date.now() / 1000 - 4 * 60 * 60 * 24;
var part = now % secondsLoop;
var ang;
var num;
ctx.font = radiusPeople*0.15 + "px arial";
ctx.textBaseline="middle";
ctx.textAlign="center";
ctx.fillStyle = 'black';
var sliceSize = 360 / sliceToColor.length;
for(num = 0; num < sliceToColor.length; num++){
ang = Math.PI * 2 * ((num * sliceSize) / 360);
ang += degrees_to_radians((360 / secondsLoop) * part);
ctx.rotate(ang);
ctx.translate(0, -radiusPeople*0.85);
ctx.rotate(-ang);
ctx.drawImage(images[num], 0 - images[num].width / 2, 0 - images[num].height / 2, images[num].width, images[num].height);
ctx.rotate(ang);
ctx.translate(0, radiusPeople*0.85);
ctx.rotate(-ang);
}
}
function drawRoomNames() {
var ang;
var num;
var radiusCaptions = radiusPeople * 1.3;
ctx.font = radiusCaptions*0.15 + "px arial";
ctx.textBaseline="middle";
ctx.textAlign="center";
ctx.fillStyle = 'black';
var sliceSize = 360 / sliceToColor.length;
for(num = 0; num < sliceToColor.length; num++){
ang = Math.PI * 2 * ((num * sliceSize + sliceSize / 2) / 360);
ctx.rotate(ang);
ctx.translate(0, -radiusCaptions*0.85);
ctx.rotate(-ang);
ctx.fillText(sliceToRoom[num], 0, 0);
ctx.rotate(ang);
ctx.translate(0, radiusCaptions*0.85);
ctx.rotate(-ang);
}
}
function init() {
if (!isImgLoaded) {
return;
}
w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
radius = Math.min(w, h) * 2;
radiusPeople = radius / 6;
canvas.width = w;
canvas.height = h;
ctx.translate(w / 2, h / 2);
isInitialized = true;
drawBackground();
drawRoomNames();
drawPeople();
canvas.addEventListener('mousemove', e => {
let bound = canvas.getBoundingClientRect();
let x = e.pageX - bound.top;
let y = e.pageY - bound.left;
activeRoom = false;
pathRooms.forEach((path, index) => {
if (ctx.isPointInPath(path, x, y)) {
activeRoom = index;
}
});
});
}
window.onresize = init;
var isInitialized = false;
var isImgLoaded = false;
var images = imageSrc.map(function(){return false;});
function preloadImages() {
imageSrc.forEach(function(imgSrc, index) {
var newImg = new Image();
newImg.indexSrc = index;
newImg.onload = function() {
var factor = (newImg.width / radius) * 13;
newImg.width = newImg.width / factor;
newImg.height = newImg.height / factor;
images[index] = newImg;
isImgLoaded = images.reduce(function(acc, cur) {
return acc && cur;
}, true)
init();
};
newImg.src = imgSrc;
});
}
preloadImages();
setInterval(function() {
if (!isInitialized) {
return;
}
drawBackground();
drawRoomNames();
drawPeople();
}, 1000);
})();

BIN
static/pp/tobias.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 KiB