How can I replace negative margins with transforms to achieve the effect of paper folding?

I am trying to implement a paper folding effect using only CSS. I achieved the desired effect visually (almost), with some serious flaws; namely:

  • Using shadow and negative margins will significantly reduce performance.
  • Negative margins are out of sync with the transformation and end up overlapping the list items somewhere between the start and end of the transition.

I need help:

  • Important . Replace negative margins with a styling transform to help you achieve the desired folding effect more realistically, improving performance and removing overlapping elements created by negative margins. The problem is that all elements unfold and fold behind the menu button, as the transform moves the elements relative to their own original spaces, without affecting other elements through their movements, leaving their spaces present.
  • Optional : Replace drop shadows with an alternative way of simulating light hitting the edges and then fading out inside the crease, with the light passing through the screen where the user is, to improve performance, possibly using transparency and pseudo-classes.

I noticed that each pair is already connected and * linked * if I remove the fields and watch them add up without moving, thanks to the origin of the transformation. Perhaps there is a way to achieve this, to link both above and below the element. You can remove negative margins and run them to see what I mean.

$('#menu-main-toggle').on('click', function() {
    if ($(this).hasClass('active')) {
      $(this).removeClass('active');      
    } else {
      $(this).addClass('active');
    }
});
      

/* Aesthetic Styling Begin */
ul{margin:0;padding:0;list-style-type:none}#menu-main,#menu-main-toggle{width:75%;margin:auto}#menu-main-toggle,.menu-item{background:#e74c3c;border:none}#menu-main .menu-link,#menu-main-toggle{outline:0;display:block;height:50px;padding:12px;color:#fff;border-top:1px dashed #bf2718;box-sizing:border-box;text-decoration:none;text-align:center}
/* Aesthetic Styling End */

#nav-primary .dropdown-toggle.active + .toggleable > .menu-item {
    margin: 0;
    transform: perspective(320px) rotateX(0deg);
    box-shadow: inset 0 0 20px 0 transparent;
}

#menu-main .menu-item {
    transition: transform .75s ease-in-out, margin .75s ease-in-out, box-shadow .75s ease-in-out;
}
#menu-main .menu-item.odd {
    margin-bottom: -100px;
    transform: perspective(320px) rotateX(-90deg);
    transform-origin: top;
    box-shadow: inset 0 -50px 25px 0 rgba(0, 0, 0, .5);
}
#menu-main .menu-item.even {
    margin-top: -100px;
    transform: perspective(320px) rotateX(90deg);
    transform-origin: bottom;
    box-shadow: inset 0 50px 25px 0 rgba(0, 0, 0, .5);
}
      

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" rel="stylesheet"/>
<nav id="nav-primary" class="text-center">
  <button id="menu-main-toggle" class="dropdown-toggle">Menu</button>
  <ul id="menu-main" class="toggleable">
    <li class="menu-item odd">
      <a class="menu-link" href="#">Lorem</a>
    </li>
    <li class="menu-item even">
      <a class="menu-link" href="#">Ipsum</a>
    </li>
    <li class="menu-item odd">
      <a class="menu-link" href="#">Situs Dolor</a>
    </li>
    <li class="menu-item even">
      <a class="menu-link" href="#">Consectetur</a>
    </li>
    <li class="menu-item odd">
      <a class="menu-link" href="#">Duis</a>
    </li>
    <li class="menu-item even">
      <a class="menu-link" href="#">Vivamus</a>
    </li>
  </ul>
</nav>
      

Run codeHide result


+3


source to share


1 answer


Here's a possible solution.

I only tested it in Chrome as it uses variables that need a modern browser. But you can easily adapt it to work without variables.

The transformations used are rigid . I will try to explain them later.

Hover over the list to fold it

.test {
	--height: 150px;
	--time: 5s;
	--angle: 0deg;
	--angle2: -0deg;
	--bkg1: 0%;
	--bkg2: 100%;
}

.test:hover {
	--angle: 90deg;
	--angle2: -90deg;
	--bkg1: 100%;
	--bkg2: 0%;
}

ul {
	margin-top: 10px;
	width: 70%;
	position: relative;
	list-style: none;
}

li {
	position: absolute;
	width: 100%;
	transition: transform var(--time);
	bottom:  0px;
	transform: rotateX(var(--angle)) translateY(100%) rotateX(var(--angle2));
}

li:nth-child(1) {
	height: 0px;
}
li:nth-child(2), li:nth-child(3) {
	height: calc(2 * var(--height));
}
li:nth-child(4), li:nth-child(5) {
	height: calc(4 * var(--height));
}
li:nth-child(6), li:nth-child(7) {
	height: calc(6 * var(--height));
}

a {
	position: absolute;
	height: var(--height);
	width: 100%;
	display: block;
	font-size: 40px;
	background-size: 100% 300%;
	background-repeat: no-repeat;
	transition: transform var(--time), background-position var(--time);
}

li:nth-child(odd) a {
	top: 100%;
	transform-origin: top;
	transform: rotateX(var(--angle)) translateY(100%) perspective(400px) translateY(-100%) rotateX(var(--angle2)) rotateX(var(--angle2));
	background-image: linear-gradient(to top, rgba(0,0,0,0.6) 30%, transparent 66%);
	background-position: center var(--bkg1);
}
li:nth-child(even) a {
	bottom: 0px;
	transform-origin: bottom;
	transform: rotateX(var(--angle2)) translateY(-100%) perspective(400px) translateY(100%) rotateX(var(--angle)) rotateX(var(--angle));
	background-image: linear-gradient(rgba(0,0,0,0.6) 30%, transparent 66%);
	background-position: center var(--bkg2);
}

li:nth-child(1) a {
	background-color: rgba(200,0,0,0.3);
}
li:nth-child(2) a {
	background-color: rgba(0,200,0,0.3);
}
li:nth-child(3) a {
	background-color: rgba(0,0,200,0.3);
}
li:nth-child(4) a {
	background-color: rgba(200,200,0,0.3);
}
li:nth-child(5) a {
	background-color: rgba(200,0,200,0.3);
}
li:nth-child(6) a {
	background-color: rgba(0,200,200,0.3);
}
      

<ul class="test">
   <li>
      <a>-1-</a>
   </li>
   <li>
      <a>-2-</a>
   </li>
   <li>
      <a>-3-</a>
   </li>
   <li>
      <a>-4-</a>
   </li>
   <li>
      <a>-5-</a>
   </li>
   <li>
      <a>-6-</a>
   </li>
</ul>
      

Run codeHide result


Let's try to explain the transformations.



To make things a little easier, I use 2 elements to achieve the transformation: the base li will handle Y movement, a will handle its own rotation.

For a given element, the amount of translation Y is set by rotating the elements on it. This can be played by one element with the same height, the sum of these elements rotating at the same tempo.

At the first moment, the li is placed at Y = 0. Then it rotates, then translates to the same amount as its height, and rotates counterclockwise, since we want this element to reach only the Y position, not rotation.

For element a it's trickier :-( We want it to rotate and do it in perspective. But I was unable to adjust the following elements in this case, so I need it to be fake perspective. This perspective will reduce the element's width but not the height There are 2 points that remain unchanged when rotated: the axis of rotation and the perspective point, so I need to move the element to this rotating border first, apply perspective here, go back to where it was, and then apply the final rotation.

The last 2 turns solve the problem with CSS variables. I need

rotateX(calc( 2 * var(--angle)))

      

but that seems too big for Chrome.

+2


source







All Articles