v1.0.0 — redesign complet charte graphique V2
- 8 états météo : arc-en-ciel, ensoleillé, brumeux, nuageux, pluvieux, orageux, tempête, fin du monde - Fonts : Fredoka (titres) + Nunito (texte), palette V2 (bleu #3B82F6, orange #F59E0B, rouge #EF4444) - Nav flottante : Météo / Radar / Bilan / Paramètres (La Carte supprimée) - Thème clair / sombre (toggle dans Paramètres) - Icône app : papier quadrillé + 0 rouge manuscrit - Illustrations météo CSS pures (soleil, nuages, pluie, éclair, champignon nucléaire, arc-en-ciel) - Panel admin centralisé dans Paramètres (flash, classe, évaluations, météo, membres) - Bottom nav flottante avec safe-area pour ne plus toucher la nav système Android - Suppression des anciennes vues (cards.ts, map.ts, plus.ts, exam-form.ts)
This commit is contained in:
parent
8c79476d96
commit
2f2f363650
23 changed files with 1679 additions and 1715 deletions
|
|
@ -1,6 +1,5 @@
|
|||
/**
|
||||
* Génère des icônes PWA simples (dégradé bleu + pastille blanche).
|
||||
* Usage : node scripts/gen-icons.mjs
|
||||
* Génère l'icône Météo Exam V2 : papier quadrillé + 0 rouge manuscrit.
|
||||
*/
|
||||
import { writeFileSync, mkdirSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
|
|
@ -51,51 +50,73 @@ function encodePng(width, height, rgba) {
|
|||
|
||||
function drawIcon(size) {
|
||||
const buf = Buffer.alloc(size * size * 4)
|
||||
const c0 = [56, 189, 248]
|
||||
const c1 = [99, 102, 241]
|
||||
const paperR = 248, paperG = 250, paperB = 252 // #F8FAFC
|
||||
const gridR = 185, gridG = 213, gridB = 246 // #B9D5F6
|
||||
const redR = 239, redG = 68, redB = 68 // #EF4444
|
||||
const lineR = 236, lineG = 73, lineB = 73 // #EC4949
|
||||
const cx = size / 2
|
||||
const cy = size / 2
|
||||
const rInner = size * 0.3
|
||||
const rPad = size * 0.16
|
||||
const gridSize = size / 10
|
||||
|
||||
for (let y = 0; y < size; y++) {
|
||||
for (let x = 0; x < size; x++) {
|
||||
const i = (y * size + x) * 4
|
||||
const dx = x - cx
|
||||
const dy = y - cy
|
||||
const dist = Math.sqrt(dx * dx + dy * dy)
|
||||
|
||||
// Fond arrondi : on coupe les coins
|
||||
let transparent = false
|
||||
if (x < rPad && y < rPad) transparent = Math.hypot(rPad - x, rPad - y) > rPad
|
||||
else if (x >= size - rPad && y < rPad) transparent = Math.hypot(x - (size - rPad), rPad - y) > rPad
|
||||
else if (x < rPad && y >= size - rPad) transparent = Math.hypot(rPad - x, y - (size - rPad)) > rPad
|
||||
else if (x >= size - rPad && y >= size - rPad) transparent = Math.hypot(x - (size - rPad), y - (size - rPad)) > rPad
|
||||
// Fond papier
|
||||
let r = paperR, g = paperG, b = paperB, a = 255
|
||||
|
||||
if (transparent) {
|
||||
buf[i + 3] = 0
|
||||
continue
|
||||
// Quadrillage
|
||||
const gx = x % gridSize
|
||||
const gy = y % gridSize
|
||||
const nearVx = gx < 1.5 || gx > gridSize - 1.5
|
||||
const nearHy = gy < 1.5 || gy > gridSize - 1.5
|
||||
if (nearVx || nearHy) {
|
||||
r = gridR; g = gridG; b = gridB
|
||||
}
|
||||
|
||||
const t = (x + y) / (size * 2)
|
||||
let r = c0[0] + (c1[0] - c0[0]) * t
|
||||
let g = c0[1] + (c1[1] - c0[1]) * t
|
||||
let b = c0[2] + (c1[2] - c0[2]) * t
|
||||
|
||||
if (dist < rInner) {
|
||||
r = 255
|
||||
g = 255
|
||||
b = 255
|
||||
} else if (dist < rInner + size * 0.025) {
|
||||
r = Math.min(255, r + 50)
|
||||
g = Math.min(255, g + 50)
|
||||
b = Math.min(255, b + 50)
|
||||
// Marge rouge verticale (style cahier)
|
||||
if (x < size * 0.08 && x > size * 0.05) {
|
||||
r = lineR; g = lineG; b = lineB
|
||||
}
|
||||
|
||||
buf[i] = Math.round(r)
|
||||
buf[i + 1] = Math.round(g)
|
||||
buf[i + 2] = Math.round(b)
|
||||
buf[i + 3] = 255
|
||||
// Dessiner le "0" rouge (ellipse inclinée)
|
||||
const dx = (x - cx) / (size * 0.22)
|
||||
const dy = (y - cy) / (size * 0.3)
|
||||
const dist0 = Math.sqrt(dx * dx + dy * dy)
|
||||
|
||||
// Anneau du 0
|
||||
if (dist0 > 0.7 && dist0 < 1.15) {
|
||||
r = redR; g = redG; b = redB
|
||||
}
|
||||
|
||||
// Rayons du soleil (petits traits autour du 0)
|
||||
const angle = Math.atan2(y - cy, x - cx)
|
||||
const rayDist = Math.sqrt((x - cx) ** 2 + (y - cy) ** 2)
|
||||
const rayOuter = size * 0.42
|
||||
const rayInner = size * 0.36
|
||||
if (rayDist > rayInner && rayDist < rayOuter) {
|
||||
// 8 rayons
|
||||
const rayAngle = (Math.round(angle / (Math.PI / 4)) * Math.PI) / 4
|
||||
const perpDist = Math.abs((x - cx) * Math.sin(rayAngle) - (y - cy) * Math.cos(rayAngle))
|
||||
if (perpDist < size * 0.02) {
|
||||
r = redR; g = redG; b = redB
|
||||
}
|
||||
}
|
||||
|
||||
// Soulignement double en bas
|
||||
const underY1 = cy + size * 0.38
|
||||
const underY2 = cy + size * 0.42
|
||||
if (Math.abs(y - underY1) < size * 0.015 && x > cx - size * 0.3 && x < cx + size * 0.3) {
|
||||
r = redR; g = redG; b = redB
|
||||
}
|
||||
if (Math.abs(y - underY2) < size * 0.015 && x > cx - size * 0.28 && x < cx + size * 0.32) {
|
||||
r = redR; g = redG; b = redB
|
||||
}
|
||||
|
||||
buf[i] = r
|
||||
buf[i + 1] = g
|
||||
buf[i + 2] = b
|
||||
buf[i + 3] = a
|
||||
}
|
||||
}
|
||||
return buf
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue