:before和:after元素旋转中心失效问题求助
汉堡菜单转X形动画修复方案
问题描述
实现汉堡菜单转X形动画时,顶部和底部的bar旋转时会偏离中心,即使设置了transform-origin: center也无效,期望三个bar能以按钮中心为轴完成旋转切换。
原始代码
HTML
<body> <div id="menu-btn" onclick="toggleState(this)"> <div id="menu-icon" class=""></div> </div> </body>
CSS
:root { --btn-size: min(8vh, 8vw); --bar-height: calc(var(--btn-size) / 5); --margin-top: calc(var(--bar-height)*2); --margin-bottom: calc(var(--bar-height)*-2); --animation: 1s; --anim-half: calc(var(--animation)/2); --anim-func: ease-in-out; --btn-color: #929292; } body { height: max-content; width: max-content; margin: 5%; } #menu-btn { height: var(--btn-size); width: var(--btn-size); display: flex; align-items: center; cursor: pointer; } #menu-icon, #menu-icon::before, #menu-icon::after { height: var(--bar-height); width: var(--btn-size); background-color: var(--btn-color); position: absolute; transform-origin: center; } #menu-icon { transition: rotate var(--animation) var(--anim-func) 0.00s; } #menu-icon::before { transition: translate var(--anim-half) var(--anim-func) var(--anim-half), rotate var(--anim-half) var(--anim-func) 0.00s; content: ""; transform: translateY(var(--margin-top)); } #menu-icon::after { transition: translate var(--anim-half) var(--anim-func) var(--anim-half), rotate var(--anim-half) var(--anim-func) 0.00s; content: ""; transform: translateY(var(--margin-bottom)); } .animate #menu-icon { transition: rotate var(--animation) var(--anim-func) 0.00s; rotate: 360deg; } .animate #menu-icon::before { transition: translate var(--anim-half) var(--anim-func) 0.00s, rotate var(--anim-half) var(--anim-func) var(--anim-half); translate: 0px var(--margin-bottom); rotate: 45deg; } .animate #menu-icon::after { transition: translate var(--anim-half) var(--anim-func) 0.00s, rotate var(--anim-half) var(--anim-func) var(--anim-half); translate: 0px var(--margin-top); rotate: -45deg; }
JavaScript
function toggleState(target) { target.classList.toggle("animate"); }
修复方案
问题核心是绝对定位元素缺少正确的定位参考,且transform执行顺序导致旋转中心偏离。修复步骤如下:
- 给
#menu-btn和#menu-icon添加定位属性,让伪元素的绝对定位基于按钮容器,而非整个文档 - 用
top/bottom属性替代transform: translateY设置伪元素初始位置,确保旋转中心对齐按钮中心 - 统一设置所有bar的
transform-origin为按钮中心 - 调整动画过渡的顺序和延迟,让位置移动完成后再执行旋转
修改后的完整代码
HTML(无变化)
<body> <div id="menu-btn" onclick="toggleState(this)"> <div id="menu-icon" class=""></div> </div> </body>
CSS
:root { --btn-size: min(8vh, 8vw); --bar-height: calc(var(--btn-size) / 5); --animation: 1s; --anim-half: calc(var(--animation)/2); --anim-func: ease-in-out; --btn-color: #929292; } body { height: max-content; width: max-content; margin: 5%; } #menu-btn { height: var(--btn-size); width: var(--btn-size); display: flex; align-items: center; cursor: pointer; /* 作为按钮内部元素的定位容器 */ position: relative; } #menu-icon { height: var(--bar-height); width: var(--btn-size); background-color: var(--btn-color); /* 作为伪元素的定位参考 */ position: relative; transition: rotate var(--animation) var(--anim-func); transform-origin: center; } #menu-icon::before, #menu-icon::after { content: ""; height: var(--bar-height); width: var(--btn-size); background-color: var(--btn-color); position: absolute; /* 旋转中心设为按钮的中心 */ transform-origin: 50% 50%; left: 0; } #menu-icon::before { top: calc(var(--bar-height) * 2); transition: top var(--anim-half) var(--anim-func), rotate var(--anim-half) var(--anim-func) var(--anim-half); } #menu-icon::after { bottom: calc(var(--bar-height) * 2); transition: bottom var(--anim-half) var(--anim-func), rotate var(--anim-half) var(--anim-func) var(--anim-half); } .animate #menu-icon { rotate: 360deg; } .animate #menu-icon::before { top: 0; rotate: 45deg; transition: top var(--anim-half) var(--anim-func) var(--anim-half), rotate var(--anim-half) var(--anim-func); } .animate #menu-icon::after { bottom: 0; rotate: -45deg; transition: bottom var(--anim-half) var(--anim-func) var(--anim-half), rotate var(--anim-half) var(--anim-func); }
JavaScript(无变化)
function toggleState(target) { target.classList.toggle("animate"); }
说明
修改后,伪元素先通过top/bottom属性移动到按钮中心位置,再围绕按钮中心旋转,完美实现汉堡菜单到X形的平滑切换,不会出现偏移问题。
内容的提问来源于stack exchange,提问作者TheDudeofDC
相关产品推荐
相关产品推荐

