Anfang des Jahres stieß ich auf diese Demo von Florin Pop, bei der eine Linie über oder unter den Buchstaben einer einzeiligen Überschrift verläuft. Ich fand die Idee gut, aber es gab ein paar kleine Dinge an der Implementierung, die ich meiner Meinung nach gleichzeitig vereinfachen und verbessern könnte.

Zunächst wird der Überschriftentext in der Original-Demo dupliziert, was ich wusste, dass man leicht vermeiden könnte. Dann ist da noch die Tatsache, dass die Länge der durch den Text verlaufenden Linie eine magische Zahl ist, was kein sehr flexibler Ansatz ist. Und schließlich, können wir das JavaScript loswerden?
Schauen wir uns also an, wohin ich das gebracht habe.
HTML-Struktur
Florin packt den Text in ein Überschriftenelement und dupliziert dann diese Überschrift. Mit Splitting.js wird der Textinhalt der duplizierten Überschrift durch Spans ersetzt, die jeweils einen Buchstaben des Originaltextes enthalten.
Da ich bereits beschlossen hatte, dies ohne Textduplizierung zu tun, erscheint die Verwendung einer Bibliothek zum Aufteilen des Textes in Zeichen und anschließendes Einfügen jedes Zeichens in ein span etwas übertrieben. Daher machen wir alles mit einem HTML-Präprozessor.
- let text = 'We Love to Play';
- let arr = text.split('');
h1(role='image' aria-label=text)
- arr.forEach(letter => {
span.letter #{letter}
- });
Da das Aufteilen von Text in mehrere Elemente für Screenreader möglicherweise nicht gut funktioniert, haben wir dem Ganzen eine role von image und ein aria-label gegeben.
Dies erzeugt den folgenden HTML-Code
<h1 role="image" aria-label="We Love to Play">
<span class="letter">W</span>
<span class="letter">e</span>
<span class="letter"> </span>
<span class="letter">L</span>
<span class="letter">o</span>
<span class="letter">v</span>
<span class="letter">e</span>
<span class="letter"> </span>
<span class="letter">t</span>
<span class="letter">o</span>
<span class="letter"> </span>
<span class="letter">P</span>
<span class="letter">l</span>
<span class="letter">a</span>
<span class="letter">y</span>
</h1>
Grundlegende Stile
Wir platzieren die Überschrift in der Mitte ihres Elternelements (in diesem Fall body) mithilfe eines grid-Layouts.
body {
display: grid;
place-content: center;
}

width abzudecken, sondern wird stattdessen in der Mitte platziert.Wir können auch einige verschönernde Elemente hinzufügen, wie eine schöne font oder einen background auf dem Container.
Als nächstes erstellen wir die Linie mit einem absolut positionierten ::after-Pseudo-Element mit einer Dicke (height) von $h.
$h: .125em;
$r: .5*$h;
h1 {
position: relative;
&::after {
position: absolute;
top: calc(50% - #{$r}); right: 0;
height: $h;
border-radius: 0 $r $r 0;
background: crimson;
}
}
Der obige Code kümmert sich um die Positionierung und die height des Pseudo-Elements, aber was ist mit der width? Wie bringen wir es dazu, vom linken Rand des Viewports bis zum rechten Rand des Überschriftentextes zu reichen?
Linienlänge
Nun, da wir ein grid-Layout haben, bei dem die Überschrift horizontal zentriert ist, bedeutet dies, dass die vertikale Mittellinie des Viewports mit der der Überschrift übereinstimmt und beide in zwei gleich breite Hälften teilt.
Folglich ist der Abstand zwischen dem linken Rand des Viewports und dem rechten Rand der Überschrift die halbe Viewport-Breite (50vw) plus die halbe Überschriftenbreite, was als %-Wert ausgedrückt werden kann, wenn er bei der Berechnung der width des entsprechenden Pseudo-Elements verwendet wird.
Die width unseres ::after-Pseudo-Elements ist also:
width: calc(50vw + 50%);
Die Linie über und unter den Buchstaben verlaufen lassen
Bisher ist das Ergebnis nur eine karminrote Linie, die schwarzen Text durchquert.
Wir möchten, dass einige der Buchstaben über der Linie erscheinen. Um diesen Effekt zu erzielen, geben wir ihnen (oder auch nicht) zufällig eine Klasse von .over. Das bedeutet eine leichte Änderung des Pug-Codes.
- let text = 'We Love to Play';
- let arr = text.split('');
h1(role='image' aria-label=text)
- arr.forEach(letter => {
span.letter(class=Math.random() > .5 ? 'over' : null) #{letter}
- });
Wir positionieren die Buchstaben mit der Klasse .over relativ und geben ihnen einen positiven z-index.
.over {
position: relative;
z-index: 1;
}
Meine ursprüngliche Idee beinhaltete die Verwendung von translatez(1px) anstelle von z-index: 1, aber dann fiel mir ein, dass die Verwendung von z-index sowohl eine bessere Browserunterstützung hat als auch weniger Aufwand bedeutet.
Die Linie verläuft über einigen Buchstaben, aber unter anderen.
Animieren!
Jetzt, da wir den kniffligen Teil hinter uns haben, können wir auch eine animation hinzufügen, damit die Linie einfährt. Das bedeutet, dass die karminrote Linie zu Beginn um ihre volle width (100%) nach links (in negativer Richtung der x-Achse, also mit Minuszeichen) verschoben wird, nur um sie dann wieder in ihre normale Position zurückkehren zu lassen.
@keyframes slide { 0% { transform: translate(-100%); } }
Ich habe mich entschieden, vor dem Start der animation etwas Zeit zum Durchatmen zu lassen. Das bedeutete, dass ich die Verzögerung von 1s hinzugefügt habe, was wiederum bedeutete, dass ich das Schlüsselwort backwards für animation-fill-mode hinzugefügt habe, damit die Linie im Zustand bleibt, der durch den 0%-Keyframe vor dem Start der animation definiert ist.
animation: slide 2s ease-out 1s backwards;
Eine 3D-Note
Das brachte mich auf eine weitere Idee: die Linie jeden einzelnen Buchstaben durchlaufen zu lassen, das heißt, über dem Buchstaben zu beginnen, durch ihn hindurchzugehen und darunter zu enden (oder umgekehrt).
Dies erfordert echtes 3D und einige kleine Anpassungen.
Zuerst setzen wir transform-style auf preserve-3d auf der Überschrift, da wir möchten, dass alle ihre Kinder (und Pseudo-Elemente) Teil derselben 3D-Anordnung sind, was bewirkt, dass sie entsprechend ihrer Positionierung in 3D geordnet und überlappend sind.
Als nächstes wollen wir jeden Buchstaben um seine y-Achse drehen, wobei die Drehrichtung von der Anwesenheit der zufällig zugewiesenen Klasse abhängt (deren Namen wir von „over“ in .rev für „reverse“ ändern, da „over“ nicht mehr wirklich darauf hindeutet, was wir hier tun).
Bevor wir dies jedoch tun, müssen wir bedenken, dass unsere Span-Elemente zu diesem Zeitpunkt immer noch Inline-Elemente sind und das Setzen eines transform auf einem Inline-Element keinerlei Wirkung hat.
Um dieses Problem zu lösen, setzen wir display: flex auf die Überschrift. Dies schafft jedoch ein neues Problem: Span-Elemente, die nur ein Leerzeichen enthalten (" "), werden auf eine Breite von null gequetscht.

<span> in Firefox DevTools.Eine einfache Lösung dafür ist, white-space: pre auf unsere .letter-Spans zu setzen.
Sobald wir dies getan haben, können wir unsere Spans um einen Winkel $a drehen... in die eine oder andere Richtung!
$a: 2deg;
.letter {
white-space: pre;
transform: rotatey($a);
}
.rev { transform: rotatey(-$a); }
Da die Drehung um die y-Achse unsere Buchstaben horizontal staucht, können wir sie entlang der x-Achse um einen Faktor ($f) skalieren, der das Inverse des Kosinus von $a ist.
$a: 2deg;
$f: 1/cos($a)
.letter {
white-space: pre;
transform: rotatey($a) scalex($f)
}
.rev { transform: rotatey(-$a) scalex($f) }
Wenn Sie verstehen möchten, warum dieser spezielle Skalierungsfaktor verwendet wird, können Sie sich diesen älteren Artikel ansehen, in dem ich alles im Detail erkläre.
Und das war's! Wir haben nun das 3D-Ergebnis, das wir angestrebt haben! Beachten Sie jedoch, dass die hier verwendete Schriftart so gewählt wurde, dass unser Ergebnis gut aussieht, und eine andere Schriftart möglicherweise nicht so gut funktioniert.
Hat jemand anders gedacht, dass 3D-Threading vor zehn Minuten unmöglich war? Dieses Ent-Quetschen der gedrehten Buchstaben ist genial.
Anas Arbeit hört nie auf, mich zu beeindrucken.
Hallo
Ich wollte Sie nur darauf aufmerksam machen, dass einer meiner ehemaligen Studenten (sein Name ist Côme Gaillard) dies ohne jede Bibliothek neu geschrieben hat, einfach in reinem HTML-CSS-JS, ich dachte, es würde Sie interessieren.
Die 3D-Version gleitet unter Firefox mit der roten Linie über den Text und beginnt erst dann, durch die Buchstaben zu weben, wenn die Animation endet. Ich frage mich, ob dieser Fehler im Browser oder in der Demo liegt.
Oh, wow, ich glaube, das ist komplizierter als nur ein Browser- oder ein Demo-Fehler…
Welche Version von Firefox und welches Betriebssystem verwenden Sie, um dies zu sehen? Ich habe es gerade erneut sowohl in Nightly als auch in Stable unter Windows 10 getestet, und es funktioniert in beiden wie beabsichtigt, die Linie verläuft durch die Buchstaben, während sie nach rechts gleitet, nicht am Ende der Animation.
Ich benutze 73.0.1 unter Windows 10 mit einer GeForce GTX 750 Ti. Wenn ich mir meine „Support-Informationen“ ansehe, könnte es ein Treiberproblem geben, obwohl ich keine Ahnung habe, warum das dieses spezielle Problem verursachen sollte.
Hm, ein Neustart des Browsers hat es normal funktionieren lassen. Da er ursprünglich über eine Remote-Desktop-Verbindung gestartet wurde, glaube ich, dass es sich um ein Problem mit dem für Remote Desktop verwendeten Videotreiber oder damit handelt, wie Firefox seinen Compositor einrichtet, wenn er keine Hardwarebeschleunigung nutzen kann.
Obwohl mir diese Demonstration gefallen hat, fand ich die obige Zeile etwas ironisch oder zufällig heuchlerisch, da die Methode im Wesentlichen genau das tut, was sie angeblich nicht tun würde: eine Bibliothek verwenden, um Text in mehrere einzelne Spans aufzuteilen. Sie verschiebt lediglich die Ausführungskosten vom Client-Gerät/Browser auf einen serverseitigen Prerendering-Prozess.
Es ist der ewige Kampf zwischen den Ausführungskosten eines kleinen JavaScript-Codeschnipsels, der auf dem Client läuft, im Gegensatz zum Overhead, den ein Client durch den Download weiterer Datenbits hat, da alle sehr kleinen HTML-Fragmente vor dem Senden über das Kabel zu viel längeren Span-Clustern erweitert werden. In diesem Fall ist der Kompromiss wahrscheinlich meist zugunsten des Prerenderings, aber die Konservierung von HTML-Energie bedeutet, dass irgendwo jemand den Code ausführen musste, um dieses Rendering durchzuführen...
Wo ist die Bibliothek? Ich sehe keine erwähnt und würde ein kleines, handgeschriebenes Skript nicht als eine solche klassifizieren.
Gute Arbeit! Die 3D-Version ist stark abhängig von den verwendeten Buchstaben. Man sieht das Problem beim Ausrufezeichen, wo die Linie durchläuft, anstatt darum herum zu weben.
Wenn wir eine Monospace-Schriftart oder sogenannte Festbreitenschriftart verwenden, kann ein ähnlicher Effekt mit zwei gestrichelten Linien nachgebildet werden.
Linien werden mit Phasenversatz und
repeating-linear-gradientmit einem Farbstopp von1ch(was der Breite eines Buchstabens/Zeichens in einer Monospace-Schriftart entspricht) gezeichnet. Eine gestrichelte Linie unter und eine über dem Text platziert, erweckt die Illusion einer durchgehenden Linie.Theoretisch sollte jede Monospace-Schriftart in dieser Demo funktionieren. Ich habe sie mit einigen getestet: PT Mono, Roboto Mono, Nova Mono und Courier Prime.
https://fonts.google.com/?category=Monospace