hexo-theme-fluid icon indicating copy to clipboard operation
hexo-theme-fluid copied to clipboard

给代码块增加折叠功能

Open KiyanYang opened this issue 2 years ago • 6 comments

如是功能需求,请确定符合以下情况

  • [ ] 难以通过自定义实现
  • [ ] 难以通过第三方插件实现

请描述该需求尝试解决的问题

代码块没有折叠功能,虽然可以通过自定义实现,但是需要每次打开页面都动态替换页面内容,非常不方便也不高效,而且折叠功能也较为重要。

请描述您认为可行的解决方案

添加折叠功能。

我的替代方案

效果见我的博客,比如文章 https://kiyanyang.github.io/posts/f92be1eb 。 我的效果和 https://github.com/fluid-dev/hexo-theme-fluid/issues/530 进行了结合。

注意:该方法对按照如下主题配置有效,对其他类型的主题配置可能无效

code:
  highlight:
    enable: true
    line_number: true
    lib: "highlightjs"
    highlightjs:
      style: "Github Gist"
      bg_color: false

总思路:借助 bootstrap 的 collapse 功能来折叠代码块

对于我的主题配置来说就是:获取所有 figure.highlight 元素,这个元素内有一个 table(带行号的代码块),因此使用 bootstrap 的 collapse 功能来折叠 table即可。代码如下(JavaScript):

// KiyanYang

// 获取唯一 ID
function getUuiD() {
  return Math.random().toString(36).substring(2, 8) + Date.now().toString(36);
}

function addLanguage() {
  // 获取所有 figure.highlight 元素
  var hs = $("figure.highlight");
  for (var i = 0; i < hs.length; i++) {
    // 获取代码语言
    var lang = hs[i].firstChild.firstChild.firstChild.lastChild.firstChild.firstChild.classList[1];
    // 折叠块的 id
    var id = `kiyan-collapse-${getUuiD()}`;
    // 前面折叠按钮,这里我使用的是 FAS 图标,可以在 custom_css 添加 https://use.fontawesome.com/releases/v5.15.4/css/all.css 来引入
    var btn = `<i class="fas fa-angle-down" type="button" data-toggle="collapse" data-target="#${id}"></i>`;
    // 代码语言
    var span = `<span>${lang}</span>`;
    // 折叠块包裹原来的内容
    var div = `<div class="collapse show" id="${id}">${hs[i].innerHTML}</div>`;
    hs[i].innerHTML = btn + span + div;
  }
}

$(document).ready(addLanguage);

关于我代码块部分的样式也一并展示如下(Stylus):

// ==================================================
// 代码块折叠
// ==================================================
figure.highlight {
    background: rgb(230, 235, 241);
    border-radius: 0.5rem;

    table {
        border-radius: 0 0 0.5rem 0.5rem;
    }

    // 折叠图标动画
    .fas.fa-angle-down {
        transform: none;
        transition: transform 0.2s ease-in-out;

        &.collapsed {
            transform: rotate(-90deg);
        }
    }

    // 折叠图标
    > i {
        color: #777777;
        margin-left: 10px;
        line-height: 2rem;
    }

    // 代码语言
    > span {
        color: #777777;
        margin-left: 10px;
        font-weight: bold;
    }

    // 固定代码块的第一列也即固定代码行数
    td:first-child {
        position: sticky;
        left: 0;
        z-index: 1;
    }
}
// ==================================================
// 代码块的复制按钮
// ==================================================
.copy-btn {
    // 注意:top 是和折叠功能共生的
    top: 5px;
    font-size: 1rem;

    > i {
        font-size: 0.875rem;
        font-weight: bold;
    }
}

.copy-btn-dark {
    color: darkslategrey;
}

引入外部的 js、css 请参看官方文档

KiyanYang avatar Dec 19 '21 14:12 KiyanYang

使用过滤器在文章完成渲染后增加折叠功能的代码。

scripts/ 目录下新建文件 <fileName>.js,文件名自定,内容如下:

"use strict";

// 获取唯一 ID
function getUuid() {
  return Math.random().toString(36).substring(2, 8) + Date.now().toString(36);
}

hexo.extend.filter.register(
  "after_post_render",
  (data) => {
    const { line_number, lib } = hexo.theme.config.code.highlight;

    let reg;
    if (lib === "highlightjs") {
      if (line_number) {
        reg = /(<figure class="highlight.+?>)(.+?hljs (.*?)".+?)(<\/figure>)/gims;
      } else {
        reg = /(<div class="code-wrapper.+?>)(.+?hljs (.*?)".+?)(<\/div>)/gims;
      }
    } else if (lib === "prismjs") {
      reg = /(<div class="code-wrapper.+?>)(.+?data-language="(.*?)".+?)(<\/div>)/gims;
    }

    data.content = data.content.replace(reg, (match, begin, inner, lang, end, offset, string) => {
      const collapseId = `collapse-${getUuid()}`;
      //                             ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 设置折叠按钮图标,此处使用 GitHub 图标
      const collapseBtn = `<i class="iconfont icon-github-fill" type="button" data-toggle="collapse" data-target="#${collapseId}"></i>`;
      const collapseDiv = `<div class="collapse show" id="${collapseId}">${inner}</div>`;
      const langSpan = `<span>${lang}</span>`;
      return begin + collapseBtn + langSpan + collapseDiv + end;
    });
    return data;
  },
  10000 // 应该在完成其他渲染后执行,因此将优先级设大一点
);

更多细节请查看我的博客文章「使用 Hexo 过滤器实现 Fluid 主题的代码折叠」。

KiyanYang avatar May 22 '22 14:05 KiyanYang

大佬你好,引入下拉图标(fas fa-angle-down)能仔细讲下吗,我博客现在只能使用fluid自带的图标,前端小白,导入cumtom_css也试过了,无果,/source/css/code-folding.styl文件中图标的旋转动画也没有生效,我是拷贝的你的代码,不知道哪出了问题。

Meloor avatar Oct 26 '22 20:10 Meloor

图标来自 https://fontawesome.com/ ,可以参考以下代码

custom_css:
  - https://lib.baomitu.com/font-awesome/6.1.2/css/all.min.css # Font Awesome
  - /css/code-folding # 你的图标的旋转动画, 注意这里使用无后缀或者使用 css 后缀

KiyanYang avatar Oct 27 '22 00:10 KiyanYang

感谢大佬,问题解决了! 除了按照你说的修改custom_css:外,还需要修改code-folding.styl中的选择器, 可能hexo版本不一致,导致生成的静态文件.html结构不一样, 我的是hexo5.4.2 。 <blog>/public/<post path>/<post name>.html:

            <div class="markdown-body">
                     <!-- ... -->
                  <p>OuterClass.java:外部类嵌套内部类</p>
                  <figure class="highlight java"><i class="fas fa-angle-down" type="button" data-toggle="collapse"
                      data-target="#collapse-er2vp8l9qvgz18"></i><span>java</span>
                    <div class="collapse" id="collapse-er2vp8l9qvgz18">
                      <table>
                        <tr>
                          <td class="gutter">
                            <pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre>
                          </td>
                          <td class="code">
                            <pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OuterClass</span> &#123;    <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">InnerClass</span>&#123;&#125;    <br>    <span class="hljs-keyword">class</span> <span class="hljs-title class_">InnerClass2</span>&#123;&#125;    <br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">InnerClass3</span>&#123;&#125;<br>&#125;<br></code></pre>
                          </td>
                        </tr>
                      </table>
                    </div>
                  </figure>
           
                 <!-- ... -->
 </div>

<blog>/source/css/code-folding.styl:

// table里放的是代码
.markdown-body > figure > div > table{
  border-radius: 0 0 0.5rem 0.5rem;
}

.markdown-body > figure{
  background-color: #e6ebf1;
  border-radius: 0.625rem;

  // 折叠图标
  > i {
    color: #777777;
    margin-left: 10px;
    line-height: 2rem;
    transform: none;
    transition: color 0.2s ease-in-out, transform 0.2s ease-in-out;

    &.collapsed {
      transform: rotate(-90deg);
    }
  }

  // 代码语言
  > span {
    color: #777777;
    margin-left: 10px;
    font-weight: bold;
  }
}

[data-user-color-scheme='dark'] {
  .markdown-body > figure{
    background-color: #696969;
    transition: background-color 0.2s ease-in-out;

    > i {
      color: #c4c6c9;
    }

    > span {
      color: #c4c6c9;
      transition: color 0.2s ease-in-out;
    }
  }
}


效果图: image

Meloor avatar Oct 27 '22 09:10 Meloor

请问您方便贴一下这两个文件:code-folding.styl 和 过滤器的js文件 的目录供我参考一下吗?

zlwhub avatar Nov 13 '22 10:11 zlwhub

blog
|----scripts
|    |----filter-code-collapse.js
|
|----source
     |----css
          |----code-collapse.styl

KiyanYang avatar Nov 13 '22 10:11 KiyanYang