如何用变换替换负边距以实现纸张折叠效果?

时间:2017-04-12 11:22:18

标签: css css3 css-transforms

我试图仅使用CSS实现纸张折叠效果。我在视觉上(几乎)取得了预期的效果,但存在一些重大缺陷;即:

  • 使用盒阴影和负边距会大大降低性能。
  • 负边距不会与转换过渡同步,最终会在转换开始和结束之间的某个位置重叠列表项。

我需要帮助:

  • 重要:使用变换样式替换负边距,有助于更逼真地实现所需的折叠效果,从而提高性能并消除由负边距创建的重叠元素。问题是让所有元素展开并折叠回 Menu 按钮后面,因为transform会相对于它们自己的原始空间移动元素,而不会通过它们的移动影响其他元素,而是留下它们的空间。 / LI>
  • 可选:使用另一种方式替换盒子阴影,以模拟光线撞击边缘,然后在折叠内部褪色,光线通过屏幕进入用户所在的位置,以便通过使用不透明度和伪类来提高性能。

我注意到每一对已经连接并且*链接*如果我移除边距并观察它们折叠而不移动,这要归功于变换原点。也许有一种方法可以实现这一点,以链接元素上方和下方的元素。您可以删除负边距并运行它以了解我的意思。



$('#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>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:2)

这是一个可行的解决方案。

我只在Chrome中测试过它,因为它使用的变量需要现代浏览器。但是你可以很容易地适应它而无需变量。

使用的变换是。我稍后会尝试解释它们。

将鼠标悬停在列表上以折叠

.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>

让我们试着解释变换。

为了让事情变得更容易,我使用2个元素来实现变换:基本li将处理Y运动,a将处理自己的旋转。

对于给定元素,Y平移量由其顶部元素的旋转给出。这可以通过单个元素再现,具有相同高度的这些元素的总和,以相同的速度旋转。

在第一时刻,li被置于Y = 0.然后它被旋转,然后平移与其高度相同的量,并且反向旋转,因为我们只希望该元素达到Y位置而不是旋转。

对于a元素,这更难:-(。我们希望它旋转,并以透视方式进行。但我无法调整以下元素到这种情况,所以我需要它是一个< em>假的透视。这个透视会缩小元素的宽度,但不会缩小高度。在旋转的情况下,有2个点保持不变:旋转轴和透视点。所以我需要先将元素移动到它的旋转边框,在这里应用透视图,返回到原来的位置,然后应用最终旋转。

最后2次轮换解决了CSS变量的问题。我需要

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

但这似乎对Chrome来说太过分了。