提问人:Maks Verver 提问时间:11/18/2023 最后编辑:Rory McCrossanMaks Verver 更新时间:11/18/2023 访问量:42
对可折叠侧边栏进行动画处理
Animating a collapsible side bar
问:
我正在尝试创建一个简单的页面,在主要内容的一侧有一个菜单。现在我想使菜单可折叠。
作为概念证明,请考虑以下示例:
#container {
display: flex;
position: fixed;
height: 100%;
}
#menu {
background: yellow;
}
#toggle {
cursor: pointer;
user-select: none
}
#menu.collapsed .content {
display: none;
}
<div id="container">
<div id="menu">
<div id="toggle" onclick="document.getElementById('menu').classList.toggle('collapsed')">X</div>
<div class="content">Menu content</div>
</div>
<div class="content">Main content goes here</div>
</div>
您可以单击 X 来切换菜单,该菜单按预期工作,但过渡是即时的,感觉很不和谐。我想通过慢慢关闭菜单来让它感觉更流畅。
我发现使它工作的一种方法是在菜单上设置显式并使用属性。样式表变为:width
transition: width
这种工作,正如你在这里看到的:
#container {
display: flex;
position: fixed;
height: 100%;
}
#menu {
background: yellow;
}
#toggle {
cursor: pointer;
user-select: none
}
#menu.collapsed .content {
overflow: hidden;
}
#menu .content {
transition: width 1s;
width: 100px;
}
#menu.collapsed .content {
width: 0px;
}
<div id="container">
<div id="menu">
<div id="toggle" onclick="document.getElementById('menu').classList.toggle('collapsed')">X</div>
<div class="content">Menu content</div>
</div>
<div class="content">Main content goes here</div>
</div>
我不太喜欢这个解决方案,原因如下(重要性递减):
我必须猜测初始宽度(在本例中)。我宁愿初始宽度是从内容的自然宽度派生而来的,就像以前发生的那样。但我不能只是删除,因为那样过渡就不再起作用了。而且似乎也无法正常工作。
100px
width: 100px
width: 100%
当菜单关闭时,文本会重排,看起来有点难看。如果可能的话,我想防止这种情况发生。
将菜单保留在 DOM 树中可能比浏览器仍然必须渲染它(也许???)效率低
width: 0px
display: none
有没有更好的方法来动画菜单的关闭/打开?
(我在这里找到了一个相关的问题:仅使用 CSS 的可折叠灵活宽度侧边栏。它使用 instead 而不是 ,这在某种程度上解决了问题 1,但它引入了一个新问题,即如果为高,它实际上会为过渡动画添加延迟。max-width
width
max-width
答:
要对侧边栏进行动画处理,您可以使用 、 和 的组合来获得非常令人信服的结果:transition: width;
position: fixed
white-space: nowrap; overflow: hidden;
function openNav() {
document.getElementById("mySidenav").style.width = "250px";
}
function closeNav() {
document.getElementById("mySidenav").style.width = "0";
}
.sidenav {
height: 100%;
width: 0;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #111;
overflow-x: hidden;
transition: 0.5s;
padding-top: 60px;
}
.sidenav a {
padding: 8px 8px 8px 32px;
text-decoration: none;
font-size: 25px;
color: #818181;
display: block;
transition: 0.3s;
}
.sidenav a:hover {
color: #f1f1f1;
}
.sidenav .closebtn {
position: absolute;
top: 0;
right: 25px;
font-size: 36px;
margin-left: 50px;
}
<div id="mySidenav" class="sidenav">
<a href="javascript:void(0)" class="closebtn" onclick="closeNav()">×</a>
<a href="#">About</a>
<a href="#">Services</a>
<a href="#">Clients</a>
<a href="#">Contact</a>
</div>
<h2>Animated Sidenav Example</h2>
<p>Click on the element below to open the side navigation menu.</p>
<span style="font-size:30px;cursor:pointer" onclick="openNav()">☰ open</span>
评论
overflow: hidden;
如果出于某种原因不想为菜单设置固定宽度,那么您需要在 js 中进行大量操作。它们将被专门用于设置菜单宽度 )) 下面是一个不考虑自适应调整大小的示例,但可以使用您作为起点:
document.querySelectorAll('.menu .toggle').forEach(el => {
el.addEventListener('click', () => {
const container = el.closest('.container');
const menuContent = container.querySelector('.menu-content');
const content = menuContent.querySelector('.content');
if ( container.classList.contains('collapsed') ) {
menuContent.style.width = content.getBoundingClientRect().width + 'px';
} else {
menuContent.style.width = 0;
}
container.classList.toggle('collapsed');
});
})
function setMenuWidth() {
document.querySelectorAll('.menu-content').forEach(el => {
const container = el.closest('.container');
if ( container.classList.contains('collapsed') ) {
return;
}
const menuContent = container.querySelector('.menu-content');
const content = menuContent.querySelector('.content');
const width = content.getBoundingClientRect().width;
content.style.removeProperty('width');
el.style.removeProperty('width');
content.style.width = width + 'px';
el.style.width = width + 'px';
})
}
setMenuWidth();
.container {
display: flex;
position: fixed;
height: 100%;
}
.menu {
background: yellow;
flex:none;
}
.menu .toggle {
cursor: pointer;
user-select: none
}
.menu-content {
transition: width .4s;
overflow: hidden;
}
.menu-content .content {
transition: opacity .4s;
}
.container.collapsed .menu-content .content {
opacity: 0;
}
<div class="container">
<div class="menu">
<div class="toggle">X</div>
<div class="menu-content">
<div class="content">Menu content</div>
</div>
</div>
<div class="content">Main content goes here</div>
</div>
评论