Überlappende Divs zum Blättern mit CSS 3
Was Frauen wollen…
Ich stehe ja „kurz“ davor, den einen Ring (sie zu finden, ins Dunkle zu treiben und ewig zu binden) überzustreifen. Damit werde ich zwar nicht unsichtbar (außer evtl. für andere Frauen), aber die Vorbereitungen darauf machen einiges an Arbeit.
Denn zu so einem Event werden üblicherweise Familie und Freunde eingeladen. Da die klassische Variante per Post mir einmal zu langweilig war, zum anderen zu teuer, habe ich mich für den digitalen Weg über eine eigene Webseite entschieden (und in zähen Verhandlungen meine bald bessere Hälfte auch überzeugt, das ich das durchaus kann… 😉 ).
Natürlich muss so eine Einladung was hermachen, ein einfacher Brief tut es da nicht! Und da es Verwandte ohne Internet gibt, war es naheliegend, das Design der Papierkarten für diese für den Rest auch online nachzubauen.
… ist nicht immer ganz einfach!
Klingt einfach… kommt aber extrem auf die Karte an. Denn Frau wählt ja nicht nach technischer Umsetzbarkeit aus, sondern nach Optik.
Kurz gesagt ist es eine Karte, wo die darunterliegenden Seiten immer unter der oberen Seite hervorschauen. Zur besseren Vorstellung habe ich davon ein Bild eingefügt:
Die Anforderungen sind klar, es soll so aussehen und sich auch aufklappen lassen. Das geht mit CSS3 relativ einfach und es gibt auch massig Beispiele dafür, wie man ein div umdrehen kann. Es gibt auch zig Beispiele dafür, wie man ein Buch mit Blättereffekten realisieren kann. Aber ich habe nichts gefunden, das auch nur in die Nähe dieses Layouts gekommen ist.
Besser selber gemacht
Im Endeffekt ist es trivial. Es sind einige divs, die sich überlappen. Die divs an sich sind flippbar.
Wäre da nicht noch Safari, der hier offensichtlich einen Bug hat…
Meine Lösung funktioniert auf allen modernen Browsern, das schließt also IE unter Version 10 aus. Es funktioniert, mit leichten Abstrichen (man muss scrollen um zur Karte zu kommen), auch auf mobilen Geräten.
Zum Einsatz kommt Bootstrap für die Basics, außerdem jQuery und eigenes CSS. Der JS-Anteil ist tricky, wenn letztendlich doch trivial, der größere, wichtigere Teil ist tatsächlich pures CSS.
Die Technik
Divs überlagern geht (schon lange) mit z-index. Je größer der Index, desto weiter „vorne“ liegt das div und überlagert damit die darunterliegenden.
Das man divs quasi umdrehen kann ist noch „relativ“ neu, d.h. es kam mit CSS 3 und wird z.B. vom IE erst ab Version 10 unterstützt. Ohne das wäre kein echter Blättereffekt möglich, bzw. nur per Javascript umsetzbar.
Der HTML-Part:
<div id="invitation">
<div id="p1" class="card">
<div class="front blueBg">
</div>
<div class="back whiteBg">
<h3>Einladung</h3>
<hr />
</div>
</div>
<div id="p2" class="card">
<div class="front blueBg">
<div>
</div>
<div class="vertical-text">EINLADUNG</div>
</div>
<div class="back blueBg">
<h3>Informationen</h3>
<hr>
</div>
</div>
...
Im Prinzip sind das nur diverse divs ineinander geschachtelt. Das erste div ist die gesamte Karte, die divs mit der CSS-Klasse „card“ sind die einzelnen Seiten. Dabei ist das div mit der Klasse „front“ die Vorderseite, das mit „back“ entsprechend die Rückseite.
Die übrigen Klassen machen noch ein wenig Bunt usw. dazu.
Der CSS-Part:
#invitation {
/* es ist einfacher mit absoluten Größen zu arbeiten */
width: 800px;
height: 400px;
left: 55%;
top: 100px;
position: absolute;
/* Das ist quasi der Abstand zwischen dem Bildschirm und der Karte.
Ziemlich wichtig, damit das auch gut aussieht. */
perspective: 3000px;
}
.card {
width: 100%;
height: 100%;
position: absolute;
/* Das hier legt die Grundlagen für die Animation fest */
transform-style: preserve-3d;
transition: transform 1s;
transform-origin: 0px 0px 0px;
}
.card .back {
left: 500px;
/* Damit wird die Rückseite umgedreht */
transform: rotateY(-180deg);
transform-origin: 0px 0px 0px;
}
.card.flipping {
/* Damit wird die Seite an sich umgedreht */
transform: rotateY(-180deg);
}
.back, .front {
/* Die Rückseite wird nicht angezeigt */
backface-visibility: hidden;
}
.flipped .back {
/* Zeige die Rückseite, wenn die Seite umgedreht ist */
backface-visibility: visible;
}
Tipp: Bei der Demo mal mit Firebug oder ähnlichem Tool an „perspective“ herumspielen. Zwischen 1000px und 10000px sieht man deutliche Unterschiede.
Der Javascript-Part:
$(document).ready(function() {
$('.front').click(function() {
location.hash = $(this).parent().attr('id');
flipper($(this));
});
$('.back').click(function() {
var loc = $(this).parent().prev('.card').attr('id');
if (loc) {
location.hash = loc;
}
else {
location.hash = '';
}
var obj = $(this);
obj.parent().removeClass('flipping');
obj.parent().prev().show();
setTimeout( function () {
obj.parent().removeClass('flipped');
}, 350);
});
var loc = location.hash;
if (loc) {
flipper($('.card' + loc + ' .front'));
var lc = parseInt(loc.replace('#', '').replace('p', '')) * 500;
setTimeout( function () {
$('.card' + loc).addClass('flipping');
setTimeout( function () {
$('.card' + loc).prev('.card').hide();
$('.card' + loc).addClass('flipped');
}, 350);
}, lc);
}
});
function flipper(obj)
{
var toFlip = obj.parent().prevAll('.card:not(.flipped)');
Array.prototype.reverse.call(toFlip).each(function (indexInArray) {
var obj1 = $(this);
setTimeout( function () {
obj1.addClass('flipping');
setTimeout( function () {
obj1.prev('.card').hide();
obj1.addClass('flipped');
}, 350);
}, indexInArray * 500);
});
if (toFlip.length == 0) {
obj.parent().addClass('flipping');
setTimeout( function () {
obj.parent().prev('.card').hide();
obj.parent().addClass('flipped');
}, 350);
}
}
Es werden auf alle Klassen „front“ und „back“ Klick-Listener gesetzt (Zeile 2, 8). Damits netter ist, werden pro Seite Anker gesetzt, um Vor-Zurück usw. zu erlauben (Zeile 3, 9-16) und auch wieder ausgelesen (Zeile 26-39).
Lustigerweise ist zurückblättern (Zeile 18-23) einfacher als vorblättern (Zeile 42-64). Das liegt daran, das man eigentlich immer nur eine Seite zurückblättern kann, aber viele Seiten auf einmal vor. Ist auch klar, wenn man sich das Bild von oben nochmal ansieht, die unteren Seiten werden umgeblättert immer die oberen überdecken, man sieht von hinten also immer nur die vorherige Seite, von vorne aber alle.
Was passiert da jetzt genau? „Eigentlich“ werden nur CSS-Klassen hinzugefügt oder entfernt.
Sieht man schön beim zurückblättern, da wird dem überliegenden card-div die Klasse „flipping“ entfernt (Zeile 19), das davorliegende card-div wird wieder eingeblendet (Zeile 20) und ein kleines Timeout sorgt dafür, das nach 350 Millisekunden auch die Klasse „flipped“ entfernt wird (Zeile 21-23).
Andersherum ist es etwas schwieriger. Es werden alle nicht geflippten divs die vor der zu flippenden Seite liegen geholt (Zeile 44, jQuery yay!) und diese quasi „durchgeblättert“.
Das macht Zeile 46. Dabei wird wieder ein Timer eingesetzt (48-54), der dafür sorgt, das jede Seite für sich blättert (sieht auch wirklich nett aus). Als erstes wird da dann eine Klasse „flipping“ hinzugefügt (Zeile 49) und ein weiterer Timer gesetzt, der dann die vorherige Seite versteckt und die Klasse „flipped“ hinzufügt (Zeile 50-53).
Damit man Seiten direkt ansprechen kann, aber auch auf die letzte Seite kommt (also die Rückseite der Karte), wird auch geflippt, wenn man nochmal auf die aktuelle Seite klickt und es keine vorherigen Seiten gibt (Zeile 57-63).
Nochmal CSS:
/*
* Seite 1
*/
#p1 {
width: 500px;
}
#p1, #p1 .front {
z-index: 6;
}
#p1.flipped, #p1.flipped .back {
z-index: 1;
}
#p1.flipped .front, #p1 .back {
z-index: 0;
}
/*
* Seite 2
*/
#p2 {
width: 550px;
}
#p2, #p2 .front {
z-index: 5;
}
#p2.flipped, #p2.flipped .back {
z-index: 2;
}
#p2.flipped .front, #p2 .back {
z-index: 0;
}
#p2 .back {
left: 550px;
}
Wie oben erwähnt, muss man den z-index beim flippen anpassen, damit nicht die falsche Seite nach oben gesetzt wird. Dazu verändert der sich je nachdem ob das card-div die Klasse „flipped“ hat oder nicht. Die Größen der Karten habe ich auch pro Seite festgelegt, damit es diesen netten Überlagerungseffekt gibt.
Das habe ich für 6 Seiten gemacht, aber prinzipiell gehen da natürlich viel mehr.
Was war mit Safari?
Interessanterweise gab es kaum Probleme zwischen den unterschiedlichen Browsern. Ich entwickle immer mit Firefox und da ging es problemlos. Für Chrome waren lustigerweise ein paar kleine Anpassungen nötig. Für IE 11 war überraschenderweise fast keine Anpassung notwendig.
Nur… es gibt da ja noch einen Browser und da die Dame des Hauses und ihre Familie teilweise auf Apfel-Produkte setzt, kommt da natürlich der Safari zum Einsatz.
Und der hat ein Problem. Wenn man da mit „transform“ arbeitet, ignoriert er den z-index. Das Ergebnis war, das sich die Seiten genau falsch herum überlagert haben. Es sah zum fürchten aus! Der Bug ist auch seit 4 Jahren bekannt…
Was also tun? Wer oben aufmerksam mitgelesen hat, bemerkt, das ich beim flippen einen kleinen Taschenspielertrick angewandt habe. Immer wenn die Seite quasi schon fast umgeblättert ist, blende ich die darunterliegende Seite aus (Zeile 51 und 60 bei Javascript), lasse sie also verschwinden. Was weg ist kann auch nichts überlagern. Beim zurückblättern lasse ich die Seite genauso wieder erscheinen (Zeile 20 bei Javascript).
Leider verliere ich damit einen hübschen Schatteneffekt (ohne diesen Trick sieht man die Schatten der umgeblätterten Seiten, man sieht das bei den nicht umgeblätterten Seiten), aber anders lies es sich nicht machen. Am Schatten erkennt man auch den Trick, wenn man genauer hinschaut. 😉
Demo
Grau ist alle Theorie und ehrlich gesagt kann ich es schriftlich auch gar nicht so genau beschreiben, was ich da gemacht habe. 😉
Daher habe ich eine Demo eingerichtet, bei der man sich das in aller Ruhe ansehen kann:
http://tobyf.de/demos/cardflip/
Source steht unter der „Mir egal, ich such mir mein Zeugs auch überall im Internet zusammen“-Lizenz (alternativ gerne auch unter der Chocolateware- oder Linkware-Lizenz). 😀
Vielleicht spart jemand durch diesen Post ein paar Stunden wenn er/sie vor einem ähnlichen Problem steht.
Bei Fragen einfach einen Kommentar dalassen, ich vervollständige den Artikel dann gerne.
3 thoughts on “Überlappende Divs zum Blättern mit CSS 3”
Comments are closed.
Filed under: Coding - @ 26.05.2015 16:48
Schlagwörter: 3D, CSS 3, Einladung, Hochzeit, HTML, javascript, Karte, Programmieren, Safari, transform, transition, webdev, Webkit
Ich bin wirklich begeistert von der Umsetzung und kann nur sagen, der Aufwand hat sich gelohnt! Danke! 😀
Hilfe, Hilfe, ich versuche schon seit Tagen, diese FlipBook-Idee von dir umzusetzen. Funktioniert jetzt auch soweit bei div. Browsern, außer dem IE10/11. Hier wird ein Foto, das auf der Vorderseite links zu sehen ist, auch nach dem Umblättern noch abgeschnitten rechts angezeigt. Wäre super, wenn du mir helfen könntest. Gruß aus Niederbayern 🙂
Hallo Herbert,
wenn du mir nen Link schickst kann ich es mir gerne mal ansehen! Ich hatte eigentlich Bilder usw im Einsatz und damit keine Probleme?
Einfach kurz ne Mail an blog@tobyf.de!