feat(skins): skin Steam Big Picture / SteamOS et vidéo de démarrage
Some checks are pending
CI / windows (push) Waiting to run
Some checks are pending
CI / windows (push) Waiting to run
Thème CSS Loader Orbe (couleurs, police Archivo, fond orbe) avec noms de classes lisibles traduits par CSS Loader, outil de vérification contre l'interface Steam actuelle, rendu de l'orbe et de la vidéo.
This commit is contained in:
parent
088d550856
commit
9a7fca6b3c
13 changed files with 348 additions and 0 deletions
60
skins/steam/tools/art.html
Normal file
60
skins/steam/tools/art.html
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Orbe Steam art</title>
|
||||
<style>
|
||||
/* Rendu image par image (pilote : tools/render.py). ?mode=bg : fond transparent 1920×1080 ; ?mode=boot : vidéo de démarrage 1280×800. */
|
||||
@font-face { font-family: Archivo; src: url(../Orbe/fonts/Archivo.woff2) format('woff2'); font-weight: 100 900; }
|
||||
html, body { margin: 0; background: transparent; overflow: hidden; }
|
||||
body.boot { background: #0a0a0a; }
|
||||
canvas { position: absolute; inset: 0; }
|
||||
.mark { position: absolute; left: 0; right: 0; top: 50%; text-align: center; font: 900 120px/1 Archivo; letter-spacing: -.05em; color: #f5f5f0; opacity: 0; }
|
||||
.mark span { color: #d3fd50; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="cv"></canvas>
|
||||
<div class="mark" id="mark">orbe<span>.</span></div>
|
||||
<script>
|
||||
const mode = new URLSearchParams(location.search).get('mode') || 'bg'
|
||||
const W = mode === 'boot' ? 1280 : 1920, H = mode === 'boot' ? 800 : 1080
|
||||
const cv = document.getElementById('cv'), ctx = cv.getContext('2d'), mark = document.getElementById('mark')
|
||||
cv.width = W; cv.height = H; cv.style.width = W + 'px'; cv.style.height = H + 'px'
|
||||
if (mode === 'boot') document.body.className = 'boot'
|
||||
|
||||
/* Même sphère de Fibonacci que le launcher et le site ; départ dispersé (graine fixe) pour l'arrivée des particules */
|
||||
const N = 620, pts = []
|
||||
let s = 7; const rnd = () => (s = (s * 16807) % 2147483647) / 2147483647
|
||||
for (let i = 0; i < N; i++) {
|
||||
const y = 1 - (i / (N - 1)) * 2, r = Math.sqrt(1 - y * y), th = i * 2.39996323
|
||||
const a = rnd() * 6.283, d = 1.6 + rnd() * 2.2
|
||||
pts.push({ x: Math.cos(th) * r, y, z: Math.sin(th) * r, hot: i % 9 === 0, sx: Math.cos(a) * d, sy: Math.sin(a) * d, lag: rnd() * .35 })
|
||||
}
|
||||
const ease = t => 1 - Math.pow(1 - Math.min(1, Math.max(0, t)), 3)
|
||||
|
||||
/* t en secondes */
|
||||
window.render = t => {
|
||||
const boot = mode === 'boot'
|
||||
const R = boot ? 250 : 430, cx = boot ? W / 2 : W * .74, cy = boot ? H / 2 - 40 : H * .5
|
||||
const a = .6 + t * .35, b = Math.sin(t * .4) * .3, ca = Math.cos(a), sa = Math.sin(a), cb = Math.cos(b), sb = Math.sin(b)
|
||||
/* vidéo : arrivée 0 → 1,6 s, l'orbe s'efface derrière le mot (2,2 → 3 s), fondu au noir à la fin (3,4 → 4 s) */
|
||||
const fade = boot ? 1 - ease((t - 3.4) / .6) : 1, dim = boot ? 1 - .7 * ease((t - 2.2) / .8) : 1
|
||||
ctx.clearRect(0, 0, W, H)
|
||||
for (const p of pts) {
|
||||
const k = boot ? ease((t - p.lag) / 1.25) : 1
|
||||
const x1 = p.x * ca - p.z * sa, z1 = p.x * sa + p.z * ca, y1 = p.y * cb - z1 * sb, z2 = p.y * sb + z1 * cb, d = (z2 + 1.4) / 2.4
|
||||
const px = p.sx + (x1 - p.sx) * k, py = p.sy + (y1 - p.sy) * k
|
||||
const al = (p.hot ? .2 + d * .7 : .06 + d * .42) * fade * dim * (boot ? Math.min(1, k * 1.5) : 1)
|
||||
ctx.fillStyle = p.hot ? `rgba(211,253,80,${al})` : `rgba(243,243,238,${al})`
|
||||
ctx.beginPath(); ctx.arc(cx + px * R, cy + py * R, (p.hot ? 2.4 : 1.5) * (boot ? 1.4 : 2) * (.6 + d * .7), 0, 6.283); ctx.fill()
|
||||
}
|
||||
if (boot) {
|
||||
const m = ease((t - 1.7) / .7) * fade
|
||||
mark.style.opacity = m; mark.style.transform = `translateY(${-50 + (1 - m) * 12}%)`
|
||||
}
|
||||
}
|
||||
render(0)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
16
skins/steam/tools/check.py
Normal file
16
skins/steam/tools/check.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Vérifie que chaque classe lisible du skin (module_Nom) est connue de CSS Loader et existe dans l'interface Steam actuelle.
|
||||
Usage : check.py <map.json de api.deckthemes.com/stable.json> <sp.css du paquet steamui_websrc_all>"""
|
||||
import json, re, sys
|
||||
from pathlib import Path
|
||||
|
||||
mapping, sp = json.load(open(sys.argv[1])), open(sys.argv[2]).read()
|
||||
alias = {a: v[-1] for v in mapping.values() for a in v}
|
||||
bad = 0
|
||||
for f in sorted((Path(__file__).parent.parent / "Orbe").glob("*.css")):
|
||||
for name in sorted(set(re.findall(r"\.([a-z]+_[A-Za-z]+)\b", f.read_text()))):
|
||||
cur = alias.get(name)
|
||||
ok = cur is not None and re.search(r"\." + re.escape(cur) + r"(?![\w-])", sp)
|
||||
bad += not ok
|
||||
print("%-4s %-12s %-45s %s" % ("ok" if ok else "ABS", f.name, name, cur or "-"))
|
||||
sys.exit(1 if bad else 0)
|
||||
45
skins/steam/tools/render.py
Normal file
45
skins/steam/tools/render.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Rend tools/art.html dans un Chromium headless (protocole CDP) : fond orbe (PNG transparent) et vidéo de démarrage (images PNG).
|
||||
Usage : render.py <url de art.html> <cdp ws http://hôte:port> <dossier de sortie>"""
|
||||
import base64, json, os, sys, time, urllib.request
|
||||
import websocket
|
||||
|
||||
url, cdp, out = sys.argv[1:4]
|
||||
os.makedirs(out + "/frames", exist_ok=True)
|
||||
t = json.load(urllib.request.urlopen(urllib.request.Request(cdp + "/json/new?about:blank", method="PUT")))
|
||||
ws, n = websocket.create_connection(t["webSocketDebuggerUrl"], timeout=60), 0
|
||||
|
||||
|
||||
def cmd(method, **params):
|
||||
global n
|
||||
n += 1
|
||||
ws.send(json.dumps({"id": n, "method": method, "params": params}))
|
||||
while True:
|
||||
m = json.loads(ws.recv())
|
||||
if m.get("id") == n:
|
||||
return m.get("result", {})
|
||||
|
||||
|
||||
def shot(path, w, h):
|
||||
png = cmd("Page.captureScreenshot", format="png", clip={"x": 0, "y": 0, "width": w, "height": h, "scale": 1})["data"]
|
||||
open(path, "wb").write(base64.b64decode(png))
|
||||
|
||||
|
||||
def load(mode, w, h):
|
||||
cmd("Emulation.setDeviceMetricsOverride", width=w, height=h, deviceScaleFactor=1, mobile=False)
|
||||
cmd("Emulation.setDefaultBackgroundColorOverride", color={"r": 0, "g": 0, "b": 0, "a": 0})
|
||||
cmd("Page.navigate", url=url + "?mode=" + mode)
|
||||
time.sleep(1.5)
|
||||
cmd("Runtime.evaluate", expression="document.fonts.ready", awaitPromise=True)
|
||||
|
||||
|
||||
cmd("Page.enable")
|
||||
load("bg", 1920, 1080)
|
||||
cmd("Runtime.evaluate", expression="render(2)")
|
||||
shot(out + "/orb.png", 1920, 1080)
|
||||
|
||||
load("boot", 1280, 800)
|
||||
for i in range(120): # 4 s à 30 i/s
|
||||
cmd("Runtime.evaluate", expression="render(%f)" % (i / 30))
|
||||
shot("%s/frames/%03d.png" % (out, i), 1280, 800)
|
||||
cmd("Page.close")
|
||||
Loading…
Add table
Add a link
Reference in a new issue