Die Anmeldung bekommt die Biene in Schraegsicht
Die 3/4-Ansicht ist unter 64 px unbrauchbar und deshalb genau hier richtig: Auf dem Anmeldebildschirm ist Platz, und man wartet ohnehin auf den Anbieter. Sie schwebt sechs Sekunden lang zwei Pixel weit — eher ein Atmen als eine Bewegung, und bei prefers-reduced-motion steht sie still. Gerechnet wird sie in tools/biene.py aus demselben Raster wie die anderen Fassungen.
This commit is contained in:
1 parent
3f8b98b869
commit
ff413864a9
6 files changed
+113
-3
No files matched your search
@@ -201,6 +201,7 @@ python3 tools/biene.py
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `web/img/biene.svg` | Favicon |
|
| `web/img/biene.svg` | Favicon |
|
||||||
| `web/js/biene.js` | Menüleiste des Schreibtischs (`BIENE_SVG`), System-Fenster und Startlauf (`BIENE_ASCII`) |
|
| `web/js/biene.js` | Menüleiste des Schreibtischs (`BIENE_SVG`), System-Fenster und Startlauf (`BIENE_ASCII`) |
|
||||||
|
| `web/img/biene-3d.svg`, `-3d-256.png` | Anmeldebildschirm — die Schrägsicht |
|
||||||
| `web/img/biene-256.png`, `-32.png` | Doku und Vorschau |
|
| `web/img/biene-256.png`, `-32.png` | Doku und Vorschau |
|
||||||
|
|
||||||
In die HoneyBar ist dieselbe SVG-Zeile **einkopiert** — sie ist eine einzelne
|
In die HoneyBar ist dieselbe SVG-Zeile **einkopiert** — sie ist eine einzelne
|
||||||
@@ -218,6 +219,18 @@ die Farbe mit — die Biene nicht. Ein Zeichen, das die Farbe wechselt, ist ein
|
|||||||
Symbol wie *wolke*; eines, das seine Farbe behält, ist ein Wappen. Der eine
|
Symbol wie *wolke*; eines, das seine Farbe behält, ist ein Wappen. Der eine
|
||||||
warme Fleck liest sich als „das hier ist das System selbst".
|
warme Fleck liest sich als „das hier ist das System selbst".
|
||||||
|
|
||||||
|
### Einmal groß: die Schrägsicht
|
||||||
|
|
||||||
|
Auf dem Anmeldebildschirm steht dieselbe Biene als **Würfel in 2:1-Schrägsicht**
|
||||||
|
— gerechnet, nicht gemalt: Das Skript legt Deckel und die zwei sichtbaren
|
||||||
|
Wangen an und malt Streifen, Augen und Flügel darauf. Die Streifen laufen
|
||||||
|
parallel zur linken Deckelkante und setzen sich auf der rechten Wange als
|
||||||
|
senkrechte Bänder fort, damit sie über den Knick hinweg zusammenpassen.
|
||||||
|
|
||||||
|
Sie ist **unter 64 px unbrauchbar** — zu viele Kanten auf zu wenig Pixeln —,
|
||||||
|
und genau deshalb steht sie dort: Der Anmeldebildschirm ist die einzige Stelle
|
||||||
|
mit Platz und Zeit. In den Leisten bleibt die Seitenansicht.
|
||||||
|
|
||||||
### Und aus Schriftzeichen
|
### Und aus Schriftzeichen
|
||||||
|
|
||||||
Im System-Fenster und über dem Startlauf steht dieselbe Biene aus `#`, `:` und
|
Im System-Fenster und über dem Startlauf steht dieselbe Biene aus `#`, `:` und
|
||||||
|
|||||||
+77
-2
@@ -59,6 +59,76 @@ BIENE = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# ── Die 3/4-Ansicht ──────────────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# Nur für den Anmeldebildschirm: groß, einmal am Tag, mit Zeit zum Hinsehen.
|
||||||
|
# Unter 64 px zerfällt sie — zu viele Kanten auf zu wenig Pixeln.
|
||||||
|
#
|
||||||
|
# Ein Würfel hat in Pixelkunst die Steigung 2:1: zwei Pixel nach rechts, einer
|
||||||
|
# nach unten. Von Hand wird das krumm, also werden die drei sichtbaren Flächen
|
||||||
|
# gerechnet — Deckel als Raute, darunter zwei Wangen. Wichtig ist, **zeilenweise**
|
||||||
|
# zu rechnen: Wer die Raute Punkt für Punkt setzt, lässt Löcher, weil zwei
|
||||||
|
# Schritte nach rechts nur einen nach unten kosten.
|
||||||
|
def wuerfel(breite, wand):
|
||||||
|
hoehe = breite // 2
|
||||||
|
mitte = breite / 2
|
||||||
|
deckel = {
|
||||||
|
(x, y)
|
||||||
|
for y in range(hoehe)
|
||||||
|
for x in range(breite)
|
||||||
|
if abs(x + 0.5 - mitte) / 2 + abs(y + 0.5 - hoehe / 2) <= hoehe / 2
|
||||||
|
}
|
||||||
|
unterkante = {}
|
||||||
|
for (x, y) in deckel:
|
||||||
|
unterkante[x] = max(unterkante.get(x, -1), y)
|
||||||
|
flaechen = {q: "T" for q in deckel}
|
||||||
|
for x, yb in unterkante.items():
|
||||||
|
for t in range(1, wand + 1):
|
||||||
|
flaechen[(x, yb + t)] = "V" if x < mitte else "R" # vorn / rechts
|
||||||
|
return flaechen, unterkante, mitte
|
||||||
|
|
||||||
|
|
||||||
|
def schraeg(breite=22, wand=10, dx=5, dy=9, n=32, band=5):
|
||||||
|
flaechen, unterkante, mitte = wuerfel(breite, wand)
|
||||||
|
g = [["." for _ in range(n)] for _ in range(n)]
|
||||||
|
|
||||||
|
def setz(x, y, c):
|
||||||
|
if 0 <= x + dx < n and 0 <= y + dy < n:
|
||||||
|
g[y + dy][x + dx] = c
|
||||||
|
|
||||||
|
# Die Streifen laufen parallel zur linken Deckelkante (u = x - 2y) und
|
||||||
|
# setzen sich auf der rechten Wange als senkrechte Bänder fort: dieselbe
|
||||||
|
# Rechnung, ausgewertet an der Kante, an der die Wange beginnt. Deshalb
|
||||||
|
# passen sie über den Knick hinweg zusammen.
|
||||||
|
streifig = lambda x, y: ((x - 2 * y) // band) % 2 == 0
|
||||||
|
for (x, y), art in sorted(flaechen.items()):
|
||||||
|
if art == "T":
|
||||||
|
setz(x, y, "S" if streifig(x, y) else "L")
|
||||||
|
elif art == "R":
|
||||||
|
setz(x, y, "s" if streifig(x, unterkante[x]) else "y")
|
||||||
|
else:
|
||||||
|
setz(x, y, "Y")
|
||||||
|
|
||||||
|
# Augen: Rechtecke in Flächenkoordinaten — auf dem Schirm werden daraus
|
||||||
|
# Parallelogramme, genau wie beim Vorbild.
|
||||||
|
for ax in (2, 7):
|
||||||
|
for x in range(ax, ax + 3):
|
||||||
|
if x in unterkante:
|
||||||
|
for t in (3, 4, 5):
|
||||||
|
setz(x, unterkante[x] + t, "E")
|
||||||
|
if ax + 1 in unterkante:
|
||||||
|
setz(ax + 1, unterkante[ax + 1] + 4, "B")
|
||||||
|
|
||||||
|
for k in range(3): # zwei Flügel in derselben Schräge
|
||||||
|
for b in range(6):
|
||||||
|
setz(3 + 2 * k + b, -2 + k, "W")
|
||||||
|
setz(10 + 2 * k + b, -4 + k, "w")
|
||||||
|
for k in range(2): # kurze Fühler am Deckelrand
|
||||||
|
setz(int(mitte) - 5 - k, -3 - k, "D")
|
||||||
|
setz(int(mitte) + 6 + k, -5 - k, "D")
|
||||||
|
return ["".join(z) for z in g]
|
||||||
|
|
||||||
|
|
||||||
def svg(raster):
|
def svg(raster):
|
||||||
"""Waagerechte Läufe gleicher Farbe werden ein Rechteck — dieselbe Idee wie
|
"""Waagerechte Läufe gleicher Farbe werden ein Rechteck — dieselbe Idee wie
|
||||||
bei den Pixelsymbolen in web/js/glyphs.js, nur mit Farbe statt currentColor.
|
bei den Pixelsymbolen in web/js/glyphs.js, nur mit Farbe statt currentColor.
|
||||||
@@ -80,7 +150,8 @@ def svg(raster):
|
|||||||
r, g, b = FARBEN[z]
|
r, g, b = FARBEN[z]
|
||||||
teile.append(f'<rect x="{x}" y="{y}" width="{lauf}" height="1" fill="#{r:02x}{g:02x}{b:02x}"/>')
|
teile.append(f'<rect x="{x}" y="{y}" width="{lauf}" height="1" fill="#{r:02x}{g:02x}{b:02x}"/>')
|
||||||
x += lauf
|
x += lauf
|
||||||
return ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" '
|
n = len(raster)
|
||||||
|
return (f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {n} {n}" '
|
||||||
'shape-rendering="crispEdges" aria-hidden="true">' + "".join(teile) + "</svg>")
|
'shape-rendering="crispEdges" aria-hidden="true">' + "".join(teile) + "</svg>")
|
||||||
|
|
||||||
|
|
||||||
@@ -127,6 +198,10 @@ def main():
|
|||||||
zeichen = text(BIENE)
|
zeichen = text(BIENE)
|
||||||
|
|
||||||
(WURZEL / "web/img/biene.svg").write_text(bild + "\n")
|
(WURZEL / "web/img/biene.svg").write_text(bild + "\n")
|
||||||
|
# Für die Anmeldung: einmal groß, in Schrägsicht.
|
||||||
|
drei = schraeg()
|
||||||
|
(WURZEL / "web/img/biene-3d.svg").write_text(svg(drei) + "\n")
|
||||||
|
png(drei, 8, WURZEL / "web/img/biene-3d-256.png")
|
||||||
png(BIENE, 16, WURZEL / "web/img/biene-256.png")
|
png(BIENE, 16, WURZEL / "web/img/biene-256.png")
|
||||||
png(BIENE, 2, WURZEL / "web/img/biene-32.png")
|
png(BIENE, 2, WURZEL / "web/img/biene-32.png")
|
||||||
|
|
||||||
@@ -144,7 +219,7 @@ def main():
|
|||||||
f"export const BIENE_ASCII = {json.dumps(zeichen)};\n"
|
f"export const BIENE_ASCII = {json.dumps(zeichen)};\n"
|
||||||
)
|
)
|
||||||
print(text(BIENE))
|
print(text(BIENE))
|
||||||
print(f"\nweb/img/biene.svg ({len(bild)} Zeichen), biene-256.png, biene-32.png, web/js/biene.js")
|
print(f"\nweb/img/biene.svg ({len(bild)} Zeichen), biene-3d.svg, drei PNG, web/js/biene.js")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+19
-1
@@ -944,8 +944,26 @@ body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Die Biene über dem Anmeldeknopf, in Schrägsicht. Sie schwebt ein wenig —
|
||||||
|
sechs Sekunden für zwei Pixel, also eher ein Atmen als eine Bewegung. Wer
|
||||||
|
Bewegung abgestellt hat, bekommt sie ruhig. */
|
||||||
|
.login-biene {
|
||||||
|
display: block;
|
||||||
|
width: clamp(96px, 22vw, 132px);
|
||||||
|
height: auto;
|
||||||
|
margin: 22px 0 0;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
animation: schweben 6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes schweben {
|
||||||
|
50% { transform: translateY(-4px); }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.login-biene { animation: none; }
|
||||||
|
}
|
||||||
|
|
||||||
.login-box {
|
.login-box {
|
||||||
margin-top: 26px;
|
margin-top: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" shape-rendering="crispEdges" aria-hidden="true"><rect x="23" y="3" width="1" height="1" fill="#3b2412"/><rect x="22" y="4" width="1" height="1" fill="#3b2412"/><rect x="10" y="5" width="1" height="1" fill="#3b2412"/><rect x="15" y="5" width="6" height="1" fill="#d6d1be"/><rect x="11" y="6" width="1" height="1" fill="#3b2412"/><rect x="17" y="6" width="6" height="1" fill="#d6d1be"/><rect x="8" y="7" width="6" height="1" fill="#f5f2e7"/><rect x="19" y="7" width="6" height="1" fill="#d6d1be"/><rect x="10" y="8" width="6" height="1" fill="#f5f2e7"/><rect x="12" y="9" width="6" height="1" fill="#f5f2e7"/><rect x="13" y="10" width="4" height="1" fill="#ffd060"/><rect x="17" y="10" width="2" height="1" fill="#5c3a1c"/><rect x="11" y="11" width="3" height="1" fill="#5c3a1c"/><rect x="14" y="11" width="5" height="1" fill="#ffd060"/><rect x="19" y="11" width="2" height="1" fill="#5c3a1c"/><rect x="9" y="12" width="2" height="1" fill="#ffd060"/><rect x="11" y="12" width="5" height="1" fill="#5c3a1c"/><rect x="16" y="12" width="5" height="1" fill="#ffd060"/><rect x="21" y="12" width="2" height="1" fill="#5c3a1c"/><rect x="7" y="13" width="1" height="1" fill="#5c3a1c"/><rect x="8" y="13" width="5" height="1" fill="#ffd060"/><rect x="13" y="13" width="5" height="1" fill="#5c3a1c"/><rect x="18" y="13" width="5" height="1" fill="#ffd060"/><rect x="23" y="13" width="2" height="1" fill="#5c3a1c"/><rect x="5" y="14" width="5" height="1" fill="#5c3a1c"/><rect x="10" y="14" width="5" height="1" fill="#ffd060"/><rect x="15" y="14" width="5" height="1" fill="#5c3a1c"/><rect x="20" y="14" width="5" height="1" fill="#ffd060"/><rect x="25" y="14" width="2" height="1" fill="#5c3a1c"/><rect x="5" y="15" width="2" height="1" fill="#f2b33d"/><rect x="7" y="15" width="5" height="1" fill="#5c3a1c"/><rect x="12" y="15" width="5" height="1" fill="#ffd060"/><rect x="17" y="15" width="5" height="1" fill="#5c3a1c"/><rect x="22" y="15" width="3" height="1" fill="#ffd060"/><rect x="25" y="15" width="2" height="1" fill="#462b14"/><rect x="5" y="16" width="4" height="1" fill="#f2b33d"/><rect x="9" y="16" width="5" height="1" fill="#5c3a1c"/><rect x="14" y="16" width="5" height="1" fill="#ffd060"/><rect x="19" y="16" width="4" height="1" fill="#5c3a1c"/><rect x="23" y="16" width="2" height="1" fill="#d2952a"/><rect x="25" y="16" width="2" height="1" fill="#462b14"/><rect x="5" y="17" width="6" height="1" fill="#f2b33d"/><rect x="11" y="17" width="5" height="1" fill="#5c3a1c"/><rect x="16" y="17" width="5" height="1" fill="#ffd060"/><rect x="21" y="17" width="2" height="1" fill="#462b14"/><rect x="23" y="17" width="2" height="1" fill="#d2952a"/><rect x="25" y="17" width="2" height="1" fill="#462b14"/><rect x="5" y="18" width="2" height="1" fill="#f2b33d"/><rect x="7" y="18" width="2" height="1" fill="#181410"/><rect x="9" y="18" width="4" height="1" fill="#f2b33d"/><rect x="13" y="18" width="5" height="1" fill="#5c3a1c"/><rect x="18" y="18" width="1" height="1" fill="#ffd060"/><rect x="19" y="18" width="2" height="1" fill="#d2952a"/><rect x="21" y="18" width="2" height="1" fill="#462b14"/><rect x="23" y="18" width="2" height="1" fill="#d2952a"/><rect x="25" y="18" width="2" height="1" fill="#462b14"/><rect x="5" y="19" width="2" height="1" fill="#f2b33d"/><rect x="7" y="19" width="1" height="1" fill="#181410"/><rect x="8" y="19" width="1" height="1" fill="#63c7c0"/><rect x="9" y="19" width="1" height="1" fill="#181410"/><rect x="10" y="19" width="5" height="1" fill="#f2b33d"/><rect x="15" y="19" width="2" height="1" fill="#5c3a1c"/><rect x="17" y="19" width="1" height="1" fill="#462b14"/><rect x="18" y="19" width="3" height="1" fill="#d2952a"/><rect x="21" y="19" width="2" height="1" fill="#462b14"/><rect x="23" y="19" width="2" height="1" fill="#d2952a"/><rect x="25" y="19" width="2" height="1" fill="#462b14"/><rect x="5" y="20" width="2" height="1" fill="#f2b33d"/><rect x="7" y="20" width="3" height="1" fill="#181410"/><rect x="10" y="20" width="2" height="1" fill="#f2b33d"/><rect x="12" y="20" width="1" height="1" fill="#181410"/><rect x="13" y="20" width="3" height="1" fill="#f2b33d"/><rect x="16" y="20" width="2" height="1" fill="#462b14"/><rect x="18" y="20" width="3" height="1" fill="#d2952a"/><rect x="21" y="20" width="2" height="1" fill="#462b14"/><rect x="23" y="20" width="2" height="1" fill="#d2952a"/><rect x="25" y="20" width="2" height="1" fill="#462b14"/><rect x="5" y="21" width="4" height="1" fill="#f2b33d"/><rect x="9" y="21" width="1" height="1" fill="#181410"/><rect x="10" y="21" width="2" height="1" fill="#f2b33d"/><rect x="12" y="21" width="3" height="1" fill="#181410"/><rect x="15" y="21" width="1" height="1" fill="#f2b33d"/><rect x="16" y="21" width="2" height="1" fill="#462b14"/><rect x="18" y="21" width="3" height="1" fill="#d2952a"/><rect x="21" y="21" width="2" height="1" fill="#462b14"/><rect x="23" y="21" width="2" height="1" fill="#d2952a"/><rect x="25" y="21" Line truncated
|
||||||
|
After Width: | Height: | Size: 7.2 KiB |
@@ -21,6 +21,9 @@
|
|||||||
<!-- ── Anmeldung ──────────────────────────────────────────────────── -->
|
<!-- ── Anmeldung ──────────────────────────────────────────────────── -->
|
||||||
<div class="login" id="login" hidden>
|
<div class="login" id="login" hidden>
|
||||||
<pre class="boot-log" id="loginLog"></pre>
|
<pre class="boot-log" id="loginLog"></pre>
|
||||||
|
<!-- Einmal groß: die Schrägsicht. Sie ist unter 64 px unbrauchbar, und
|
||||||
|
hier ist Platz — man wartet ohnehin auf den Anbieter. -->
|
||||||
|
<img class="login-biene" src="./img/biene-3d.svg" alt="" width="128" height="128" />
|
||||||
<div class="login-box">
|
<div class="login-box">
|
||||||
<button class="login-btn" id="btnLogin">▸ <span id="loginLabel">Anmelden</span></button>
|
<button class="login-btn" id="btnLogin">▸ <span id="loginLabel">Anmelden</span></button>
|
||||||
<p class="login-note" id="loginNote"></p>
|
<p class="login-note" id="loginNote"></p>
|
||||||
|
|||||||
Reference in new issue
Block a user