Zwei Zeilen Untertitel, die zweite halb unter dem Bildrand
Gemessen am Bildschirmfoto: zwischen den Zeilen lagen 71 Pixel bei 18 Pixel Texthöhe — fast das Vierfache dessen, was `line-height: 1.25` ergibt. Und die zweite Zeile war mitten im Buchstaben abgeschnitten. Beides derselbe Grund. Der bisherige Weg ging über die Norm: `lineAlign = "end"` macht `line: 90` zur **Unter**kante, und dann steht auch ein zweizeiliger Untertitel vollständig über dem Rand. Chrome und Firefox halten sich daran — nachgestellt und abgelichtet. WebKit nicht: dort ist es die Oberkante, der Kasten wächst nach unten aus dem Bild, und `line-height` aus `::cue` bleibt ebenfalls wirkungslos. Jetzt wird dort gar nichts mehr an den Einblendungen verstellt. Stattdessen rückt der Kasten ab, in den der Browser sie setzt — ein Element, das WebKit selbst erfunden hat und das es dort also gibt. Eine Verschiebung des ganzen Kastens kann nichts abschneiden, egal wie viele Zeilen eine Einblendung hat. Wer den Kasten nicht kennt — Firefox —, bekommt weiter den Weg über die Norm. Erkannt wird das daran, ob die Regel im Stilblatt stehen bleibt, nicht an der Browserkennung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5a0f2bf07f
commit
78e347c58d
@@ -53,14 +53,41 @@ export const GROESSE_SCHRITT = 5;
|
||||
/** Das Vielfache der Schriftgrösse, das eine Zeile hoch ist. */
|
||||
const ZEILENABSTAND = 1.25;
|
||||
|
||||
/** Wo die Unterkante des Untertitels sitzt, in Prozent der Videohöhe.
|
||||
/** Wie weit die Einblendungen vom unteren Bildrand abrücken, in Prozent der
|
||||
* Videohöhe.
|
||||
*
|
||||
* **Nicht am Bildrand.** Der Browser setzt Einblendungen von sich aus ganz
|
||||
* nach unten. Dort liegt bei uns die Bedienleiste des Spielers — und bei
|
||||
* einem zweizeiligen Untertitel verschwand die untere Zeile darunter. Zehn
|
||||
* Prozent Abstand halten sie frei, unabhängig von der Schriftgrösse und
|
||||
* davon, ob eine oder zwei Zeilen dastehen. */
|
||||
const UNTERKANTE = 90;
|
||||
* einem zweizeiligen Untertitel verschwand die untere Zeile darunter. */
|
||||
const ABSTAND_UNTEN = 10;
|
||||
|
||||
/** Ob der Browser den Kasten kennt, in den er die Einblendungen setzt.
|
||||
*
|
||||
* **Die Unterscheidung, um die es hier geht.** Der Weg über die Norm —
|
||||
* `VTTCue.lineAlign = "end"` und `line` in Prozent — ist der richtige, und
|
||||
* Chrome und Firefox befolgen ihn. WebKit nicht: dort gibt `line` die
|
||||
* **Ober**kante an, und ein zweizeiliger Untertitel wächst darunter aus dem
|
||||
* Bild heraus. Gemessen an einem Bildschirmfoto: die zweite Zeile war mitten
|
||||
* im Buchstaben abgeschnitten. Ebenso bleibt dort `line-height` aus `::cue`
|
||||
* wirkungslos — zwischen den Zeilen klaffte das Vierfache einer Zeilenhöhe.
|
||||
*
|
||||
* Beide Löcher stopft derselbe Griff: der Kasten, den WebKit selbst erfunden
|
||||
* hat. Ihn gibt es dort, wo die Norm hier versagt.
|
||||
*
|
||||
* Erkannt wird er, indem eine Regel damit geschrieben und nachgesehen wird,
|
||||
* ob sie stehen blieb — ein Browser, der den Selektor nicht versteht, wirft
|
||||
* sie weg. Keine Abfrage der Browserkennung: die lügt. */
|
||||
let kastenBekannt: boolean | undefined;
|
||||
|
||||
function kenntKasten(): boolean {
|
||||
if (kastenBekannt !== undefined) return kastenBekannt;
|
||||
const probe = document.createElement('style');
|
||||
probe.textContent = 'video::-webkit-media-text-track-container{top:0}';
|
||||
document.head.appendChild(probe);
|
||||
kastenBekannt = (probe.sheet?.cssRules.length ?? 0) > 0;
|
||||
probe.remove();
|
||||
return kastenBekannt;
|
||||
}
|
||||
|
||||
export type Untertitelstil = {
|
||||
/** In Prozent der Größe, die der Browser von sich aus wählt. */
|
||||
@@ -137,6 +164,20 @@ export function stilAnwenden(stil: Untertitelstil): void {
|
||||
color: ${stil.farbe};
|
||||
background-color: ${stil.hintergrund};
|
||||
${rand}
|
||||
}
|
||||
/* Derselbe Zeilenabstand noch einmal, fuer WebKit: in ::cue greift er
|
||||
dort nicht, am Kasten mit den Zeilen schon. Wo beides wirkt, steht
|
||||
zweimal dasselbe und nichts streitet sich. */
|
||||
video::-webkit-media-text-track-display {
|
||||
line-height: ${ZEILENABSTAND} !important;
|
||||
}
|
||||
/* Und so ruecken die Einblendungen vom Rand ab: verschoben wird der
|
||||
ganze Kasten, nicht die einzelne Einblendung. Damit kann keine davon
|
||||
unten herausragen, egal wie viele Zeilen sie hat und egal, wie der
|
||||
Browser die Zeilenangabe auslegt. */
|
||||
video::-webkit-media-text-track-container {
|
||||
transform: translateY(-${ABSTAND_UNTEN}%);
|
||||
line-height: ${ZEILENABSTAND};
|
||||
}`;
|
||||
|
||||
let block = document.getElementById(SCHLUESSEL) as HTMLStyleElement | null;
|
||||
@@ -155,22 +196,19 @@ function zahlImRahmen(wert: unknown, vorgabe: number): number {
|
||||
return Math.max(GROESSE_KLEINST, Math.min(GROESSE_GROESST, gerundet));
|
||||
}
|
||||
|
||||
/** Hebt die Einblendungen einer Spur vom Bildrand ab.
|
||||
/** Hebt die Einblendungen einer Spur vom Bildrand ab — wo CSS nicht reicht.
|
||||
*
|
||||
* **Warum über die Cues und nicht über CSS.** `::cue` gestaltet den Text,
|
||||
* aber nicht seinen Platz: die Position steht in der Datei, und der Browser
|
||||
* setzt sie ohne Angabe ganz nach unten. Erreichbar ist sie nur hier, am
|
||||
* fertig geladenen `VTTCue`.
|
||||
* **Nur noch für Browser ohne den WebKit-Kasten.** Kennt einer ihn, ist das
|
||||
* Abrücken schon erledigt (siehe `stilAnwenden`), und beides zugleich
|
||||
* schöbe doppelt. Übrig bleibt im Wesentlichen Firefox — dort gibt es den
|
||||
* Kasten nicht, dafür hält er sich an die Norm.
|
||||
*
|
||||
* **`lineAlign: "end"` ist der eigentliche Trick.** Damit gibt `line` die
|
||||
* Lage der *Unterkante* an. Ohne ihn wäre es die Oberkante — und dann
|
||||
* stünde ein zweizeiliger Untertitel wieder weiter unten als ein
|
||||
* einzeiliger, also genau der Fall, der das Problem war.
|
||||
*
|
||||
* Kennt ein Browser `lineAlign` nicht, bleibt der Wert einfach liegen; die
|
||||
* Einblendung sitzt dann etwas höher als nötig, aber nie zu tief. */
|
||||
* **`lineAlign: "end"` ist dabei der Trick.** Damit gibt `line` die Lage der
|
||||
* *Unterkante* an. Ohne ihn wäre es die Oberkante — und dann wüchse ein
|
||||
* zweizeiliger Untertitel nach unten aus dem Bild. Genau das tut WebKit,
|
||||
* und genau deshalb wird dieser Weg dort nicht mehr gegangen. */
|
||||
export function einblendungenAusrichten(spur: TextTrack | null | undefined): void {
|
||||
if (!spur?.cues) return;
|
||||
if (!spur?.cues || kenntKasten()) return;
|
||||
|
||||
for (const roh of Array.from(spur.cues)) {
|
||||
const cue = roh as VTTCue;
|
||||
@@ -180,6 +218,6 @@ export function einblendungenAusrichten(spur: TextTrack | null | undefined): voi
|
||||
|
||||
cue.snapToLines = false;
|
||||
cue.lineAlign = 'end';
|
||||
cue.line = UNTERKANTE;
|
||||
cue.line = 100 - ABSTAND_UNTEN;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user