Hexo搭建butterfly博客---(五)导航栏美化

Hexo搭建butterfly博客---(五)导航栏美化

预览地址:https://blog.ffing.fun

参考博主:安知鱼

导航栏效果

https://qnimg.ffing.cn/wp-content/uploads/2023/03/image-22.png?imageView2/0/q/75|watermark/1/image/aHR0cHM6Ly9xbmltZy5mZmluZy5jbi9mbl9sb2dvLnBuZw==/dissolve/55/gravity/SouthEast/dx/0/dy/0

为方便操作,直接替换即可

  • 1.导航栏菜单排版
  • 2.创建导航栏nav_menu.css和nav_menu.js文件
  • 3.替换themes/butterfly/source/js/main.js
  • 4.替换themes/butterfly/layout/includes/header/index.pug
  • 5.替换themes/butterfly/layout/includes/header/nav.pug
  • 6.引入css和js,三连

一,导航菜单排版

https://qnimg.ffing.cn/wp-content/uploads/2023/03/image-23.png?imageView2/0/q/75|watermark/1/image/aHR0cHM6Ly9xbmltZy5mZmluZy5jbi9mbl9sb2dvLnBuZw==/dissolve/55/gravity/SouthEast/dx/0/dy/0

二,创建导航栏nav_menu.css和nav_menu.js文件

1.创建《博客目录》/source/css/nav_menu.css,颜色及美化css(套用了一部分安知鱼博主的一部分,自己修改了一部分)




#nav a:hover {
    background: var(--anzhiyu-main);
    transition: 0.3s;
  }
  
  #nav-totop:hover .totopbtn i {
    opacity: 1;
  }
  #nav-totop #percent {
    font-size: 12px;
    background: var(--anzhiyu-white);
    color: var(--anzhiyu-main);
    width: 25px;
    height: 25px;
    border-radius: 35px;
    display: flex;
    justify-content: center;
    align-items: center;
    transition: 0.3s;
  }
  .nav-fixed #nav-totop #percent,
  .page #nav-totop #percent {
    background: var(--font-color);
    color: var(--card-bg);
    font-size: 13px;
  }
  
  #nav-totop {
    width: 35px;
  }
  #page-header:not(.is-top-bar) #percent {
    transition: 0.3s;
  }
  #page-header:not(.is-top-bar) #nav-totop {
    width: 0;
    opacity: 0;
    transition: width 0.3s, opacity 0.2s;
    margin-left: 0 !important;
    
  }
  #nav-totop #percent {
    font-weight: 700;
  }
  #nav-totop:hover #percent {
    opacity: 0;
    transform: scale(1.5);
    font-weight: 700;
  }
  #page-header #nav #nav-right div {
    margin-left: 0.5rem;
    padding: 0;
  }
  
  #nav-totop {
    display: flex;
    align-items: center;
    justify-content: center;
    transition: 0.3s;
    
  }
  .nav-button {
    cursor: pointer;
  }
  div#menus {
    display: flex;
    align-items: center;
    
  }
  #page-header #nav .nav-button a {
    height: 35px;
    width: 35px;
    display: flex;
    align-items: center;
    justify-content: center;
    
  }
  
  #nav .site-page {
    padding-bottom: 0px;
    
  }
  #nav *::after {
    background-color: transparent !important;
    
  }
  
  /* 顶栏修改 */
  #nav .menus_items .menus_item .menus_item_child li a {
    padding: 2px 16px;
  }
  #nav .menus_items .menus_item .menus_item_child li:hover a {
    color: white !important;
    
  }
  
  #nav .menus_items .menus_item .menus_item_child li {
    margin: 6px;
    border-radius: 5px;
    transition: all 0.3s;
    display: inline-block;
    margin: 0 3px;
    
  }
  #nav .menus_items .menus_item .menus_item_child:before {
    top: -19px;
    
  }
  #site-name,
  .shuoshuo {
    white-space: nowrap;
    overflow: hidden;
    
  }
  #site-name {
    padding: 0 8px;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: 0.3s;
    
  }
  
  #blog_name #site-name i {
    opacity: 0;
    position: absolute;
    
  }
  
  #blog_name #site-name:hover .title {
    opacity: 0;
    border-radius: 5px;
  }
  
  #blog_name #site-name:hover i {
    opacity: 1;
    transform: scale(1.01);
    color: white;
  }
  /* 圆角隐藏 */
  ul.menus_item_child {
    border-radius: 5px;
  }
  
  /* 一级菜单居中 */
  #nav .menus_items {
    position: absolute;
    width: fit-content;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    height: 60px;
  }
  
  #nav .menus_items .menus_item:hover .menus_item_child {
    display: block;
    transform: translateX(-50%);
    right: auto;
    left: auto !important;
    padding: 6px 4px;
    box-sizing: content-box;
    line-height: 35px;

  }
  #nav .menus_items .menus_item:hover {
    padding: 0 5px 27px 5px !important;
    margin-bottom: -14.5px !important;
    
  }
  
  #nav .menus_items .menus_item .menus_item_child {
    top: 44px;
  }
  
  @media screen and (min-width: 768px) {
    .page .menus_item:hover > a.site-page {
      color: var(--anzhiyu-white) !important;
      background: var(--anzhiyu-main);
      transition: 0.3s;
      box-shadow: var(--anzhiyu-shadow-main);
      
    }
  }
  
  .nav-fixed #nav {
    transform: translateY(58px) !important;
  }
  #nav {
    padding: 0 calc((100% - 1420px) / 2);
    backdrop-filter: saturate(180%) blur(20px);
  }
  
  #nav a {
    border-radius: 8px;
    color: var(--font-color);
  }
  .page #nav a:hover {
    color: var(--anzhiyu-white) !important;
    background: var(--anzhiyu-main);
    transition: 0.3s;
    box-shadow: var(--anzhiyu-shadow-main);
  }
  
  #menus > div.menus_items > div > a {
    letter-spacing: 0.3rem;
    font-weight: 700;
    padding: 0em 0.3em 0em 0.5em;
    height: 35px;
    line-height: 35px;
  }
  #nav .menus_items .menus_item {
    padding: 0 5px;
    display: flex;
    flex-direction: column;
    margin: auto;
    align-items: center;
  }
  
  #nav div#toggle-menu {
    padding: 2px 0 4px 6px;
  }
  
  #nav-totop .totopbtn i {
    position: absolute;
    display: flex;
    opacity: 0;
  }
  #page-name::before {
    font-size: 18px;
    position: absolute;
    width: 100%;
    height: 100%;
    border-radius: 8px;
    color: white !important;
    top: 0;
    left: 0;
    content: "回到顶部";
    background-color: var(--anzhiyu-theme);
    transition: all 0.3s;
    -webkit-transition: all 0.3s;
    -moz-transition: all 0.3s;
    -ms-transition: all 0.3s;
    -o-transition: all 0.3s;
    opacity: 0;
    box-shadow: 0 0 3px var(--anzhiyu-theme);
    line-height: 45px; /*如果垂直位置不居中可以微调此值,也可以删了*/
  }
  #page-name:hover:before {
    opacity: 1;
  }
  #name-container {
    transition: all 0.3s;
    -webkit-transition: all 0.3s;
    -moz-transition: all 0.3s;
    -ms-transition: all 0.3s;
    -o-transition: all 0.3s;
  }
  #name-container:hover {
    transform: translateX(-50%) scale(1.03);
  }
  #page-name {
    position: relative;
    padding: 10px 30px;
  }
  
  center#name-container {
    position: absolute;
    width: 100%;
    left: 50%;
    transform: translateX(-50%);
    font-family: "ZhuZiAYuanJWD";
  }
  .nav-fixed.nav-visible #name-container {
    transition: 0.3s;
    transform: translate(-50%, 60px);
  }
  .nav-fixed.nav-visible #menus .menus_items {
    transform: translate(-50%);
    transition: 0.3s;
    line-height: 60px;
  }
  .nav-fixed #menus .menus_items {
    transform: translate(-50%, -60px);
    transition: 0.3s;
  }
  .nav-fixed #name-container {
    top: 15%;
    transition: 0.3s;
  }
  #name-container {
    bottom: 60px;
  }
  
  .mask-name-container {
    max-width: 1200px;
    width: 50%;
    height: 100%;
    position: absolute;
    overflow: hidden;
    left: 50%;
    transform: translateX(-50%);
  }
  
  @media screen and (max-width: 992px) {
    .mask-name-container {
      width: 65%;
    }
  }
  
  @media screen and (max-width: 768px) {
    .mask-name-container {
      display: none;
    }
  }
  #sidebar #sidebar-menus .menus_items .site-page:hover {
    color: var(--anzhiyu-white);
    border-radius: var(--anzhiyu-border-radius);
  }
  #nav .menus_items .menus_item > a > i:last-child {
    display: none;
  }
  #nav #search-button {
    font-size: 1.3em;
  }
  
  @media screen and (min-width: 900px) {
    #nav .back-home-button:hover {
      box-shadow: var(--anzhiyu-shadow-main);
    }
  }
  
  .back-home-button:hover {
    background: var(--anzhiyu-main);
    color: var(--anzhiyu-white) !important;
  }
  .back-home-button {
    display: flex;
    width: 35px;
    height: 35px;
    padding: 0 !important;
    align-items: center;
    justify-content: center;
    margin-right: 4px;
    transition: 0.3s;
    border-radius: 8px;
  }
  
  .back-home-button:hover .back-menu-list-groups {
    display: flex;
    opacity: 1;
    transition: 0.3s;
    top: 55px;
    pointer-events: auto;
    left: 0;
  }
  .back-home-button .back-menu-list-groups {
    position: absolute;
    top: 65px;
    left: 0;
    background: var(--anzhiyu-card-bg);
    border-radius: 12px;
    border: var(--style-border);
    flex-direction: column;
    font-size: 12px;
    color: var(--anzhiyu-secondtext);
    box-shadow: var(--anzhiyu-shadow-border);
    transition: 0s;
    opacity: 0;
    pointer-events: none;
  }
  
  .back-home-button .back-menu-list-group {
    display: flex;
    flex-direction: column;
  }
  .back-home-button .back-menu-list-group .back-menu-list-title {
    margin: 8px 0 0 16px;
    transition: 0.3s;
  }
  .back-home-button .back-menu-list {
    display: flex;
    flex-direction: column;
  }
  .back-home-button .back-menu-list::before {
    position: absolute;
    top: -22px;
    left: 0px;
    width: 100%;
    height: 25px;
    content: "";
  }
  
  .back-home-button .back-menu-list-group:hover .back-menu-list-title {
    color: var(--anzhiyu-main);
  }
  .back-home-button .back-menu-list-groups:hover {
    border: var(--style-border-hover);
  }
  .back-home-button .back-menu-list .back-menu-item {
    display: flex;
    align-items: center;
    margin: 4px 8px;
    padding: 4px 8px !important;
    transition: 0.3s;
    border-radius: 8px;
  }
  .back-home-button .back-menu-list .back-menu-item .back-menu-item-text {
    font-size: var(--global-font-size);
    margin-left: 0.5rem;
    color: var(--anzhiyu-fontcolor);
    white-space: nowrap;
  }
  #nav #blog_name {
    flex-wrap: nowrap;
    height: 60px;
    display: flex;
    align-items: center;
    /* z-index: 102; */
    transition: 0.3s;
  }
  .back-home-button .back-menu-list .back-menu-item .back-menu-item-icon {
    width: 24px;
    height: 24px;
    border-radius: 24px;
    background: var(--anzhiyu-secondbg);
  }
  #page-header #nav .back-home-button {
    cursor: pointer;
    position: relative;
  }
  
  @media screen and (min-width: 1300px) {
    #nav a:hover {
      transform: scale(1.03);
    }
  }
  .back-home-button .back-menu-list .back-menu-item:hover .back-menu-item-text {
    color: var(--anzhiyu-white);
  }
  .back-menu-item-icon.loading img {
    width: 25px;
  }
  
  #page-header #nav #menus .nav-button.long a.totopbtn,
  #page-header #nav #menus .nav-button.long,
  #page-header #nav #menus .nav-button.long a.totopbtn span {
    width: 70px;
  }
  #page-header #nav #menus .nav-button.long a.totopbtn span {
    border-radius: 35px;
    display: flex;
    justify-content: center;
    align-items: center;
    transition: 0.3s;
    white-space: nowrap;
  }
  
  #page-header #nav #menus .nav-button.long a.totopbtn:hover {
    border-radius: 35px;
    height: 30px;
  }
  
  #nav #search-button {
    padding-left: 0;
  }
  #page-header #nav .nav-button {
    margin-left: 0.5rem;
    padding: 0;
  }
  #page-header:not(.is-top-bar) #nav-totop a {
    display: none;
  }
  #search-button a.site-page.social-icon.search span {
    display: none;
  }

2.创建《博客目录》/source/js/nav_menu.js

// 返回顶部 显示网页阅读进度
window.onscroll = percent; // 执行函数
// 页面百分比
function percent() {
  let a = document.documentElement.scrollTop || window.pageYOffset, // 卷去高度
    b =
      Math.max(
        document.body.scrollHeight,
        document.documentElement.scrollHeight,
        document.body.offsetHeight,
        document.documentElement.offsetHeight,
        document.body.clientHeight,
        document.documentElement.clientHeight
      ) - document.documentElement.clientHeight, // 整个网页高度 减去 可视高度
    result = Math.round((a / b) * 100), // 计算百分比
    btn = document.querySelector("#percent"); // 获取图标

  result <= 99 || (result = 99), (btn.innerHTML = result);
}

document.getElementById("page-name").innerText = document.title.split(" | FFing")[0];

三,替换themes/butterfly/source/js/main.js

document.addEventListener('DOMContentLoaded', function () {
  let blogNameWidth, menusWidth, searchWidth, $nav
  let mobileSidebarOpen = false

  const adjustMenu = (init) => {
    if (init) {
      blogNameWidth = document.getElementById('site-name').offsetWidth
      const $menusEle = document.querySelectorAll('#menus .menus_item')
      menusWidth = 0
      $menusEle.length && $menusEle.forEach(i => { menusWidth += i.offsetWidth })
      const $searchEle = document.querySelector('#search-button')
      searchWidth = $searchEle ? $searchEle.offsetWidth : 0
      $nav = document.getElementById('nav')
    }

    let hideMenuIndex = ''
    if (window.innerWidth <= 768) hideMenuIndex = true
    else hideMenuIndex = blogNameWidth + menusWidth + searchWidth > $nav.offsetWidth - 120

    if (hideMenuIndex) {
      $nav.classList.add('hide-menu')
    } else {
      $nav.classList.remove('hide-menu')
    }
  }

  // 初始化header
  const initAdjust = () => {
    adjustMenu(true)
    $nav.classList.add('show')
  }

  // sidebar menus
  const sidebarFn = {
    open: () => {
      btf.sidebarPaddingR()
      document.body.style.overflow = 'hidden'
      btf.animateIn(document.getElementById('menu-mask'), 'to_show 0.5s')
      document.getElementById('sidebar-menus').classList.add('open')
      mobileSidebarOpen = true
    },
    close: () => {
      const $body = document.body
      $body.style.overflow = ''
      $body.style.paddingRight = ''
      btf.animateOut(document.getElementById('menu-mask'), 'to_hide 0.5s')
      document.getElementById('sidebar-menus').classList.remove('open')
      mobileSidebarOpen = false
    }
  }

  /**
   * 首頁top_img底下的箭頭
   */
  const scrollDownInIndex = () => {
    const $scrollDownEle = document.getElementById('scroll-down')
    $scrollDownEle && $scrollDownEle.addEventListener('click', function () {
      btf.scrollToDest(document.getElementById('content-inner').offsetTop, 300)
    })
  }

  /**
   * 代碼
   * 只適用於Hexo默認的代碼渲染
   */
  const addHighlightTool = function () {
    const highLight = GLOBAL_CONFIG.highlight
    if (!highLight) return

    const isHighlightCopy = highLight.highlightCopy
    const isHighlightLang = highLight.highlightLang
    const isHighlightShrink = GLOBAL_CONFIG_SITE.isHighlightShrink
    const highlightHeightLimit = highLight.highlightHeightLimit
    const isShowTool = isHighlightCopy || isHighlightLang || isHighlightShrink !== undefined
    const $figureHighlight = highLight.plugin === 'highlighjs' ? document.querySelectorAll('figure.highlight') : document.querySelectorAll('pre[class*="language-"]')

    if (!((isShowTool || highlightHeightLimit) && $figureHighlight.length)) return

    const isPrismjs = highLight.plugin === 'prismjs'

    let highlightShrinkEle = ''
    let highlightCopyEle = ''
    const highlightShrinkClass = isHighlightShrink === true ? 'closed' : ''

    if (isHighlightShrink !== undefined) {
      highlightShrinkEle = `<i class="fas fa-angle-down expand ${highlightShrinkClass}"></i>`
    }

    if (isHighlightCopy) {
      highlightCopyEle = '<div class="copy-notice"></div><i class="fas fa-paste copy-button"></i>'
    }

    const copy = (text, ctx) => {
      if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
        document.execCommand('copy')
        if (GLOBAL_CONFIG.Snackbar !== undefined) {
          btf.snackbarShow(GLOBAL_CONFIG.copy.success)
        } else {
          const prevEle = ctx.previousElementSibling
          prevEle.innerText = GLOBAL_CONFIG.copy.success
          prevEle.style.opacity = 1
          setTimeout(() => { prevEle.style.opacity = 0 }, 700)
        }
      } else {
        if (GLOBAL_CONFIG.Snackbar !== undefined) {
          btf.snackbarShow(GLOBAL_CONFIG.copy.noSupport)
        } else {
          ctx.previousElementSibling.innerText = GLOBAL_CONFIG.copy.noSupport
        }
      }
    }

    // click events
    const highlightCopyFn = (ele) => {
      const $buttonParent = ele.parentNode
      $buttonParent.classList.add('copy-true')
      const selection = window.getSelection()
      const range = document.createRange()
      if (isPrismjs) range.selectNodeContents($buttonParent.querySelectorAll('pre code')[0])
      else range.selectNodeContents($buttonParent.querySelectorAll('table .code pre')[0])
      selection.removeAllRanges()
      selection.addRange(range)
      const text = selection.toString()
      copy(text, ele.lastChild)
      selection.removeAllRanges()
      $buttonParent.classList.remove('copy-true')
    }

    const highlightShrinkFn = (ele) => {
      const $nextEle = [...ele.parentNode.children].slice(1)
      ele.firstChild.classList.toggle('closed')
      if (btf.isHidden($nextEle[$nextEle.length - 1])) {
        $nextEle.forEach(e => { e.style.display = 'block' })
      } else {
        $nextEle.forEach(e => { e.style.display = 'none' })
      }
    }

    const highlightToolsFn = function (e) {
      const $target = e.target.classList
      if ($target.contains('expand')) highlightShrinkFn(this)
      else if ($target.contains('copy-button')) highlightCopyFn(this)
    }

    const expandCode = function () {
      this.classList.toggle('expand-done')
    }

    function createEle (lang, item, service) {
      const fragment = document.createDocumentFragment()

      if (isShowTool) {
        const hlTools = document.createElement('div')
        hlTools.className = `highlight-tools ${highlightShrinkClass}`
        hlTools.innerHTML = highlightShrinkEle + lang + highlightCopyEle
        hlTools.addEventListener('click', highlightToolsFn)
        fragment.appendChild(hlTools)
      }

      if (highlightHeightLimit && item.offsetHeight > highlightHeightLimit + 30) {
        const ele = document.createElement('div')
        ele.className = 'code-expand-btn'
        ele.innerHTML = '<i class="fas fa-angle-double-down"></i>'
        ele.addEventListener('click', expandCode)
        fragment.appendChild(ele)
      }

      if (service === 'hl') {
        item.insertBefore(fragment, item.firstChild)
      } else {
        item.parentNode.insertBefore(fragment, item)
      }
    }

    if (isHighlightLang) {
      if (isPrismjs) {
        $figureHighlight.forEach(function (item) {
          const langName = item.getAttribute('data-language') ? item.getAttribute('data-language') : 'Code'
          const highlightLangEle = `<div class="code-lang">${langName}</div>`
          btf.wrap(item, 'figure', { class: 'highlight' })
          createEle(highlightLangEle, item)
        })
      } else {
        $figureHighlight.forEach(function (item) {
          let langName = item.getAttribute('class').split(' ')[1]
          if (langName === 'plain' || langName === undefined) langName = 'Code'
          const highlightLangEle = `<div class="code-lang">${langName}</div>`
          createEle(highlightLangEle, item, 'hl')
        })
      }
    } else {
      if (isPrismjs) {
        $figureHighlight.forEach(function (item) {
          btf.wrap(item, 'figure', { class: 'highlight' })
          createEle('', item)
        })
      } else {
        $figureHighlight.forEach(function (item) {
          createEle('', item, 'hl')
        })
      }
    }
  }

  /**
   * PhotoFigcaption
   */
  function addPhotoFigcaption () {
    document.querySelectorAll('#article-container img').forEach(function (item) {
      const parentEle = item.parentNode
      const altValue = item.title || item.alt
      if (altValue && !parentEle.parentNode.classList.contains('justified-gallery')) {
        const ele = document.createElement('div')
        ele.className = 'img-alt is-center'
        ele.textContent = altValue
        parentEle.insertBefore(ele, item.nextSibling)
      }
    })
  }

  /**
   * Lightbox
   */
  const runLightbox = () => {
    btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))
  }

  /**
   * justified-gallery 圖庫排版
   */
  const runJustifiedGallery = function (ele) {
    ele.forEach(item => {
      const $imgList = item.querySelectorAll('img')

      $imgList.forEach(i => {
        const dataLazySrc = i.getAttribute('data-lazy-src')
        if (dataLazySrc) i.src = dataLazySrc
        btf.wrap(i, 'div', { class: 'fj-gallery-item' })
      })
    })

    if (window.fjGallery) {
      setTimeout(() => { btf.initJustifiedGallery(ele) }, 100)
      return
    }

    const newEle = document.createElement('link')
    newEle.rel = 'stylesheet'
    newEle.href = GLOBAL_CONFIG.source.justifiedGallery.css
    document.body.appendChild(newEle)
    getScript(`${GLOBAL_CONFIG.source.justifiedGallery.js}`).then(() => { btf.initJustifiedGallery(ele) })
  }

  /**
   * 滾動處理
   */
  const scrollFn = function () {
    const $rightside = document.getElementById('rightside')
    const innerHeight = window.innerHeight + 56

    // 當滾動條小于 56 的時候
    if (document.body.scrollHeight <= innerHeight) {
      $rightside.style.cssText = 'opacity: 1; transform: translateX(-58px)'
      return
    }

    // find the scroll direction
    function scrollDirection (currentTop) {
      const result = currentTop > initTop // true is down & false is up
      initTop = currentTop
      return result
    }

    let initTop = 0
    let isChatShow = true
    const $header = document.getElementById('page-header')
    const isChatBtnHide = typeof chatBtnHide === 'function'
    const isChatBtnShow = typeof chatBtnShow === 'function'

    const scrollTask = btf.throttle(() => {
        const currentTop = window.scrollY || document.documentElement.scrollTop
        const isDown = scrollDirection(currentTop)
        if (currentTop > 56) {
          $header.classList.add('is-top-bar')
          if (isDown) {
            if ($header.classList.contains('nav-visible')) $header.classList.remove('nav-visible')
            if (isChatBtnShow && isChatShow === true) {
              chatBtnHide()
              isChatShow = false
            }
          } else {
            if (!$header.classList.contains('nav-visible')) $header.classList.add('nav-visible')
            if (isChatBtnHide && isChatShow === false) {
              chatBtnShow()
              isChatShow = true
            }
          }
          $header.classList.add('nav-fixed')
          if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
            $rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
          }
        } else {
          if (currentTop === 0) {

            $header.classList.remove('is-top-bar')
          }
          $rightside.style.cssText = "opacity: ''; transform: ''"
        }

        if (document.body.scrollHeight <= innerHeight) {
          $rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
        }
      }, 200)
    
    window.scrollCollect = scrollTask

    window.addEventListener('scroll', scrollCollect)
  }

  /**
  * toc,anchor
  */
  const scrollFnToDo = function () {
    const isToc = GLOBAL_CONFIG_SITE.isToc
    const isAnchor = GLOBAL_CONFIG.isAnchor
    const $article = document.getElementById('article-container')

    if (!($article && (isToc || isAnchor))) return

    let $tocLink, $cardToc, scrollPercent, autoScrollToc, isExpand

    if (isToc) {
      const $cardTocLayout = document.getElementById('card-toc')
      $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
      $tocLink = $cardToc.querySelectorAll('.toc-link')
      const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
      isExpand = $cardToc.classList.contains('is-expand')

      scrollPercent = currentTop => {
        const docHeight = $article.clientHeight
        const winHeight = document.documentElement.clientHeight
        const headerHeight = $article.offsetTop
        const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
        const scrollPercent = (currentTop - headerHeight) / (contentMath)
        const scrollPercentRounded = Math.round(scrollPercent * 100)
        const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded
        $tocPercentage.textContent = percentage
      }

      window.mobileToc = {
        open: () => {
          $cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
        },

        close: () => {
          $cardTocLayout.style.animation = 'toc-close .2s'
          setTimeout(() => {
            $cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
          }, 100)
        }
      }

      // toc元素點擊
      $cardToc.addEventListener('click', e => {
        e.preventDefault()
        const target = e.target.classList
        if (target.contains('toc-content')) return
        const $target = target.contains('toc-link')
          ? e.target
          : e.target.parentElement
        btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300)
        if (window.innerWidth < 900) {
          window.mobileToc.close()
        }
      })

      autoScrollToc = item => {
        const activePosition = item.getBoundingClientRect().top
        const sidebarScrollTop = $cardToc.scrollTop
        if (activePosition > (document.documentElement.clientHeight - 100)) {
          $cardToc.scrollTop = sidebarScrollTop + 150
        }
        if (activePosition < 100) {
          $cardToc.scrollTop = sidebarScrollTop - 150
        }
      }
    }

    // find head position & add active class
    const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
    let detectItem = ''
    const findHeadPosition = function (top) {
      if (top === 0) {
        return false
      }

      let currentId = ''
      let currentIndex = ''

      list.forEach(function (ele, index) {
        if (top > btf.getEleTop(ele) - 80) {
          const id = ele.id
          currentId = id ? '#' + encodeURI(id) : ''
          currentIndex = index
        }
      })

      if (detectItem === currentIndex) return

      if (isAnchor) btf.updateAnchor(currentId)

      detectItem = currentIndex

      if (isToc) {
        $cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })

        if (currentId === '') {
          return
        }

        const currentActive = $tocLink[currentIndex]
        currentActive.classList.add('active')

        setTimeout(() => {
          autoScrollToc(currentActive)
        }, 0)

        if (isExpand) return
        let parent = currentActive.parentNode

        for (; !parent.matches('.toc'); parent = parent.parentNode) {
          if (parent.matches('li')) parent.classList.add('active')
        }
      }
    }

    // main of scroll
    window.tocScrollFn = function () {
      return btf.throttle(function () {
        const currentTop = window.scrollY || document.documentElement.scrollTop
        isToc && scrollPercent(currentTop)
        findHeadPosition(currentTop)
      }, 100)()
    }
    window.addEventListener('scroll', tocScrollFn)
  }

  /**
   * Rightside
   */
  const rightSideFn = {
    switchReadMode: () => { // read-mode
      const $body = document.body
      $body.classList.add('read-mode')
      const newEle = document.createElement('button')
      newEle.type = 'button'
      newEle.className = 'fas fa-sign-out-alt exit-readmode'
      $body.appendChild(newEle)

      function clickFn () {
        $body.classList.remove('read-mode')
        newEle.remove()
        newEle.removeEventListener('click', clickFn)
      }

      newEle.addEventListener('click', clickFn)
    },
    switchDarkMode: () => { // Switch Between Light And Dark Mode
      const nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'
      if (nowMode === 'light') {
        activateDarkMode()
        saveToLocal.set('theme', 'dark', 2)
        GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
      } else {
        activateLightMode()
        saveToLocal.set('theme', 'light', 2)
        GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
      }
      // handle some cases
      typeof utterancesTheme === 'function' && utterancesTheme()
      typeof changeGiscusTheme === 'function' && changeGiscusTheme()
      typeof FB === 'object' && window.loadFBComment && window.loadFBComment()
      typeof runMermaid === 'function' && window.runMermaid()
    },
    showOrHideBtn: (e) => { // rightside 點擊設置 按鈕 展開
      const rightsideHideClassList = document.getElementById('rightside-config-hide').classList
      rightsideHideClassList.toggle('show')
      if (e.classList.contains('show')) {
        rightsideHideClassList.add('status')
        setTimeout(() => {
          rightsideHideClassList.remove('status')
        }, 300)
      }
      e.classList.toggle('show')
    },
    scrollToTop: () => { // Back to top
      btf.scrollToDest(0, 500)
    },
    hideAsideBtn: () => { // Hide aside
      const $htmlDom = document.documentElement.classList
      $htmlDom.contains('hide-aside')
        ? saveToLocal.set('aside-status', 'show', 2)
        : saveToLocal.set('aside-status', 'hide', 2)
      $htmlDom.toggle('hide-aside')
    },

    runMobileToc: () => {
      if (window.getComputedStyle(document.getElementById('card-toc')).getPropertyValue('opacity') === '0') window.mobileToc.open()
      else window.mobileToc.close()
    }
  }

  document.getElementById('rightside').addEventListener('click', function (e) {
    const $target = e.target.id ? e.target : e.target.parentNode
    switch ($target.id) {
      case 'go-up':
        rightSideFn.scrollToTop()
        break
      case 'rightside_config':
        rightSideFn.showOrHideBtn($target)
        break
      case 'mobile-toc-button':
        rightSideFn.runMobileToc()
        break
      case 'readmode':
        rightSideFn.switchReadMode()
        break
      case 'darkmode':
        rightSideFn.switchDarkMode()
        break
      case 'hide-aside-btn':
        rightSideFn.hideAsideBtn()
        break
      default:
        break
    }
  })

  /**
   * menu
   * 側邊欄sub-menu 展開/收縮
   */
  const clickFnOfSubMenu = () => {
    document.querySelectorAll('#sidebar-menus .site-page.group').forEach(function (item) {
      item.addEventListener('click', function () {
        this.classList.toggle('hide')
      })
    })
  }

  /**
   * 複製時加上版權信息
   */
  const addCopyright = () => {
    const copyright = GLOBAL_CONFIG.copyright
    document.body.oncopy = (e) => {
      e.preventDefault()
      let textFont; const copyFont = window.getSelection(0).toString()
      if (copyFont.length > copyright.limitCount) {
        textFont = copyFont + '\n' + '\n' + '\n' +
        copyright.languages.author + '\n' +
        copyright.languages.link + window.location.href + '\n' +
        copyright.languages.source + '\n' +
        copyright.languages.info
      } else {
        textFont = copyFont
      }
      if (e.clipboardData) {
        return e.clipboardData.setData('text', textFont)
      } else {
        return window.clipboardData.setData('text', textFont)
      }
    }
  }

  /**
   * 網頁運行時間
   */
  const addRuntime = () => {
    const $runtimeCount = document.getElementById('runtimeshow')
    if ($runtimeCount) {
      const publishDate = $runtimeCount.getAttribute('data-publishDate')
      $runtimeCount.innerText = btf.diffDate(publishDate) + ' ' + GLOBAL_CONFIG.runtime
    }
  }

  /**
   * 最後一次更新時間
   */
  const addLastPushDate = () => {
    const $lastPushDateItem = document.getElementById('last-push-date')
    if ($lastPushDateItem) {
      const lastPushDate = $lastPushDateItem.getAttribute('data-lastPushDate')
      $lastPushDateItem.innerText = btf.diffDate(lastPushDate, true)
    }
  }

  /**
   * table overflow
   */
  const addTableWrap = () => {
    const $table = document.querySelectorAll('#article-container :not(.highlight) > table, #article-container > table')
    if ($table.length) {
      $table.forEach(item => {
        btf.wrap(item, 'div', { class: 'table-wrap' })
      })
    }
  }

  /**
   * tag-hide
   */
  const clickFnOfTagHide = function () {
    const $hideInline = document.querySelectorAll('#article-container .hide-button')
    if ($hideInline.length) {
      $hideInline.forEach(function (item) {
        item.addEventListener('click', function (e) {
          const $this = this
          $this.classList.add('open')
          const $fjGallery = $this.nextElementSibling.querySelectorAll('.fj-gallery')
          $fjGallery.length && btf.initJustifiedGallery($fjGallery)
        })
      })
    }
  }

  const tabsFn = {
    clickFnOfTabs: function () {
      document.querySelectorAll('#article-container .tab > button').forEach(function (item) {
        item.addEventListener('click', function (e) {
          const $this = this
          const $tabItem = $this.parentNode

          if (!$tabItem.classList.contains('active')) {
            const $tabContent = $tabItem.parentNode.nextElementSibling
            const $siblings = btf.siblings($tabItem, '.active')[0]
            $siblings && $siblings.classList.remove('active')
            $tabItem.classList.add('active')
            const tabId = $this.getAttribute('data-href').replace('#', '')
            const childList = [...$tabContent.children]
            childList.forEach(item => {
              if (item.id === tabId) item.classList.add('active')
              else item.classList.remove('active')
            })
            const $isTabJustifiedGallery = $tabContent.querySelectorAll(`#${tabId} .fj-gallery`)
            if ($isTabJustifiedGallery.length > 0) {
              btf.initJustifiedGallery($isTabJustifiedGallery)
            }
          }
        })
      })
    },
    backToTop: () => {
      document.querySelectorAll('#article-container .tabs .tab-to-top').forEach(function (item) {
        item.addEventListener('click', function () {
          btf.scrollToDest(btf.getEleTop(btf.getParents(this, '.tabs')), 300)
        })
      })
    }
  }

  const toggleCardCategory = function () {
    const $cardCategory = document.querySelectorAll('#aside-cat-list .card-category-list-item.parent i')
    if ($cardCategory.length) {
      $cardCategory.forEach(function (item) {
        item.addEventListener('click', function (e) {
          e.preventDefault()
          const $this = this
          $this.classList.toggle('expand')
          const $parentEle = $this.parentNode.nextElementSibling
          if (btf.isHidden($parentEle)) {
            $parentEle.style.display = 'block'
          } else {
            $parentEle.style.display = 'none'
          }
        })
      })
    }
  }

  const switchComments = function () {
    let switchDone = false
    const $switchBtn = document.querySelector('#comment-switch > .switch-btn')
    $switchBtn && $switchBtn.addEventListener('click', function () {
      this.classList.toggle('move')
      document.querySelectorAll('#post-comment > .comment-wrap > div').forEach(function (item) {
        if (btf.isHidden(item)) {
          item.style.cssText = 'display: block;animation: tabshow .5s'
        } else {
          item.style.cssText = "display: none;animation: ''"
        }
      })

      if (!switchDone && typeof loadOtherComment === 'function') {
        switchDone = true
        loadOtherComment()
      }
    })
  }

  const addPostOutdateNotice = function () {
    const data = GLOBAL_CONFIG.noticeOutdate
    const diffDay = btf.diffDate(GLOBAL_CONFIG_SITE.postUpdate)
    if (diffDay >= data.limitDay) {
      const ele = document.createElement('div')
      ele.className = 'post-outdate-notice'
      ele.textContent = data.messagePrev + ' ' + diffDay + ' ' + data.messageNext
      const $targetEle = document.getElementById('article-container')
      if (data.position === 'top') {
        $targetEle.insertBefore(ele, $targetEle.firstChild)
      } else {
        $targetEle.appendChild(ele)
      }
    }
  }

  const lazyloadImg = () => {
    window.lazyLoadInstance = new LazyLoad({
      elements_selector: 'img',
      threshold: 0,
      data_src: 'lazy-src'
    })
  }

  const relativeDate = function (selector) {
    selector.forEach(item => {
      const $this = item
      const timeVal = $this.getAttribute('datetime')
      $this.innerText = btf.diffDate(timeVal, true)
      $this.style.display = 'inline'
    })
  }

  const unRefreshFn = function () {
    window.addEventListener('resize', () => {
      adjustMenu(false)
      btf.isHidden(document.getElementById('toggle-menu')) && mobileSidebarOpen && sidebarFn.close()
    })

    document.getElementById('menu-mask').addEventListener('click', e => { sidebarFn.close() })

    clickFnOfSubMenu()
    GLOBAL_CONFIG.islazyload && lazyloadImg()
    GLOBAL_CONFIG.copyright !== undefined && addCopyright()
  }

  window.refreshFn = function () {
    initAdjust()

    if (GLOBAL_CONFIG_SITE.isPost) {
      GLOBAL_CONFIG.noticeOutdate !== undefined && addPostOutdateNotice()
      GLOBAL_CONFIG.relativeDate.post && relativeDate(document.querySelectorAll('#post-meta time'))
    } else {
      GLOBAL_CONFIG.relativeDate.homepage && relativeDate(document.querySelectorAll('#recent-posts time'))
      GLOBAL_CONFIG.runtime && addRuntime()
      addLastPushDate()
      toggleCardCategory()
    }

    scrollFnToDo()
    GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex()
    addHighlightTool()
    GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption()
    scrollFn()

    const $jgEle = document.querySelectorAll('#article-container .fj-gallery')
    $jgEle.length && runJustifiedGallery($jgEle)

    runLightbox()
    addTableWrap()
    clickFnOfTagHide()
    tabsFn.clickFnOfTabs()
    tabsFn.backToTop()
    switchComments()
    document.getElementById('toggle-menu').addEventListener('click', () => { sidebarFn.open() })
  }

  refreshFn()
  unRefreshFn()
})

四,替换themes/butterfly/layout/includes/header/index.pug

if !theme.disable_top_img && page.top_img !== false
  if is_post()
    - var top_img = page.top_img || page.cover || page.randomcover
  else if is_page()
    - var top_img = page.top_img || theme.default_top_img
  else if is_tag()
    - var top_img = theme.tag_per_img && theme.tag_per_img[page.tag] 
    - top_img = top_img ? top_img : (theme.tag_img !== false ? theme.tag_img || theme.default_top_img : false)
  else if is_category()
    - var top_img = theme.category_per_img && theme.category_per_img[page.category]
    - top_img = top_img ? top_img : (theme.category_img !== false ? theme.category_img || theme.default_top_img : false)
  else if is_home()
    - var top_img = theme.index_img !== false ? theme.index_img || theme.default_top_img : false
  else if is_archive()
    - var top_img = theme.archive_img !== false ? theme.archive_img || theme.default_top_img : false
  else
    - var top_img = page.top_img || theme.default_top_img

  if top_img !== false
    - var imgSource = top_img && top_img.indexOf('/') !== -1 ? `background-image: url('${url_for(top_img)}')` : `background: ${top_img}`
    - var bg_img = top_img ? imgSource : ''
    - var site_title = page.title || page.tag || page.category || config.title

    - var isHomeClass = is_home() ? 'full_page nav-fixed nav-visible' : 'not-home-page'
    - is_post() ? isHomeClass = 'post-bg' : isHomeClass
  else
    - var isHomeClass = 'not-top-img'
else
  - var top_img = false
  - var isHomeClass = 'not-top-img'

header#page-header(class=isHomeClass style=bg_img)
  !=partial('includes/header/nav', {}, {cache: true})
  if top_img !== false
    if is_post()
      include ./post-info.pug
    else if is_home() 
      #site-info
        h1#site-title=site_title
        if theme.subtitle.enable
          - var loadSubJs = true
          #site-subtitle
            span#subtitle
        if(theme.social)
          #site_social_icons
            !=fragment_cache('social', function(){return partial('includes/header/social')})
      #scroll-down
        i.fas.fa-angle-down.scroll-down-effects
    else
      #page-site-info
        h1#site-title=site_title

五,替换themes/butterfly/layout/includes/header/nav.pug

- const { darkmode } = theme
nav#nav
  span#blog_name
    .back-home-button(tabindex='-1')
      i.back-home-button-icon.fas.fa-grip-vertical
      .back-menu-list-groups
        .back-menu-list-group
          .back-menu-list-title 网页
          .back-menu-list
            a.back-menu-item(href='https://www.ffing.cn', title='前往首页', target='_blank', one-link-mark='yes')
              img.back-menu-item-icon(src='https://src.ffing.cn/hexo/img/fn_white_logo.png')
              span.back-menu-item-text 首页
            a.back-menu-item(href='http://pan.ffing.cn/', rel='external nofollow', title='前往alist网盘', target='_blank', one-link-mark='yes')
              img.back-menu-item-icon(src='https://src.ffing.cn/hexo/img/alist_logo.png')
              span.back-menu-item-text alist网盘
            a.back-menu-item(href='https://kms.ffing.cn/', rel='external nofollow', title='前往kms服务', target='_blank', one-link-mark='yes')
              img.back-menu-item-icon(src='https://src.ffing.cn/hexo/img/kms_logo.png')
              span.back-menu-item-text kms服务
        .back-menu-list-group
          .back-menu-list-title 在线工具
          .back-menu-list
            a.back-menu-item(href='https://www.iconfont.cn', rel='noopener nofollow', title='前往阿里icon', target='_blank', one-link-mark='yes')
              img.back-menu-item-icon(src='https://src.ffing.cn/hexo/img/ali-icon_logo.png')
              span.back-menu-item-text 阿里icon

    a#site-name(href=url_for('/'))
      .title #[=config.title]
      i.fa-solid.fa-house

  div.mask-name-container
    center(id="name-container")
      a(id="page-name" href="javascript:btf.scrollToDest(0, 500)") PAGE_NAME
  #weather
  <div id="tp-weather-widget"></div> 
  #menus
    if (theme.algolia_search.enable || theme.local_search.enable)
      div.nav-button#search-button
        a.site-page.social-icon.search
          i.fas.fa-search.fa-fw
          span=' '+_p('search.title')

    if darkmode.enable && darkmode.button
      div.nav-button#darkmode_navswitch
        a.darkmode_switchbutton(type="button" title=_p('rightside.night_mode_title') onclick="switchDarkMode()")
          i.fas.fa-adjust
    div.nav-button#nav-totop
      a.totopbtn
        i.fas.fa-arrow-up
        span#percent(onclick="btf.scrollToDest(0,500)") 0


    !=partial('includes/header/menu_item', {}, {cache: true})

    #toggle-menu
      a.site-page
        i.fas.fa-bars.fa-fw

六,引入css和js,三连

_config.butterfly.xml引入css、js

inject:
  head:
   #安知鱼导航栏
   - <link rel="stylesheet" href="https://src.ffing.cn/hexo/css/nav_menu.css">
    #安知鱼导航
    - <script defer src="https://src.ffing.cn/hexo/js/switchDarkMode.js"></script>      
    - <script defer src="/js/nav_menu.js"></script> 

三连

hexo cl && hexo g &&hexo s
good good study, day day up!

发表评论

textsms
account_circle
email

Hexo搭建butterfly博客---(五)导航栏美化
预览地址:https://blog.ffing.fun 参考博主:安知鱼 导航栏效果 为方便操作,直接替换即可 1.导航栏菜单排版2.创建导航栏nav_menu.css和nav_menu.js文件3.替换themes/but…
扫描二维码继续阅读
2023-03-11