css - 如何用transform代替负边距实现折纸效果?

标签 css css-transforms

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

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

我需要什么帮助:

  • 重要:将负边距替换为转换样式,这有助于更真实地实现所需的折叠效果,提高性能并删除由负边距创建的重叠元素。问题是所有元素都展开并折叠到 Menu 按钮后面,因为 transform 移动元素相对于它们自己的原始空间,不会通过它们的移动影响其他元素,留下它们的空间。
  • 可选:用另一种方式替换 box-shadow 来模拟光线照射到边缘然后在折叠内消退,光线穿过用户所在的屏幕,以便提高性能,可能是通过使用不透明度和伪类。

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

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

最佳答案

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

我只在 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>fake perspective。这个透视图会缩小元素的宽度,但不会缩小高度。有 2 个点在旋转的情况下保持不变:旋转轴和透视点。所以我需要先将元素移动到它的旋转边界,在此处应用透视图,回到原来的位置,然后应用最后的旋转。

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

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

但这对于 Chrome 来说似乎太多了。

关于css - 如何用transform代替负边距实现折纸效果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43368156/

相关文章:

html - 不同的图像大小取决于视口(viewport)

html - 在导航栏标题中扩展搜索输入

html - 如何将 sigmajs gephi 导出文件上传到服务器? [我需要指向的带有 css 和 index.html 的 javascript 文件]

javascript - 如何使用转换将元素位置从 x1 转换为 x2?

css - chrome 在 css 转换后留下额外的空间

javascript - 当 CSS 变换将元素带入视口(viewport)时,Intersection Observer 无法正常工作

javascript - 为什么我的 javascript 函数没有在我的 div 中被调用?

Jquery css 函数不使用映射中的所有参数

带有伪元素的 Html/Css 三 Angular 形

javascript - 在传单中,如何将标记设置到 map 上的固定位置?