Ein eigenes Bild bringt auch seinen Ausschnitt mit
Hochladen konnte man Plakat, Hintergrund und Schriftzug schon lange — der Knopf stand da, der Endpunkt nahm an. Nur ankommen tat nichts: die Ablage baute einen Schlüssel, den sie selbst nicht wiederfand (heute früh repariert). Was blieb, waren drei Lücken, die man erst sieht, wenn der Weg überhaupt funktioniert. **Der Ausschnitt.** Ein Plakat steht hochkant im Verhältnis 2∶3, ein Hintergrund liegt quer. Was man aussucht, passt selten von selbst — und den Rest beschnitt bisher der Browser beim Anzeigen: mittig. Jetzt wählt man selbst, zieht und vergrössert, und hochgeladen wird genau dieser Ausschnitt: 600×900 beim Plakat, 1920×1080 beim Hintergrund. Gerechnet wird im Browser; das spart die Leitung und dem Server eine Bildbibliothek. **Der Schriftzug nicht.** Er ist ein PNG mit durchsichtigem Grund. Durch dieselbe Mühle geschickt würde aus der Durchsicht ein weisses Rechteck — ein Aufkleber über dem Bild. Er geht unverändert hinaus, Byte für Byte. **Die Kachel merkte nichts.** Nach dem Hochladen fehlte der Anstoss nach aussen; die Oberfläche zeigte weiter das alte Bild, bis man sie neu lud. **Und zurück ging es gar nicht.** „Von Hand gesetzt" heisst auch: beim Auffrischen bleibt es stehen. Ohne Rückweg wäre ein Fehlgriff ein Dauerzustand — jetzt steht „Wieder automatisch" daneben, sobald etwas festgesetzt ist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
327e2e9195
commit
6dc7be9822
+73
-10
@@ -1,14 +1,28 @@
|
||||
import { bildAdresse, bildauswahl, bildSetzen, bildHochladen, type Bildkandidat } from './api';
|
||||
import { knopf, fehlerzeile, meldung } from './util';
|
||||
import {
|
||||
bildAdresse, bildauswahl, bildSetzen, bildHochladen, bildFreigeben, type Bildkandidat,
|
||||
} from './api';
|
||||
import { knopf, fehlerzeile, meldung, hinweisZeigen } from './util';
|
||||
import { icons } from './icons';
|
||||
import { beimSeitenwechselSchliessen } from './dialog';
|
||||
import { zuschneiden } from './bildzuschnitt';
|
||||
|
||||
type Bildart = 'poster' | 'backdrop' | 'logo';
|
||||
|
||||
const ARTEN: { art: Bildart; titel: string; verhaeltnis: string }[] = [
|
||||
{ art: 'poster', titel: 'Plakat', verhaeltnis: '2 / 3' },
|
||||
{ art: 'backdrop', titel: 'Hintergrund', verhaeltnis: '16 / 9' },
|
||||
{ art: 'logo', titel: 'Schriftzug', verhaeltnis: '16 / 9' },
|
||||
/** Was jede Bildart ist — und in welcher Form sie hochgeladen wird.
|
||||
*
|
||||
* **`zuschnitt: null` beim Schriftzug, und das ist kein Versehen.** Ein
|
||||
* Schriftzug ist ein PNG mit durchsichtigem Grund; durch die Zuschneidemühle
|
||||
* geschickt würde aus der Durchsicht ein weisses Rechteck, und er läge wie
|
||||
* ein Aufkleber über dem Bild. Er geht unverändert hinaus. */
|
||||
const ARTEN: {
|
||||
art: Bildart; titel: string; verhaeltnis: string;
|
||||
zuschnitt: { verhaeltnis: number; breite: number } | null;
|
||||
}[] = [
|
||||
{ art: 'poster', titel: 'Plakat', verhaeltnis: '2 / 3',
|
||||
zuschnitt: { verhaeltnis: 2 / 3, breite: 600 } },
|
||||
{ art: 'backdrop', titel: 'Hintergrund', verhaeltnis: '16 / 9',
|
||||
zuschnitt: { verhaeltnis: 16 / 9, breite: 1920 } },
|
||||
{ art: 'logo', titel: 'Schriftzug', verhaeltnis: '16 / 9', zuschnitt: null },
|
||||
];
|
||||
|
||||
/** Plakat, Hintergrund und Schriftzug von Hand wählen.
|
||||
@@ -122,10 +136,13 @@ export function bilderflaeche(
|
||||
|
||||
const bloecke = new Map<Bildart, HTMLElement>();
|
||||
|
||||
ARTEN.forEach(({ art: bildart, titel: name, verhaeltnis }) => {
|
||||
ARTEN.forEach(({ art: bildart, titel: name, verhaeltnis, zuschnitt }) => {
|
||||
const aktuell = bildart === 'poster' ? auswahl.current.poster
|
||||
: bildart === 'backdrop' ? auswahl.current.backdrop
|
||||
: auswahl.current.logo;
|
||||
const festgesetzt = bildart === 'poster' ? auswahl.current.posterPinned
|
||||
: bildart === 'backdrop' ? auswahl.current.backdropPinned
|
||||
: auswahl.current.logoPinned;
|
||||
|
||||
const passende = auswahl.candidates.filter(k => k.kind === bildart);
|
||||
|
||||
@@ -148,7 +165,8 @@ export function bilderflaeche(
|
||||
const kopfzeile = document.createElement('div');
|
||||
kopfzeile.className = 'knopfreihe';
|
||||
const u = document.createElement('h3');
|
||||
u.textContent = aktuell ? `${name} — eines ist gesetzt` : name;
|
||||
u.textContent = festgesetzt ? `${name} — von Hand gesetzt`
|
||||
: aktuell ? `${name} — eines ist gesetzt` : name;
|
||||
kopfzeile.appendChild(u);
|
||||
|
||||
// Eigenes Bild — ein verstecktes Dateifeld hinter einem Knopf, weil
|
||||
@@ -162,15 +180,60 @@ export function bilderflaeche(
|
||||
eigenes.onclick = () => feld.click();
|
||||
feld.onchange = async () => {
|
||||
const datei = feld.files?.[0];
|
||||
feld.value = ''; // Dieselbe Datei zweimal soll wieder auslösen.
|
||||
if (!datei) return;
|
||||
|
||||
// **Erst zuschneiden, dann hochladen** — ein Plakat steht hochkant,
|
||||
// ein Hintergrund liegt quer. Was nicht passt, beschnitt sonst der
|
||||
// Browser beim Anzeigen: mittig, und das trifft selten.
|
||||
let inhalt: Blob = datei;
|
||||
if (zuschnitt) {
|
||||
const ausschnitt = await zuschneiden(datei, {
|
||||
...zuschnitt, titel: `${name} zuschneiden`,
|
||||
});
|
||||
if (!ausschnitt) return; // Abgebrochen — das ist keine Meldung wert.
|
||||
inhalt = ausschnitt;
|
||||
}
|
||||
|
||||
eigenes.disabled = true;
|
||||
try { await bildHochladen(art, id, bildart, datei); await laden(); }
|
||||
catch (fehler) {
|
||||
try {
|
||||
await bildHochladen(art, id, bildart,
|
||||
new File([inhalt], zuschnitt ? 'bild.jpg' : datei.name,
|
||||
{ type: zuschnitt ? 'image/jpeg' : datei.type }));
|
||||
// **Auch das zählt als Änderung.** Hier stand nichts, und die
|
||||
// Kachel zeigte danach das alte Bild weiter — bis man die ganze
|
||||
// Oberfläche neu lud.
|
||||
beiAenderung?.();
|
||||
hinweisZeigen(`${name} gesetzt.`);
|
||||
await laden();
|
||||
} catch (fehler) {
|
||||
abschnitt.appendChild(fehlerzeile(meldung(fehler)));
|
||||
eigenes.disabled = false;
|
||||
}
|
||||
};
|
||||
kopfzeile.append(eigenes, feld);
|
||||
|
||||
// **Nur, wenn es etwas zurückzunehmen gibt.** „Von Hand gesetzt" heisst
|
||||
// auch: beim Auffrischen bleibt es stehen. Ohne diesen Knopf wäre ein
|
||||
// Fehlgriff ein Dauerzustand.
|
||||
if (festgesetzt) {
|
||||
const frei = knopf('Wieder automatisch', 'btn');
|
||||
frei.type = 'button';
|
||||
frei.onclick = async () => {
|
||||
frei.disabled = true;
|
||||
try {
|
||||
await bildFreigeben(art, id, bildart);
|
||||
beiAenderung?.();
|
||||
hinweisZeigen('Kommt beim nächsten Auffrischen wieder aus der Quelle.');
|
||||
await laden();
|
||||
} catch (fehler) {
|
||||
abschnitt.appendChild(fehlerzeile(meldung(fehler)));
|
||||
frei.disabled = false;
|
||||
}
|
||||
};
|
||||
kopfzeile.appendChild(frei);
|
||||
}
|
||||
|
||||
abschnitt.appendChild(kopfzeile);
|
||||
|
||||
if (passende.length === 0 && !aktuell) {
|
||||
|
||||
Reference in New Issue
Block a user