Mein Weg zum Erlernen von CSS war etwas unorthodox. Ich begann nicht als Front-End-Entwickler. Ich war Java-Entwickler. Tatsächlich waren meine frühesten Erinnerungen an CSS das Auswählen von Farben für Dinge in Visual Studio.
Es hat eine Weile gedauert, bis ich mich dem Front-End zuwenden und meine Liebe dafür entdecken konnte. Und die Erkundung von CSS kam später. Als es soweit war, war das ungefähr zu der Zeit, als CSS3 aufkam. 3D und Animation waren die angesagten Dinge. Sie prägten meine CSS-Lernkurve fast schon. Sie zogen mich an und *formten* (Wortspiel beabsichtigt) mein Verständnis von CSS mehr als andere Dinge wie Layout, Farbe usw.
Worauf ich hinauswill, ist, dass ich mich schon eine Weile mit 3D-CSS beschäftige. Und wie bei allem, womit man viel Zeit verbringt, verfeinert man seinen Prozess über die Jahre hinweg, während man seine Fähigkeiten ausbaut. Dieser Artikel gibt einen Einblick, wie ich derzeit an 3D-CSS herangehe und erklärt einige Tipps und Tricks, die dir vielleicht helfen!
Alles ist ein Quader
Für die meisten Dinge können wir einen Quader verwenden. Wir können zwar komplexere Formen erstellen, aber diese erfordern normalerweise etwas mehr Überlegung. Kurven sind besonders schwierig und es gibt einige Tricks, um sie zu handhaben (aber dazu später mehr).
Wir werden nicht durchgehen, wie man einen Quader in CSS erstellt. Dazu können wir auf Ana Tudors Beitrag verweisen oder uns dieses Screencast ansehen, wie ich einen erstelle.
Im Grunde verwenden wir ein Element, um unseren Quader zu umschließen, und transformieren dann sechs Elemente darin. Jedes Element fungiert als eine Seite unseres Quaders. Es ist wichtig, dass wir `transform-style: preserve-3d` anwenden. Und es ist keine schlechte Idee, es überall anzuwenden. Wahrscheinlich werden wir es mit verschachtelten Quadern zu tun haben, wenn die Dinge komplexer werden. Der Versuch, ein fehlendes `transform-style` beim Wechsel zwischen Browsern zu debuggen, kann schmerzhaft sein.
* { transform-style: preserve-3d; }
Versuchen Sie für Ihre 3D-Kreationen, die mehr als ein paar Flächen haben, sich die gesamte Szene aus Quadern aufgebaut vorzustellen. Als reales Beispiel betrachten Sie diese Demo eines 3D-Buches. Es besteht aus vier Quadern. Einer für jedes Cover, einer für den Rücken und einer für die Seiten. Die Verwendung von `background-image` erledigt den Rest für uns.
Eine Szene einrichten
Wir werden Quader wie LEGO-Steine verwenden. Aber wir können uns das Leben etwas leichter machen, indem wir eine Szene einrichten und eine Ebene erstellen. Diese Ebene ist der Ort, an dem unsere Kreation stehen wird, und erleichtert uns das Drehen und Bewegen der gesamten Kreation.
Wenn ich eine Szene erstelle, drehe ich sie gerne zuerst auf der X- und Y-Achse. Dann lege ich sie mit `rotateX(90deg)` flach hin. Auf diese Weise füge ich einen neuen Quader zur Szene hinzu, indem ich ihn innerhalb des Ebenenelements hinzufüge. Eine weitere Sache, die ich tun werde, ist, `position: absolute` auf allen Quadern einzustellen.
.plane {
transform: rotateX(calc(var(--rotate-x, -24) * 1deg)) rotateY(calc(var(--rotate-y, -24) * 1deg)) rotateX(90deg) translate3d(0, 0, 0);
}
Beginnen Sie mit einem Boilerplate
Das Erstellen von Quadern verschiedener Größen und über eine Ebene hinweg bedeutet viel Wiederholung für jede Kreation. Aus diesem Grund verwende ich Pug, um meine Quader über einen Mixin zu erstellen. Wenn Sie Pug nicht kennen, habe ich eine 5-minütige Einführung geschrieben.
Eine typische Szene sieht so aus
//- Front
//- Back
//- Right
//- Left
//- Top
//- Bottom
mixin cuboid(className)
.cuboid(class=className)
- let s = 0
while s < 6
.cuboid__side
- s++
.scene
//- Plane that all the 3D stuff sits on
.plane
+cuboid('first-cuboid')
Was das CSS angeht. Meine Quaderklasse sieht derzeit so aus
.cuboid {
// Defaults
--width: 15;
--height: 10;
--depth: 4;
height: calc(var(--depth) * 1vmin);
width: calc(var(--width) * 1vmin);
transform-style: preserve-3d;
position: absolute;
font-size: 1rem;
transform: translate3d(0, 0, 5vmin);
}
.cuboid > div:nth-of-type(1) {
height: calc(var(--height) * 1vmin);
width: 100%;
transform-origin: 50% 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotateX(-90deg) translate3d(0, 0, calc((var(--depth) / 2) * 1vmin));
}
.cuboid > div:nth-of-type(2) {
height: calc(var(--height) * 1vmin);
width: 100%;
transform-origin: 50% 50%;
transform: translate(-50%, -50%) rotateX(-90deg) rotateY(180deg) translate3d(0, 0, calc((var(--depth) / 2) * 1vmin));
position: absolute;
top: 50%;
left: 50%;
}
.cuboid > div:nth-of-type(3) {
height: calc(var(--height) * 1vmin);
width: calc(var(--depth) * 1vmin);
transform: translate(-50%, -50%) rotateX(-90deg) rotateY(90deg) translate3d(0, 0, calc((var(--width) / 2) * 1vmin));
position: absolute;
top: 50%;
left: 50%;
}
.cuboid > div:nth-of-type(4) {
height: calc(var(--height) * 1vmin);
width: calc(var(--depth) * 1vmin);
transform: translate(-50%, -50%) rotateX(-90deg) rotateY(-90deg) translate3d(0, 0, calc((var(--width) / 2) * 1vmin));
position: absolute;
top: 50%;
left: 50%;
}
.cuboid > div:nth-of-type(5) {
height: calc(var(--depth) * 1vmin);
width: calc(var(--width) * 1vmin);
transform: translate(-50%, -50%) translate3d(0, 0, calc((var(--height) / 2) * 1vmin));
position: absolute;
top: 50%;
left: 50%;
}
.cuboid > div:nth-of-type(6) {
height: calc(var(--depth) * 1vmin);
width: calc(var(--width) * 1vmin);
transform: translate(-50%, -50%) translate3d(0, 0, calc((var(--height) / 2) * -1vmin)) rotateX(180deg);
position: absolute;
top: 50%;
left: 50%;
}
Was mir standardmäßig Folgendes gibt
Angetrieben von CSS-Variablen
Sie haben vielleicht eine ganze Menge CSS-Variablen (auch Custom Properties genannt) darin bemerkt. Das spart viel Zeit. Ich befeuere meine Quader mit CSS-Variablen.
--width: Die Breite eines Quaders auf der Ebene--height: Die Höhe eines Quaders auf der Ebene--depth: Die Tiefe eines Quaders auf der Ebene--x: Die X-Position auf der Ebene--y: Die Y-Position auf der Ebene
Ich verwende `vmin` meistens als meine Größeneinheit, um alles reaktionsfähig zu halten. Wenn ich etwas im Maßstab erstelle, erstelle ich möglicherweise eine reaktionsfähige Einheit. Diese Technik haben wir in einem früheren Artikel erwähnt. Auch hier lege ich die Ebene flach hin. Jetzt kann ich meine Quader als mit Höhe, Breite und Tiefe bezeichnend referenzieren. Diese Demo zeigt, wie wir einen Quader auf der Ebene bewegen und seine Abmessungen ändern können.
Debugging mit dat.GUI
Sie haben vielleicht das kleine Panel oben rechts in einigen der von uns behandelten Demos bemerkt. Das ist dat.GUI. Es ist eine leichtgewichtige Controller-Bibliothek für JavaScript, die für das Debugging von 3D-CSS äußerst nützlich ist. Mit wenig Code können wir ein Panel einrichten, das es uns ermöglicht, CSS-Variablen zur Laufzeit zu ändern. Eine Sache, die ich gerne tue, ist die Verwendung des Panels, um die Ebene auf der X- und Y-Achse zu drehen. So ist es möglich zu sehen, wie die Dinge ausgerichtet sind, oder an einem Teil zu arbeiten, den man vielleicht nicht sofort sieht.
const {
dat: { GUI },
} = window
const CONTROLLER = new GUI()
const CONFIG = {
'cuboid-height': 10,
'cuboid-width': 10,
'cuboid-depth': 10,
x: 5,
y: 5,
z: 5,
'rotate-cuboid-x': 0,
'rotate-cuboid-y': 0,
'rotate-cuboid-z': 0,
}
const UPDATE = () => {
Object.entries(CONFIG).forEach(([key, value]) => {
document.documentElement.style.setProperty(`--${key}`, value)
})
}
const CUBOID_FOLDER = CONTROLLER.addFolder('Cuboid')
CUBOID_FOLDER.add(CONFIG, 'cuboid-height', 1, 20, 0.1)
.name('Height (vmin)')
.onChange(UPDATE)
CUBOID_FOLDER.add(CONFIG, 'cuboid-width', 1, 20, 0.1)
.name('Width (vmin)')
.onChange(UPDATE)
CUBOID_FOLDER.add(CONFIG, 'cuboid-depth', 1, 20, 0.1)
.name('Depth (vmin)')
.onChange(UPDATE)
// You have a choice at this point. Use x||y on the plane
// Or, use standard transform with vmin.
CUBOID_FOLDER.add(CONFIG, 'x', 0, 40, 0.1)
.name('X (vmin)')
.onChange(UPDATE)
CUBOID_FOLDER.add(CONFIG, 'y', 0, 40, 0.1)
.name('Y (vmin)')
.onChange(UPDATE)
CUBOID_FOLDER.add(CONFIG, 'z', -25, 25, 0.1)
.name('Z (vmin)')
.onChange(UPDATE)
CUBOID_FOLDER.add(CONFIG, 'rotate-cuboid-x', 0, 360, 1)
.name('Rotate X (deg)')
.onChange(UPDATE)
CUBOID_FOLDER.add(CONFIG, 'rotate-cuboid-y', 0, 360, 1)
.name('Rotate Y (deg)')
.onChange(UPDATE)
CUBOID_FOLDER.add(CONFIG, 'rotate-cuboid-z', 0, 360, 1)
.name('Rotate Z (deg)')
.onChange(UPDATE)
UPDATE()
Wenn Sie sich das Zeitraffer-Video in diesem Tweet ansehen. Sie werden bemerken, dass ich die Ebene beim Aufbau der Szene oft drehe.
Der dat.GUI-Code ist etwas repetitiv. Wir können Funktionen erstellen, die eine Konfiguration entgegennehmen und den Controller generieren. Es bedarf einiger Feinabstimmung, um Ihren Bedürfnissen gerecht zu werden. Ich begann, mit dynamisch generierten Controllern in dieser Demo zu spielen.
Zentrierung
Sie haben vielleicht bemerkt, dass standardmäßig jeder Quader halb unter und halb über der Ebene liegt. Das ist beabsichtigt. Es ist auch etwas, womit ich erst vor kurzem begonnen habe. Warum? Weil wir das enthaltende Element unserer Quader als Zentrum des Quaders verwenden wollen. Das macht Animationen einfacher. Besonders, wenn wir um die Z-Achse rotieren wollen. Das habe ich beim Erstellen von "CSS is Cake" herausgefunden. Nachdem ich den Kuchen gemacht hatte, beschloss ich, dass jedes Stück interaktiv sein sollte. Dann musste ich zurückgehen und meine Implementierung ändern, um das Rotationszentrum des sich umkehrenden Stücks zu korrigieren.
Hier habe ich die Demo zerlegt, um die Zentren und die Auswirkungen eines versetzten Zentrums auf die Demo zu zeigen.
Positionierung
Wenn wir mit einer komplexeren Szene arbeiten, können wir sie in verschiedene Abschnitte aufteilen. Hier ist das Konzept der Unterebenen nützlich. Betrachten Sie diese Demo, in der ich meinen persönlichen Arbeitsbereich nachgebildet habe.
Hier passiert ziemlich viel und es ist schwer, den Überblick über alle Quader zu behalten. Dafür können wir Unterebenen einführen. Brechen wir diese Demo auf. Der Stuhl hat seine eigene Unterebene. Das macht es einfacher, ihn in der Szene zu bewegen und zu drehen – unter anderem –, ohne etwas anderes zu beeinflussen. Tatsächlich können wir sogar die Oberseite drehen, ohne die Füße zu bewegen!
Ästhetik
Sobald wir eine Struktur haben, ist es an der Zeit, an der Ästhetik zu arbeiten. Das hängt alles davon ab, was Sie gerade erstellen. Aber Sie können einige schnelle Erfolge erzielen, indem Sie bestimmte Techniken anwenden. Ich beginne normalerweise damit, Dinge "hässlich" zu machen, dann gehe ich zurück und erstelle CSS-Variablen für alle Farben und wende sie an. Drei Schattierungen für eine bestimmte Sache ermöglichen es uns, die Seiten eines Quaders visuell zu unterscheiden. Betrachten Sie dieses Toaster-Beispiel. Drei Schattierungen decken die Seiten des Toasters ab.
Unser Pug-Mixin von früher erlaubt es uns, Klassennamen für einen Quader zu definieren. Das Anwenden von Farbe auf eine Seite sieht normalerweise so aus
/* The front face uses a linear-gradient to apply the shimmer effect */
.toaster__body > div:nth-of-type(1) {
background: linear-gradient(120deg, transparent 10%, var(--shine) 10% 20%, transparent 20% 25%, var(--shine) 25% 30%, transparent 30%), var(--shade-one);
}
.toaster__body > div:nth-of-type(2) {
background: var(--shade-one);
}
.toaster__body > div:nth-of-type(3),
.toaster__body > div:nth-of-type(4) {
background: var(--shade-three);
}
.toaster__body > div:nth-of-type(5),
.toaster__body > div:nth-of-type(6) {
background: var(--shade-two);
}
Es ist etwas knifflig, zusätzliche Elemente mit unserem Pug-Mixin einzubinden. Aber vergessen wir nicht, jede Seite unseres Quaders bietet zwei Pseudoelemente. Wir können diese für verschiedene Details verwenden. Zum Beispiel sind der Toasterschlitz und der Schlitz für den Griff an der Seite Pseudoelemente.
Ein weiterer Trick ist die Verwendung von `background-image` zum Hinzufügen von Details. Betrachten Sie zum Beispiel den 3D-Arbeitsbereich. Wir können Hintergrundebenen verwenden, um Schattierungen zu erzeugen. Wir können tatsächliche Bilder verwenden, um texturierte Oberflächen zu erstellen. Der Bodenbelag und der Teppich sind ein wiederholtes `background-image`. Tatsächlich ist die Verwendung eines Pseudoelements für Texturen großartig, da wir sie dann bei Bedarf transformieren können, wie z. B. das Drehen eines Kachelbildes. Ich habe auch festgestellt, dass ich in einigen Fällen beim direkten Arbeiten mit einer Quaderseite ein Flackern habe.
Ein Problem bei der Verwendung eines Bildes für Texturen ist, wie wir verschiedene Schattierungen erstellen. Wir brauchen Schattierungen, um die verschiedenen Seiten zu unterscheiden. Hier kann die `filter`-Eigenschaft helfen. Das Anwenden eines `brightness`()`-Filters auf die verschiedenen Seiten eines Quaders kann sie aufhellen oder abdunkeln. Betrachten Sie diesen CSS-flippenden Tisch. Alle Oberflächen verwenden ein Texturbild. Um die Seiten zu unterscheiden, werden jedoch Helligkeitsfilter angewendet.
Rauch und Spiegel Perspektive
Wie sieht es mit Formen aus – oder Features, die wir erstellen wollen und die unmöglich erscheinen – mit einer endlichen Menge von Elementen? Manchmal können wir das Auge mit etwas Rauch und Spiegeln täuschen. Wir können ein "faux" 3D-Gefühl vermitteln. Die Zdog Bibliothek macht das gut und ist ein gutes Beispiel dafür.
Betrachten Sie diesen Ballonstrauß. Die Schnüre, die sie halten, verwenden die richtige Perspektive und jede hat ihre eigene Drehung, Neigung usw. Aber die Ballons selbst sind flach. Wenn wir die Ebene drehen, behalten die Ballons die Gegenrotation der Ebene bei. Und das vermittelt diesen "faux" 3D-Eindruck. Probieren Sie die Demo aus und schalten Sie das Gegendrehen ab.
Manchmal braucht es ein wenig Out-of-the-Box-Denken. Ein Zimmerpflanzen-Vorschlag wurde mir gemacht, als ich den 3D-Arbeitsbereich erstellte. Ich habe ein paar davon im Raum. Mein erster Gedanke war: "Nein, ich kann einen quadratischen Topf machen, und wie mache ich all die Blätter?" Nun, eigentlich können wir hier auch ein paar optische Tricks anwenden. Nehmen Sie ein Stockbild von Blättern oder einer Pflanze. Entfernen Sie den Hintergrund mit einem Werkzeug wie remove.bg. Positionieren Sie dann viele Bilder an derselben Stelle, drehen Sie aber jedes um einen bestimmten Betrag. Nun, wenn sie gedreht werden, erhalten wir den Eindruck einer 3D-Pflanze.
Ungewöhnliche Formen angehen
Ungewöhnliche Formen sind schwer auf generische Weise abzudecken. Jede Kreation hat ihre eigenen Hürden. Aber es gibt ein paar Beispiele, die Ihnen Ideen für die Bewältigung von Dingen geben könnten. Ich habe kürzlich einen Artikel über die UX von LEGO-Interface-Panels gelesen. In der Tat ist die Herangehensweise an 3D-CSS wie an ein LEGO-Set keine schlechte Idee. Aber das LEGO-Interface-Panel ist eine Form, die wir mit CSS erstellen könnten (abzüglich der Noppen – ich habe erst kürzlich gelernt, dass sie so heißen). Es ist zunächst ein Quader. Dann können wir die obere Fläche ausschneiden, die Endfläche transparent machen und ein Pseudoelement drehen, um sie zu verbinden. Wir können das Pseudoelement verwenden, um die Details mit einigen Hintergrundebenen hinzuzufügen. Versuchen Sie, den Drahtgittermodus in der folgenden Demo ein- und auszuschalten. Wenn wir die genauen Höhen und Winkel für die Flächen wollen, können wir etwas Mathematik verwenden, um die Hypotenuse usw. zu berechnen.
Eine weitere ungewöhnliche Sache ist die Handhabung von Kurven. Kugelförmige Formen liegen nicht im Bereich von CSS. Wir haben an diesem Punkt verschiedene Optionen. Eine Option ist, diese Tatsache zu akzeptieren und Polygone mit einer endlichen Anzahl von Seiten zu erstellen. Eine andere ist, abgerundete Formen zu erstellen und die Rotationstechnik zu verwenden, die wir bei der Pflanze erwähnt haben. Jede dieser Optionen könnte funktionieren. Aber auch hier kommt es auf den Anwendungsfall an. Jede hat Vor- und Nachteile. Beim Polygon verzichten wir auf die Kurven oder verwenden so viele Elemente, dass wir fast eine Kurve erhalten. Letzteres könnte zu Leistungsproblemen führen. Bei dem Perspektiv-Trick könnten wir je nach Situation ebenfalls Leistungsprobleme haben. Wir verzichten auch darauf, die "Seiten" der Form stylen zu können, da es keine gibt.
Z-Fighting
Zuletzt, aber nicht zuletzt, ist es erwähnenswert: "Z-Fighting". Das ist, wenn bestimmte Elemente auf einer Ebene überlappen oder ein unerwünschtes Flackern verursachen. Es ist schwer, gute Beispiele dafür zu geben. Es gibt keine generische Lösung dafür. Es ist etwas, das von Fall zu Fall angegangen werden muss. Die Hauptstrategie ist, die Dinge im DOM entsprechend zu ordnen. Aber manchmal ist das nicht das einzige Problem.
Genauigkeit kann manchmal Probleme verursachen. Lassen Sie uns noch einmal auf den 3D-Arbeitsbereich verweisen. Betrachten Sie die Leinwand an der Wand. Der Schatten ist ein Pseudoelement. Wenn wir die Leinwand exakt an die Wand setzen, werden wir auf Probleme stoßen. Wenn wir das tun, werden der Schatten und die Wand um die vordere Position kämpfen. Um dem entgegenzuwirken, können wir Dinge um einen kleinen Betrag verschieben. Das löst das Problem und erklärt, was vorne liegen soll.
Versuchen Sie, diese Demo mit "Canvas offset" ein- und ausgeschaltet zu vergrößern. Beachten Sie, wie der Schatten flackert, wenn kein Offset vorhanden ist? Das liegt daran, dass der Schatten und die Wand um die Ansicht kämpfen. Der Offset setzt `--x` auf einen Bruchteil von `1vmin`, den wir `--cm` genannt haben. Das ist eine responsive Einheit, die für diese Kreation verwendet wird.
Das war's!
Bringen Sie Ihr CSS in eine andere Dimension. Nutzen Sie einige meiner Tipps, erstellen Sie eigene, teilen Sie sie und teilen Sie Ihre 3D-Kreationen! Ja, 3D-Dinge in CSS zu erstellen kann schwierig sein und ist definitiv ein Prozess, den wir im Laufe der Zeit verfeinern können. Unterschiedliche Ansätze funktionieren für verschiedene Menschen, und Geduld ist eine erforderliche Zutat. Ich bin gespannt, wie Sie Ihren Ansatz weiterentwickeln!
Das Wichtigste? Haben Sie Spaß dabei!
Eine erstaunliche Artikel, Glückwunsch, ich mag wirklich die gesamte Erklärung und Ihren Ansatz zur Handhabung der gesamten Umgebung.
Ich erinnere mich an eine hübsche kleine Bibliothek aus der damaligen Zeit, Sprite3d, die für diesen Ansatz ausgezeichnet war. Noch heute sehr gut nutzbar.
https://github.com/boblemarin/Sprite3D.js/
Ich habe sogar eine skizzenhafte Website auf der Grundlage seiner Ideen erstellt...
http://bearbeats.co.uk/
Alter, das ist so genial. Ich wollte mich schon immer in 3D in anderen Medien vertiefen und jetzt hast du mir tolle Inspiration und Werkzeuge für CSS gegeben. Mach weiter so mit der unglaublichen Arbeit! Ich liebe es, Einschränkungen herausgefordert und Standards hinterfragt zu sehen.
Wirklich guter Artikel! Es ist großartig zu sehen, wie andere die Kraft von 3D CSS/HTML wirklich nutzen.
Ich habe schon immer die Idee geliebt, 3D-Websites wie diese zu bauen, und habe enorm viel Zeit damit verbracht, zu lernen, wie man das macht.
Ich habe sogar die Anstrengung unternommen, mein eigenes JavaScript-Framework zu bauen, um bei den komplexeren Berechnungen zu helfen. Sobald ich Quaternions verstanden hatte, konnte ich einige ziemlich coole Dinge tun.
2 Jahre später arbeite ich immer noch daran… Ich hoffe nur, dass es nicht umsonst war! Gibt es, oder wird es, eine gewisse Nachfrage von Website-Besitzern dafür geben, frage ich mich?