导航栏
butterfly_5.3.5
导航栏初始代码,路径:themes\butterfly\layout\includes\header\nav.pug
pug
nav#nav
span#blog-info
a.nav-site-title(href=url_for('/'))
if theme.nav.logo
img.site-icon(src=url_for(theme.nav.logo) alt='Logo')
if theme.nav.display_title
span.site-name=config.title
if globalPageType === 'post'
a.nav-page-title(href=url_for('/'))
span.site-name=(page.title || config.title)
#menus
if theme.search.use
#search-button
span.site-page.social-icon.search
i.fas.fa-search.fa-fw
span= ' ' + _p('search.title')
if theme.menu
!= partial('includes/header/menu_item', {}, {cache: true})
#toggle-menu
span.site-page
i.fas.fa-bars.fa-fw
导航栏左侧添加抽屉菜单
原理:由于子元素能够触发父元素的hover事件,通过绝对定位分离出的子元素抽屉容器不会在鼠标离开父元素后消失,从而实现菜单功能。
- 修改
themes/butterfly/layout/includes/header/nav.pug
diff
nav#nav
span#blog-info
+ if theme.nav.enable
+ .back-home-button
+ i.back-home-button-icon.fas.fa-grip-vertical
+ .back-menu-list-groups
+ each group in theme.nav.menu
+ .back-menu-list-group
+ .back-menu-list-title= group.title
+ .back-menu-list
+ each item in group.item
+ a.back-menu-item(href=url_for(item.link), title=item.name)
+ img.back-menu-item-icon(src=item.icon alt=item.name)
+ span.back-menu-item-text= item.name
- a.nav-site-title(href=url_for('/'))
+ a#blog-info-main(href=url_for('/') title=config.title)
if theme.nav.logo
img.site-icon(src=url_for(theme.nav.logo) alt='Logo')
if theme.nav.display_title
span.site-name=config.title
+ i.fa-solid.fa-house
- if globalPageType === 'post'
- a.nav-page-title(href=url_for('/'))
- span.site-name=(page.title || config.title)
#menus
if theme.search.use
#search-button
span.site-page.social-icon.search
i.fas.fa-search.fa-fw
span= ' ' + _p('search.title')
if theme.menu
!= partial('includes/header/menu_item', {}, {cache: true})
#toggle-menu
span.site-page
i.fas.fa-bars.fa-fw
- css调整
css
#blog-info {
display: flex; /* 使内部项目横向排布 */
overflow: visible !important; /* 溢出部分可见,不遮挡弹出菜单 */
z-index: 102; /* 指定一个z-index值,使其位于.mask-name-container上方 */
}
/* 抽屉按钮调整 */
#blog-info .back-home-button {
width: 35px;
display: flex;
justify-content: center;
margin-right: 4px;
border-radius: 8px;
position: relative; /* 子绝父相 */
}
#blog-info .back-home-button .back-home-button-icon {
line-height: 36.4px;
}
#page-header.not-top-img #nav .back-home-button {
color: var(--font-color);
}
#blog-info .back-home-button .back-menu-list-groups {
position: absolute;
left: 0;
top: 45px;
font-size: 12px;
opacity: 0;
transform: scale(.8);
transform-origin: top left;
transition: 0.1s;
color: #999;
background-color: var(--card-bg);
box-shadow: var(--card-box-shadow);
border-radius: 12px;
pointer-events: none; /* 阻止鼠标事件 */
}
#blog-info .back-home-button:hover .back-menu-list-groups {
opacity: 1; /* 显示时的透明度 */
transform: scale(1); /* 缩放动画效果结束帧 */
transition: 0.3s;
top: 55px;
pointer-events: auto; /* 恢复鼠标事件 */
}
/* 填充按钮和抽屉交界空白部分,使hover连续 */
#blog-info .back-home-button .back-menu-list-groups::before {
position: absolute;
top: -25px;
left: 0;
width: 100%;
height: 30px;
content: "";
}
/* 修改group主轴为垂直方向 */
#blog-info .back-home-button .back-menu-list-group {
display: flex;
flex-direction: column;
}
#blog-info .back-home-button .back-menu-list-group .back-menu-list-title {
margin: 8px 0 0 16px;
}
#blog-info .back-home-button .back-menu-list-group .back-menu-list {
display: flex;
width: 340px; /* 限制列表盒子宽度 */
flex-wrap: wrap; /* 超出宽度换行 */
flex-direction: row; /* 项目水平排列 */
justify-content: space-between; /* 项目之间留间距 */
}
#blog-info .back-home-button .back-menu-list-groups .back-menu-list-group:last-child .back-menu-list {
margin: 0 0 8px;
}
#blog-info .back-menu-list a:hover {
background: var(--text-bg-hover);
}
#blog-info .back-home-button .back-menu-list .back-menu-item {
display: flex;
width: 150px; /* 限制列表项目盒子宽度 */
margin: 4px 8px;
padding: 4px 8px !important;
border-radius: 8px !important;
align-items: center; /* 使内部元素垂直居中 */
}
#blog-info .back-menu-list-groups .back-menu-list .back-menu-item .back-menu-item-icon {
width: 24px;
height: 24px;
border-radius: 24px;
background: white;
}
#blog-info .back-menu-list-groups .back-menu-list .back-menu-item .back-menu-item-text {
font-size: var(--global-font-size);
color: var(--font-color);
margin-left: .5rem;
white-space: nowrap;
}
#blog-info .back-home-button .back-menu-list a:hover .back-menu-item-text {
color: white;
}
#blog-info #blog-info-main {
padding: 0 2px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
transition: .3s;
border-radius: 50px;
}
#blog-info #blog-info-main i {
position: absolute;
color: white;
transition: .3s;
font-size: 1rem;
opacity: 0;
}
/* hover后主页图标显示 */
#blog-info #blog-info-main:hover i {
opacity: 1;
}
/* hover后站点标题文本透明化隐藏 */
#blog-info #blog-info-main:hover .site-name {
opacity: 0;
}
/* 抽屉按钮和站点标题hover背景调整 */
/* 默认hover效果 */
#blog-info #blog-info-main:hover,
#blog-info .back-home-button:hover {
color: white !important;
background: var(--btn-bg);
transition: .3s;
}
/* 首页有头图时默认半透明hover背景 */
.full_page:not(.nav-fixed) #nav #blog-info #blog-info-main:hover,
.full_page:not(.nav-fixed) #nav #blog-info .back-home-button:hover {
background: rgba(255, 255, 255, 0.2);
}
/* 文章页有头图时默认半透明hover背景 */
.post-bg:not(.nav-fixed) #nav #blog-info #blog-info-main:hover,
.post-bg:not(.nav-fixed) #nav #blog-info .back-home-button:hover {
background: rgba(255, 255, 255, 0.2);
}
/* 使用inherit关键字取消#blog-info的hover效果 */
#page-header.nav-fixed #nav #blog-info:hover {
color: inherit !important;
}
/* 将#blog-info-main默认的inline定位改为flex定位 */
#page-header.nav-fixed #nav #blog-info > a:last-child {
display: flex
}
- 修改主题配置文件nav配置项
yml
nav:
enable: true
logo: # image
display_title: true
fixed: true # fixed navigation bar
menu:
- title: 网页
item:
- name: 个人主页
link: https://xdog.run/
icon: https://cloud.tencent.com/favicon.ico
- name: 我的导航
link: https://nav.xdog.run
icon: https://cloud.tencent.com/favicon.ico
- title: 项目
item:
- name: 安知鱼图床
link: https://image.anheyu.com/
icon: https://image.anheyu.com/favicon.ico
菜单栏居中
方法一、flex定位法(推荐)
- 修改
themes\butterfly\layout\includes\header\nav.pug
,新增nav-right
盒子,并将#search-button
和toggle-menu
移入
diff
nav#nav
span#blog-info
a.nav-site-title(href=url_for('/'))
if theme.nav.logo
img.site-icon(src=url_for(theme.nav.logo) alt='Logo')
if theme.nav.display_title
span.site-name=config.title
if globalPageType === 'post'
a.nav-page-title(href=url_for('/'))
span.site-name=(page.title || config.title)
#menus
- if theme.search.use
- #search-button
- span.site-page.social-icon.search
- i.fas.fa-search.fa-fw
- span= ' ' + _p('search.title')
if theme.menu
!= partial('includes/header/menu_item', {}, {cache: true})
- #toggle-menu
- span.site-page
- i.fas.fa-bars.fa-fw
+ #nav-right
+ if theme.search.use
+ #search-button
+ span.site-page.social-icon.search
+ i.fas.fa-search.fa-fw
+ span= ' '
+
+ #toggle-menu
+ span.site-page
+ i.fas.fa-bars.fa-fw
- css调整
css
#nav-right {
display: flex; /* 项目默认继承display: block,改为flex项目 */
flex: 1; /* 与span#blog-info保持一致,确保导航栏左右比例相同 */
justify-content: flex-end; /* 项目靠右排布 */
align-items: center;
z-index: 102;
}
/* 右边按钮调整 */
#nav-right .site-page {
display: block;
text-align: center;
height: 35px;
width: 35px;
border-radius: 50px;
line-height: 36.4px;
font-size: 1.3rem !important;
}
/* 左侧图标调整 */
#blog-info .back-home-button-icon {
line-height: 36.4px;
font-size: 1.3rem !important;
}
/* 清除nav-right下划线hover效果 */
#nav-right .site-page:hover:after {
width: 0 !important;
}
/* hover效果调整,与上一步左边抽屉菜单一致 */
#nav-right .site-page:hover {
color: white !important;
background: var(--btn-bg);
transition: .3s;
}
/* 首页有头图时默认半透明hover背景 */
.full_page:not(.nav-fixed) #nav-right .site-page:hover {
background: rgba(255,255,255,0.2);
}
/* 文章页有头图时默认半透明hover背景 */
.post-bg:not(.nav-fixed) #nav-right .site-page:hover {
background: rgba(255,255,255,0.2);
}
/* 中间菜单文字加粗 */
#nav .menus_items {
font-weight: bold;
}
/* 移动端隐藏菜单栏 */
#nav.hide-menu .menus_items {
display: none !important;
}
方法二、绝对定位法(强制让菜单栏居中)
css
/* 导航栏居中 */
#nav .menus_items {
position: absolute;
left: 50%; /* 元素左边移动到导航栏中心 */
transform: translateX(-50%); /* 元素向左移动自身一半宽度 */
z-index: 1000; /* 防止遮挡,让菜单栏置于最上方 */
}
菜单栏动态文章标题
原理:基于“导航栏居中方法一”。新增一个
#menu
盒子的兄弟盒子位于其下方,显示文章标题,用overflow: hidden;
隐藏, 鼠标滚轮上滑下滑操控nav-visible
类,下滑时nav-visible
移除,改变兄弟盒子里name-container
的CSS属性top,从而改变文章标题位置,使其出现在#menu
位置,而#menu
则通过transform: translateY
移动到原位置上方,加上动画,最终实现浏览器上下滑动翻页效果 其中href="javascript:btf.scrollToDest(0, 500)"
可以实现点击标题跳转到文章开头
- 修改
themes/butterfly/layout/includes/header/nav.pug
,加上mask-name-container
diff
+ if theme.nav.fixed
+ div.mask-name-container
+ center(id="name-container")
+ a(id="page-name" href="javascript:btf.scrollToDest(0, 500)")
+ script(defer).
+ if(document.querySelector('.post-title')) {
+ document.getElementById("page-name").innerText = document.querySelector('.post-title').innerText
+ } else {
+ document.addEventListener("DOMContentLoaded", function() {
+ if(document.querySelector('.post-title')) {
+ document.getElementById("page-name").innerText = document.querySelector('.post-title').innerText;
+ };
+ });
+ };
#menus
- css
css
/* 标题遮罩 */
.post-bg.fixed .mask-name-container {
width: 100%; /* 让容器的宽度与其父元素的宽度一致,通常是导航栏的宽度 */
height: 100%; /* 让容器的高度与其父元素的高度一致,通常是导航栏的高度 */
position: absolute; /* 将容器的定位方式设置为绝对定位,相对于其最近的非 static 定位的父元素定位 */
overflow: hidden; /* 当容器内的内容超出容器尺寸时隐藏超出部分 */
right: 0; /* 贴紧右边,使绝对定位的元素居中 */
}
/* 标题容器 */
.post-bg.fixed #name-container {
position: absolute; /* 使用绝对定位确保下面top属性生效 */
top: 62px; /* 默认在遮罩下方 */
left: 0; /* 设置标题容器距离其父元素左侧的距离为0 */
right: 0; /* 设置标题容器距离其父元素右侧的距离为0,左右同时为0可以自动拉伸到父元素的宽度 */
display: flex; /* 将标题容器的布局方式设置为弹性布局 */
justify-content: center; /* 在标题容器的主轴上居中对齐其子元素(水平方向上居中对齐)*/
align-items: center; /* 在容器的交叉轴上居中对齐其子元素(垂直方向上居中对齐)*/
}
/* 标题样式 */
.post-bg.fixed.nav-fixed #page-name {
font-weight: bold;
font-size: 1.1rem;
padding: 4px 8px;
text-overflow: ellipsis; /* 文字溢出显示省略号 */
overflow: hidden; /* 溢出隐藏 */
white-space: nowrap; /* 不换行 */
}
.post-bg.fixed .menus_items {
display: block; /* 将行级元素转换为块级元素使能transform */
}
/* 鼠标下滑时菜单栏向上隐藏,标题从下方进入导航栏 */
.post-bg.fixed.nav-fixed .menus_items {
transform: translateY(-60px);
transition: .3s; /* 下拉动画效果 */
}
.post-bg.fixed.nav-fixed #name-container {
top: 10px; /* 将标题移动到遮罩内 */
transition: .3s; /* 下拉动画效果 */
}
/* 鼠标上滑时菜单栏向下重新进入导航栏,标题向下方隐藏 */
.post-bg.fixed.nav-fixed.nav-visible .menus_items {
transform: translateY(0);
transition: .3s; /* 上拉动画效果 */
}
.post-bg.fixed.nav-fixed.nav-visible #name-container {
top: 60px; /* 将标题移动到遮罩外 */
}
/* 限制不同视口下标题宽度 */
@media screen and (max-width: 768px) {
.post-bg.fixed.nav-fixed .mask-name-container {
display: none;
}
}
@media screen and (min-width: 768px) {
.post-bg.fixed.nav-fixed #name-container #page-name {
max-width: 15.5rem;
}
}
@media screen and (min-width: 900px) {
.post-bg.fixed.nav-fixed #name-container #page-name {
max-width: 25.5rem;
}
}
@media screen and (min-width: 1200px) {
.post-bg.fixed.nav-fixed #name-container #page-name {
max-width: 35.5rem;
}
}
- hover标题显示“回到顶部”提示
css
/* page-name调整 */
.post-bg.nav-fixed #page-name {
position: relative; /* 自绝父相 */
border-radius: 100px;
min-width: 100px; /* 限制最小宽度 */
}
.post-bg.nav-fixed #page-name:hover {
color: var(--btn-bg);
}
/* 将回到顶部提示框通过after插入 */
.post-bg.nav-fixed #page-name::after {
opacity: 0;
content: "回到顶部";
transition: .2s;
position: absolute;
left: 0;
right: 0;
color: white;
}
/* 视口宽度超过900px时显示回到顶部提示框 */
@media screen and (min-width: 900px) {
.post-bg.nav-fixed #page-name:hover:after {
opacity: 1;
}
.post-bg.nav-fixed #page-name:hover {
background: var(--btn-bg);
color: var(--btn-bg);
}
}
- 修改主题配置文件,固定导航栏
yml
nav:
fixed: true # fixed navigation bar
二级菜单横向排布
css
/* 通过float转换为横向排布 */
.menus_item_child li:not(#sidebar-menus li){
float: left;
}
/* 居中显示 */
.menus_item_child:not(#sidebar-menus ul){
left:50%;
translate:-50%;
}