/* ===================================================================== bild-vorbereiten.js — ein Bild im Browser verkleinern, BEVOR es hochlädt WOZU Serverseitig bräuchte es eine Bildbibliothek (sharp o.ä.) -- eine neue Abhängigkeit mit nativem Code, installiert in einem Verzeichnis, an das Claudian nicht herankommt. Der Browser kann das ohnehin: Canvas skaliert und kodiert seit jeher. Nebeneffekt: schon der Upload wird kleiner -- wer vom Handy ein 8-MB-Foto schickt, wartet sonst am Mobilfunknetz. Bis 28.08.2026 lag diese Funktion NUR inline in der Verwaltung (verwaltung.html). Das öffentliche Stimmen-Formular (stimmen.js) lud Profilbilder dagegen ROH hoch -- so landete ein 1,1-MB-Avatar auf dem Server und wurde jedem Besucher der Startseite ausgeliefert. Deshalb liegt die Funktion jetzt hier, EINMAL, und wird von beiden benutzt. MASS PRO EINSATZORT Die größte Breite ist ein Parameter, kein fester Wert: Die Verwaltung zeigt Event-/Teambilder groß (1600 reicht für jede Anzeige dort), ein Profil-Avatar erscheint dagegen nur handgroß -- 512 ist da schon großzügig und spart gegenüber 1600 ein Vielfaches. WAS BEWUSST NICHT ANGETASTET WIRD Kleine PNGs bleiben PNG. Ein Logo mit durchsichtigem Hintergrund würde als JPEG einen schwarzen Kasten bekommen -- JPEG kennt keine Transparenz. Die Grenze liegt bei 400 KB: Darunter ist es wahrscheinlich eine Grafik, darüber praktisch immer ein Foto. ===================================================================== */ (function () { const STANDARD = { maxBreite: 1600, // reicht für die großen Anzeigen in der Verwaltung qualitaet: 0.86, pngGrenze: 400 * 1024, // kleine PNGs (Grafiken/Logos) unangetastet lassen }; /** * @param {File} datei die vom Nutzer gewählte Datei * @param {object} [opt] {maxBreite, qualitaet, pngGrenze} * @returns {Promise} verkleinerte Datei -- oder das Original, wenn * sich Verkleinern nicht lohnt oder etwas schiefgeht. */ async function bildVorbereiten(datei, opt) { const maxBreite = opt?.maxBreite ?? STANDARD.maxBreite; const qualitaet = opt?.qualitaet ?? STANDARD.qualitaet; const pngGrenze = opt?.pngGrenze ?? STANDARD.pngGrenze; try { if (!datei || !datei.type.startsWith("image/")) return datei; const istPng = datei.type === "image/png"; const bild = await new Promise((fertig, schief) => { const b = new Image(); b.onload = () => fertig(b); b.onerror = schief; b.src = URL.createObjectURL(datei); }); const zuBreit = bild.naturalWidth > maxBreite; const grossesPng = istPng && datei.size > pngGrenze; /* Nichts zu tun: kleines Bild, richtiges Format. Dann bleibt die Datei unverändert -- kein Qualitätsverlust ohne Gegenwert. */ if (!zuBreit && !grossesPng) { URL.revokeObjectURL(bild.src); return datei; } const breite = Math.min(bild.naturalWidth, maxBreite); const hoehe = Math.round((bild.naturalHeight / bild.naturalWidth) * breite); const flaeche = document.createElement("canvas"); flaeche.width = breite; flaeche.height = hoehe; const stift = flaeche.getContext("2d"); /* Ohne diese beiden Zeilen skaliert der Browser hart und die Kanten fransen aus. */ stift.imageSmoothingEnabled = true; stift.imageSmoothingQuality = "high"; /* Große PNGs werden zu JPEG -- die haben keinen durchsichtigen Hintergrund, sonst wären sie nicht so groß. Sicherheitshalber wird trotzdem weiß unterlegt, damit aus einer eventuellen Transparenz kein schwarzer Kasten wird. */ if (grossesPng) { stift.fillStyle = "#ffffff"; stift.fillRect(0, 0, breite, hoehe); } stift.drawImage(bild, 0, 0, breite, hoehe); URL.revokeObjectURL(bild.src); const typ = (grossesPng || datei.type === "image/jpeg") ? "image/jpeg" : datei.type; const klotz = await new Promise((fertig) => flaeche.toBlob(fertig, typ, qualitaet)); /* Wurde es NICHT kleiner, bleibt das Original. Bei bereits gut komprimierten Dateien kann erneutes Kodieren sonst sogar zulegen -- und Qualität kosten für nichts. */ if (!klotz || klotz.size >= datei.size) return datei; const name = datei.name.replace(/\.[^.]+$/, "") + (typ === "image/jpeg" ? ".jpg" : datei.name.slice(datei.name.lastIndexOf("."))); return new File([klotz], name, { type: typ, lastModified: Date.now() }); } catch (e) { /* Geht irgendetwas schief, wird das Original hochgeladen. Ein fehlgeschlagenes Verkleinern darf niemals einen Upload verhindern. */ return datei; } } window.bildVorbereiten = bildVorbereiten; })();