汉堡菜单动画导致label容器宽度异常增大的问题
汉堡菜单动画导致label容器宽度异常增大的问题
我完全懂你的困扰——当汉堡菜单转成X的时候,整个容器突然变宽,完全打破了原来的布局范围对吧?看了你贴的代码,问题主要出在容器尺寸随内容变化和元素旋转不改变盒模型尺寸这两个CSS特性上,咱们一步步来改,轻松解决这个问题:
问题根源拆解
你原来的.hamburger-menu用了width: max-content,这会让容器自动适配内部元素的最大宽度;而CSS里元素旋转只是视觉上的变化,元素的盒模型宽高还是原始值,所以当你把伪元素的宽度改成--x-width时,容器就被这个“隐形的原始宽高”给撑开了。另外flex布局下的元素会直接影响容器的尺寸计算,这也是容器变宽的推手。
具体修改方案
下面是针对代码的关键调整,每一步都给你讲清楚原因:
1. 固定容器尺寸,开启相对定位
先把容器的宽高固定为原汉堡的尺寸,同时加上相对定位,方便内部元素用绝对定位脱离文档流:
.hamburger-menu { --x-width: calc(min(var(--bar-width), var(--hamburger-height)) * sqrt(2)); outline: 5px solid black; position: relative; /* 为内部绝对定位元素提供基准 */ width: var(--bar-width); /* 固定为原汉堡宽度 */ height: var(--hamburger-height); /* 固定为原汉堡高度 */ cursor: pointer; background-color: gray; }
这里--x-width的计算改成了基于容器的最小边,完美符合你“宽大于高时,高度为限制因素”的要求——X的长度会是容器最小边的√2倍,刚好是内切正方形的对角线,不会超出容器范围。
2. 内部元素绝对定位,脱离文档流
给伪元素和input加上绝对定位,让它们脱离文档流,这样它们的尺寸变化再也不会影响容器的宽高:
.hamburger-menu::before, .hamburger-menu::after, .hamburger-menu input { content: ""; position: absolute; /* 脱离文档流,不撑开容器 */ width: var(--bar-width); height: var(--bar-height); background-color: cyan; border-radius: 9999px; left: 50%; /* 水平居中 */ transform: translateX(-50%); /* 水平居中偏移 */ transform-origin: center center; /* 从元素中心旋转 */ transition: all var(--time) ease-in-out; /* 统一过渡动画 */ }
3. 调整三条线的初始位置
用绝对定位的top/bottom属性来控制三条线的间距,替代原来的flex gap,精准又不受容器影响:
.hamburger-menu::before { top: var(--hamburger-gap); /* 顶部线距离容器顶部的间隙 */ } .hamburger-menu input { top: 50%; transform: translate(-50%, -50%); /* 中间线垂直水平双居中 */ background-color: red; appearance: none; pointer-events: none; } .hamburger-menu::after { bottom: var(--hamburger-gap); /* 底部线距离容器底部的间隙 */ }
4. 修正checked状态下的X动画
把两条线移到容器中心,旋转成X,同时隐藏中间的线,整个过程容器尺寸纹丝不动:
.hamburger-menu:has(input:checked)::before { width: var(--x-width); rotate: 45deg; top: 50%; transform: translate(-50%, -50%); } .hamburger-menu:has(input:checked)::after { width: var(--x-width); rotate: -45deg; bottom: 50%; transform: translate(-50%, 50%); } .hamburger-menu:has(input:checked) input { opacity: 0; /* 隐藏中间线 */ }
完整修改后的代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hamburger Menu Test</title> <style> :root { --bar-width: 240px; --bar-height: 40px; --hamburger-gap: 40px; --hamburger-height: calc((var(--bar-height) * 3) + (var(--hamburger-gap) * 2)); --time: 1s; } body { margin: 20px; } .hamburger-menu { --x-width: calc(min(var(--bar-width), var(--hamburger-height)) * sqrt(2)); outline: 5px solid black; position: relative; width: var(--bar-width); height: var(--hamburger-height); cursor: pointer; background-color: gray; } .hamburger-menu::before, .hamburger-menu::after, .hamburger-menu input { content: ""; position: absolute; width: var(--bar-width); height: var(--bar-height); background-color: cyan; border-radius: 9999px; left: 50%; transform: translateX(-50%); transform-origin: center center; transition: all var(--time) ease-in-out; } .hamburger-menu::before { top: var(--hamburger-gap); } .hamburger-menu input { top: 50%; transform: translate(-50%, -50%); background-color: red; appearance: none; pointer-events: none; } .hamburger-menu::after { bottom: var(--hamburger-gap); } .hamburger-menu:has(input:checked)::before { width: var(--x-width); rotate: 45deg; top: 50%; transform: translate(-50%, -50%); } .hamburger-menu:has(input:checked)::after { width: var(--x-width); rotate: -45deg; bottom: 50%; transform: translate(-50%, 50%); } .hamburger-menu:has(input:checked) input { opacity: 0; } </style> </head> <body> <label class="hamburger-menu"> <input type="checkbox"> </label> </body> </html>
你可以运行这段代码看看效果——容器的黑框尺寸完全不会变,X会完美限制在原容器范围内,动画过程也丝滑自然,完全符合你的需求~
内容来源于stack exchange
相关产品推荐
相关产品推荐

