For some time I’ve wanted to create a successor to my Origami UI that included sequentially unfolding panels. Unlike the paper dolls you made in elementary school, these components are folded inside each other, to make for a more interesting dynamic animation as they unfold.
Slightly expanded but still folded up, the elements would appear something like the first illustration. For most photographs in the UI, <figcaption>
elements are used to create the appearance of paper backings.
Because each group of images needs to fold out in sequence, the markup is more complex:
<figure id="dolls" onclick="unwrap()">
<img src="brazillian-indian-child.jpg" alt>
<figure id="dolls-inner1">
<img src="burmese-child-monk.jpg" alt>
<figcaption>Faces Around The World</figcaption>
<figure id="dolls-inner2">
<img src="egyptian-girl.jpg" alt>
<figcaption></figcaption>
<figure id="dolls-inner3">
<img src="lesotho-girl.jpg" alt>
<figcaption></figcaption>
</figure>
</figure>
</figure>
</figure>
The first three <figure>
elements are diagrammed in illustration 2:
<figure>
elements
The containing <div>
is given a reasonable perspective
value, and each <figure>
“hinged” from its left-hand side:
* {
box-sizing: border-box;
}
#dolls {
width: 100%;
font-size: 0;
perspective: 1500px;
}
#dolls:hover {
cursor: pointer;
}
#dolls figure {
margin: 0;
display: inline-block;
transform-origin: left center;
transform-style: preserve-3d;
position: relative;
}
#dolls > img { width: 25%; }
#dolls figure {
transition-duration: 1s;
}
#dolls-inner1 > figcaption {
border: 2px solid #333;
}
#dolls-inner1 figcaption span {
display: block;
font-size: 1.2rem;
}
The initial image is set to ¼th the overall width of the container, and every <figure>
given a transition duration of one second:
#dolls > img { width: 25%; }
#dolls figure { transition-duration: 1s; }
The <figcaption>
elements are used to create the impression of each photograph having a “back” surface by moving them along the Z axis by one pixel.
#dolls-inner1 > figcaption {
border: 2px solid #333;
}
#dolls figcaption {
width: 100%;
height: 100%;
position: absolute;
top: 0; left: 0;
transform: translateZ(-1px);
background-image: url(paper-background.jpg);
background-size: contain;
font-size: 1.4rem;
text-align: center;
padding-top: .5rem;
}
There’s a .rewrap
class that will be applied to the container <div>
, used to roll back the images once they are unwrapped.
.rewrap #dolls-inner1 {
transition-delay: 2s;
}
.rewrap #dolls-inner2 {
transition-delay: 1s;
}
.rewrap #dolls-inner3 {
transition-delay: 0s;
}
If the browser window is at least 751 pixels wide, the unwrapped images will be displayed side-by-side. The deeply nested elements creates an interesting effect on the images inside them, which have to be scaled to compensate:
@media screen and (min-width: 751px) {
#dolls-inner1 {
width: 75%;
transform: rotateY(-175deg);
}
#dolls-inner1 figcaption {
transform: translateZ(-2px) rotateY(180deg);
}
#dolls-inner1 > * {
width: 33.33%;
}
#dolls-inner2 {
width: 50%;
transform: rotateY(-175deg);
}
#dolls-inner2 > * {
width: 66.66%;
}
#dolls-inner3 {
width: 25%;
transform: rotateY(-180deg);
}
#dolls-inner3 > * {
width: 266%;
}
}
When they unwrap, each image group is transitioned until it becomes almost flat:
.unwrap #dolls-inner1,
.unwrap #dolls-inner2,
.unwrap #dolls-inner3 {
transform: rotateY(-1deg);
}
.unwrap #dolls-inner2 {
transition-delay: 1s;
}
.unwrap #dolls-inner3 {
transition-delay: 2s;
}
If browser is less than 750px wide, the images appear stacked on top of each other:
@media screen and (max-width: 750px) {
#dolls {
width: 80%;
margin: 2rem auto;
}
#dolls figure {
transform-origin: center top;
width: 100%; display: block;
}
#dolls-inner1, #dolls > img,
#dolls-inner1 > *, #dolls-inner2 > *,
#dolls-inner3 > * {
width: 100%;
}
#dolls-inner1 {
transform: rotateX(175deg);
}
#dolls-inner1 > figcaption {
transform: translateZ(-1px) rotateX(180deg);
}
#dolls-inner2 {
transform: rotateX(177deg);
}
#dolls-inner3 {
transform: rotateX(178deg);
}
#dolls-inner1 > figcaption {
height: 33%;
}
#dolls-inner2 > figcaption {
height: 50%;
}
#dolls-inner3 > figcaption {
height: 100%;
}
.unwrap #dolls-inner1,
.unwrap #dolls-inner2,
.unwrap #dolls-inner3 {
transform: rotateX(-1deg);
}
unwrap #dolls-inner2 {
transition-delay: 1s;
}
.unwrap #dolls-inner3 {
transition-delay: 2s;
}
}
Finally, JavaScript is used to initiate the unwrapping and rewrapping of the <figure>
elements:
function unwrap(){
if (dolls.classList.length == 0 || dolls.classList.contains("rewrap")) {
dolls.classList.remove("rewrap");
dolls.classList.add("unwrap");
} else {
dolls.classList.remove("unwrap");
dolls.classList.add("rewrap");
}
}
I am certain that there are more effective ways of achieving this result, which I will be exploring in future: I look forward to your comments and feedback!
Photographs by David Lazar
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/JLtvD