为hexo实现两列展示功能

目的

  一个是方便观察,另一个就是因为如果图片是长图片的话,还会造成空间浪费。

实现方式

  条条大路通罗马。只要肯探索,方式多种多样。

未选择方式

  1. 直接写html:麻烦,每次都要写,要写的html代码还比较长,还不一定记得住
  2. 写脚本
    • Typora脚本:没怎么研究,而且识别和渲染比较麻烦的感觉
    • 其他脚本:识别和匹配比较费脑子

hexo-next插件方式

  首先,经过多次配置和翻文件,我对hexo和next文的了解已经比较熟悉了。漂亮话:没有人比我更懂它们👐。

   我们知道在hexo和Tyopra中可以使用借助美人鱼插件用代码块来画图,所以我也设想过这种方式,但是,我翻了翻文件,没有选择这种方式。主要是因为我有现成的例子。很久以前,我发现文章内容过多时,折叠起来就好了,于是就在网络上借鉴起来了。这种方式就是标签插件,hexo和next都有相应的标签。

  最开始,我设想的是实现用三个标签分成两部分的方式,但通过阅读好几个例子,改为将内容用标签包住,内容中间用分割符分开。

要借鉴(致敬)前要现了解对方

  首先我们观察折叠功能实现的代码。

1
2
3
4
5
6
7
8
9
10
11
/* global hexo */
// Usage: {% fold ???? %} Something {% endfold %}
function fold (args, content) {
var text = args[0];
if(!text) text = "点击显/隐";
return '<div><div class="fold_hider"><div class="close hider_title">'
+ text + '</div></div><div class="fold">\n'
+ hexo.render.renderSync({text: content, engine: 'markdown'})
+ '\n</div></div>';
}
hexo.extend.tag.register('fold', fold, {ends: true});

  首先,可以发现,args就是参数数组,content就是标签之间包括的内容。hexo.render.renderSync({text: content, engine: 'markdown'}从名称可看出,这是一个异步渲染器,传入了标签之间的内容,使用markdown引擎进行渲染。hexo.extend.tag.register('fold', fold, {ends: true});从名称可以看出,这是进行标签注册,注册的标签是fold,使用的是fold函数,同时说明这个标签有结束标记。

  我们再看一个。这个标签我没用过,是我在文件里发现的比较简单的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var rUrl = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[.\!\/\\w]*))?)/;

/**
* Fancybox tag
*
* Syntax:
* {% fancybox /path/to/image [/path/to/thumbnail] [title] %}
*/

hexo.extend.tag.register('fancybox', function(args){
var original = args.shift(),
thumbnail = '';

if (args.length && rUrl.test(args[0])){
thumbnail = args.shift();
}

var title = args.join(' ');

return '<a class="fancybox" href="' + original + '" title="' + title + '">' +
'<img src="' + (thumbnail || original) + '" alt="' + title + '">'
'</a>' +
(title ? '<span class="caption">' + title + '</span>' : '');
});

  和前面的折叠相比较,可以发现这个注册了fancybox标签,并传入匿名函数处理事情。同时,可以看到这个标签没有结束标签。我们还可以学到这个:(thumbnail || original),我认为它运用或运算的一个规律:一真则真,来达到三目运算的感觉。

  上面的这两个代码显示的方式是我最开始使用的方式,中间因为命名出错的原因没有成功,所以我尝试使用next的方式。我们先学习一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* tabs.js | https://theme-next.js.org/docs/tag-plugins/tabs
*/

'use strict';

module.exports = ctx => function(args, content = '') {
const tabBlock = /<!--\s*tab (.*?)\s*-->\n([\w\W\s\S]*?)<!--\s*endtab\s*-->/g;

args = args.join(' ').split(',');
const tabName = args[0];
const tabActive = Number(args[1]) || 0;

let tabId = 0;
let tabNav = '';
let tabContent = '';

if (!tabName) ctx.log.warn('Tabs block must have unique name!');
const matches = content.matchAll(tabBlock);

for (const match of matches) {
let [caption = '', icon = ''] = match[1].split('@');
let postContent = match[2];

postContent = ctx.render.renderSync({ text: postContent, engine: 'markdown' }).trim();

const abbr = tabName + ' ' + ++tabId;
const href = abbr.toLowerCase().split(' ').join('-');

icon = icon.trim();
if (icon.length > 0) {
if (!icon.startsWith('fa')) icon = 'fa fa-' + icon;
icon = `<i class="${icon}"></i>`;
}

caption = icon + caption.trim();

const isActive = (tabActive > 0 && tabActive === tabId) || (tabActive === 0 && tabId === 1) ? ' active' : '';
tabNav += `<li class="tab${isActive}"><a href="#${href}">${caption || abbr}</a></li>`;
tabContent += `<div class="tab-pane${isActive}" id="${href}">${postContent}</div>`;
}

tabNav = `<ul class="nav-tabs">${tabNav}</ul>`;
tabContent = `<div class="tab-content">${tabContent}</div>`;

return `<div class="tabs" id="${tabName.toLowerCase().split(' ').join('-')}">${tabNav + tabContent}</div>`;
};

  首先'use strict';表明使用严格模式。module.exports表明这是向外暴露接口。ctx => function()这个应该是箭头函数,应该是在ctx里添加一个函数,ctx应该和hexo是同一个全局变量吧,毕竟有这个ctx.render.renderSync()tabBlock这个变量可以看出也是在将标签包裹的内容分块取出。let [caption = '', icon = ''] = match[1].split('@');从这个代码里面我们可以学到一种变量和赋值的方式。

  可以看到这个代码里面没有注册,那么在那里注册呢?可以发现在当前文件夹下有一个index.js,在该文件里模仿前面的进行编写。不过里面一个写法:const centerQuote = require('./center-quote')(hexo);,我根据查到的资料,我认为这是执行require返回一个匿名函数,然后这个匿名函数传入hexo进行执行。

实现第一步:设计

  使用标签{% left_right %} 进行标记,用<!-- s -->进行分割左右,使用第一个参数作为左边div的宽度。同时设计别名{% lr %} 。原本还想设计更多参数用来调整是否上下居中,但是我使用的是弹性盒子布局,不方便弄,我不想🙅。

  html代码设计如下:

1
2
3
4
5
6
7
8
<div name = "right_left" class = "right_left">
<div name = "left" class = "left" style = "max-width: 50%;">
<p></p>
</div>
<div name = "right" class = "right">
<img src=""><p></p>
</div>
</div>

实现第二步:编写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function left_right (args, content) {
const split_symbol = /<!--\s*s\s*-->/;
const result = content.split(split_symbol);
let left_content = result[0];
let right_content = result[1];
//max-width: 50%;
let max_width = "";
if(args[0]) max_width =args[0]+"%";
return '<div class = "left_right">'
+ '<div class = "left" style = "max-width: '+(max_width||"50%")+';">'
+ hexo.render.renderSync({text: result[0], engine: 'markdown'})
+ '</div>'
+ '<div class = "right">'
+ hexo.render.renderSync({text: result[1], engine: 'markdown'})
+ '</div>'
+ '</div>';
}
hexo.extend.tag.register('left_right', left_right, {ends: true});
hexo.extend.tag.register('lr', left_right, {ends: true});

  编写好后,我们将它放到主题文件下的\scripts\filters。至于为什么放到这里,不是tag或者其他,是因为我之前的文章折叠就是放到这里。应该是hexo和next会默认加载。

实现第三步:编写css

  css可以写的地方有许多,比如网站下的静态资源文件夹,然后在文章里引入;再比如在默认加载的css文件里编写;再比如在html代码里写。我选的方式当然要简单啦,我不喜欢太麻烦。

  首先根据主题文件的样子新建一个自己的css文件。我已经在source\css\_custom下新建了custom.styl。哈哈,之前看到好的配置,运用拿来主义变成我的了。在custom.styl里编写如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
div.left_right{
display: flex !important;//我知道用这种方式提高优先级不好,尤其是泛滥后。
justify-content: start;//最后选择。
overflow: auto;//避免超出父元素
}
div.left{
padding-right: 10px;//左右之间不要挨得太近
//max-width: 55%;//在模板里设置
word-wrap: break-word;//聊胜于无吧?自动换行
}
div.right{
//max-width: 40%;
//min-width: auto;
word-wrap: break-word;
}

  编写好后,怎么加载呢?简单,观察好几个文件就明白了。在css文件夹下,有一个main.styl,在里面追加:@import "_custom/custom.styl";@import "_custom/hbe.styl";。到此时,我们想要的功能就实现了。

我想说

  从这次功能的实现中可以发现,我对js学习得不够多,好多不是很了解。看来不能仅靠已经学习过的语音来感觉js了,要再学习深一点了。对于css,我看就算了,太多了,就靠互联网和浏览器提示了。

参考

更新

  • 2024年1月13日 仅设置左边的最大值不够好,有时候它不争气。所以根据它本身参数的特性,设计最多4个有效参数作为左右的最大、最小宽度。当参数不足时,用默认值或无效值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    function left_right (args, content) {
    const split_symbol = /<!--\s*s\s*-->/;
    const result = content.split(split_symbol);
    let left_content = result[0];
    let right_content = result[1];
    //max-width: 50%;
    let lmax_width = "";
    if(args[0]) lmax_width =args[0]+"%";
    let lmin_width = "";
    if(args[1]) lmin_width =args[1]+"%";
    let rmax_width = "";
    if(args[2]) rmax_width =args[1]+"%";
    let rmin_width = "";
    if(args[3]) rmin_width =args[1]+"%";

    return '<div class = "left_right">'
    + '<div class = "left" style = "max-width: '+(lmax_width||"50%")+';min-width: '+(lmin_width||"auto")+';">'
    + hexo.render.renderSync({text: result[0], engine: 'markdown'})
    + '</div>'
    + '<div class = "right" style = "max-width: '+(rmax_width||"auto")+';min-width: '+(rmin_width||"auto")+';">'
    + hexo.render.renderSync({text: result[1], engine: 'markdown'})
    + '</div>'
    + '</div>';
    }