博客更换ICARUS主题

前言

最近无意间留意到一个比较符合我口味的主题,也许是因为Next主题被我看腻了,于是就换了icarus然后接下来当然是各种折腾,总算是告一段落可以安心的做别的事情了。

主题介绍

主题地址:https://github.com/ppoffice/hexo-theme-icarus

这个主题可以完全替代Next主题,比较让我喜欢的是简洁大气的界面风格,另外还有细致的侧边栏以及订制功能,总结一下特点如下:

  1. 整体的界面美观大方。
  2. Icarus包含常用的搜索,评论,分享和其他插件。
  3. 可设置文章头图片。
  4. 超过70种highlight.js样式可选。
  5. 可以自定义侧边栏。
  6. 字体风格漂亮。
  7. 可在文章内自定义菜单。
  8. 响应式布局。

主题配置

导航栏

和Next不同的是不要配置font图标,只需要配置路径即可。

navbar:
    # Navigation bar menu links
    menu:
        首页: /
        归档: /archives/
        资料: /keep/
        友链: /links/
        统计: /laboratory
        相册: /photos/
        关于: /about/
        索引: /sitemap.xml
    links:
        Download on GitHub:
            icon: fab fa-github
            url: 'https://github.com/lxqxsyu'

页脚导航

我们可以在页脚的右下角设置链接导航。

footer:
    # Links to be shown on the right of the footer section
    links:
        Creative Commons:
            icon: fab fa-creative-commons
            url: 'https://creativecommons.org/'
        Attribution 4.0 International:
            icon: fab fa-creative-commons-by
            url: 'https://creativecommons.org/licenses/by/4.0/'
        Download on GitHub:
            icon: fab fa-github
            url: 'https://github.com/lxqxsyu'

图库

还记得之前在Next中要搞一个相册有多麻烦,这个主题已经默认支持瀑布流的相册布局,你可以添加到文章的任意位置。

首先在_config.yml中开启画廊功能:

plugins:
    gallery: true

添加画廊图片:

<div class="justified-gallery">
![Elephant](/hexo-theme-icarus/gallery/animals/elephant.jpeg)
![Dog](/hexo-theme-icarus/gallery/animals/dog.jpeg)
![Birds](/hexo-theme-icarus/gallery/animals/birds.jpeg)
![Cat](/hexo-theme-icarus/gallery/animals/cat.jpeg)
![Fox](/hexo-theme-icarus/gallery/animals/fox.jpeg)
![Horse](/hexo-theme-icarus/gallery/animals/horse.jpeg)
![Leopard](/hexo-theme-icarus/gallery/animals/leopard.jpeg)
</div>

设置侧边栏

侧边栏可以在widgets中设置,按照先后顺序显示,每个侧栏都可以设置左侧还是右侧显示,当然也可以隐藏。

修改主题

主题使用ejs和sass框架编写,结构清晰,甚至你都不需要会css都可以修改样式。

双栏模式

首页有左右侧边栏,其他页面只有左边侧边栏,这样可以在阅读文章的时候更舒服。

diff --git a/includes/helpers/layout.js b/includes/helpers/layout.js
index 7fd6c07..6bfd0b3 100644
--- a/includes/helpers/layout.js
+++ b/includes/helpers/layout.js
@@ -32,6 +32,9 @@ module.exports = function (hexo) {
 
     hexo.extend.helper.register('column_count', function () {
         let columns = 1;
+        if (this.page.__post === true || this.page.__page === true) {
+            return 2;
+        }
         const hasColumn = hexo.extend.helper.get('has_column').bind(this);
         columns += hasColumn('left') ? 1 : 0;
         columns += hasColumn('right') ? 1 : 0;
diff --git a/layout/common/widget.ejs b/layout/common/widget.ejs
index 9f58bd5..c6b267c 100644
--- a/layout/common/widget.ejs
+++ b/layout/common/widget.ejs
@@ -25,7 +25,7 @@
         <%- partial('widget/' + widget.type, { widget, post: page }) %>
     <% }) %>
     <% if (position === 'left') { %>
-        <div class="column-right-shadow is-hidden-widescreen <%= sticky_class('right') %>">
+        <div class="column-right-shadow <%= (page.__page !== true && page.__post !== true) ? 'is-hidden-widescreen' : '' %> <%= sticky_class('right') %>">
         <% get_widgets('right').forEach(widget => {%>
             <%- partial('widget/' + widget.type, { widget, post: page }) %>
         <% }) %>
diff --git a/layout/layout.ejs b/layout/layout.ejs
index cf7be87..b76c9e6 100644
--- a/layout/layout.ejs
+++ b/layout/layout.ejs
@@ -21,7 +21,9 @@
             <div class="columns">
                 <div class="column <%= main_column_class() %> has-order-2 column-main"><%- body %></div>
                 <%- partial('common/widget', { position: 'left' }) %>
+                <% if (page.__page !== true && page.__post !== true) { %>
                 <%- partial('common/widget', { position: 'right' }) %>
+                <% } %>
             </div>
         </div>
     </section>

文章区域放宽

这个文章内容区域是12格布局,可以修改source/css/style.styl文件中的@media screen样式如下:

@media screen and (min-width: screen-widescreen)
    .is-1-column .container
    .is-2-column .container
        max-width: screen-widescreen - 2 * gap
        width: screen-widescreen - 2 * gap
@media screen and (min-width: screen-fullhd)
    .is-2-column .container
        max-width: screen-fullhd - 2 * gap
        width: screen-fullhd - 2 * gap
    .is-1-column .container
        max-width: screen-desktop - 2 * gap
        width: screen-desktop - 2 * gap

再修改layout/layout.ejslayout/common/widget.ejs的文件内容如下:

    <% function main_column_class() {
        switch (column_count()) {
            case 1:
                return 'is-12';
            case 2:
                return 'is-8-tablet is-9-desktop is-9-widescreen';
            case 3:
                return 'is-8-tablet is-8-desktop is-6-widescreen'
        }
        return '';
    } %>
<% function side_column_class() {
    switch (column_count()) {
        case 2:
            return 'is-4-tablet is-3-desktop is-3-widescreen';
        case 3:
            return 'is-4-tablet is-4-desktop is-3-widescreen';
    }
    return '';
} %>

要注意的是对应column的数量和是12,如果是三列就是三列的和,如果是两列就是两列的和。

添加51LA统计

这个统计有一大特点就是可以拥有一个有关网站的整体数据当月当日数据的挂件,还可以分析SEO,浏览者的IP,爬虫分析等。

_config.ymlbusuanzi: false代码下发添加配置:

plugins:
    #51.la统计
    tj51la: true

打开layout/common/footer.ejs文件的<span id="busuanzi_container_site_uv">下方添加如下代码:

<% if (has_config('plugins.tj51la') ? get_config('plugins.tj51la') : false) { %>
                
<% } %>

layout/plugin目录新建文件tj51la.ejs注意将xxxxxx修改为你自己的id.

<% if (plugin !== false) { %>
    <% if (head) { %>
        <script type="text/javascript" src="https://js.users.51.la/xxxxxx.js"></script>
    <% } %>
<% } %>

如果需要你还可以在layout/common/footer.ejs中添加备案号等内容。

动态隐藏组件

上面提到过如何在page和post页面显示两栏,在首页显示三栏,我们可以判断当前的columns(几栏)来做不同的逻辑操作,有两种方法实现在两栏的时候隐藏部分侧边组件。

方法一:例如想取掉 tagclound widget,在 tagcloud.ejs 中添加判断是否是page或者post

+ <% if (page.__page !== true && page.__post !== true) { %>
<% if (site.tags.length) { %>
<div class="card widget">
    <div class="card-content">
        <h3 class="menu-label">
            <%= __('widget.tag_cloud') %>
        </h3>
        <%- tagcloud() %>
    </div>
</div>
<% } %>
+ <% } %>

方法二:在 layout/common/widget.ejs 中判断如果widget是不是tag组件。

<div class="column <%= side_column_class() %> <%= visibility_class() %> <%= order_class() %> column-<%= position %> <%= sticky_class(position) %>">
    <% get_widgets(position, column_count()).forEach(widget => {%>
        <%- partial('widget/' + widget.type, { widget, post: page }) %>
    <% }) %>
    <% if (position === 'left') { %>
        <div class="column-right-shadow <%= (page.__page !== true && page.__post !== true) ? 'is-hidden-widescreen' : '' %> <%= sticky_class('right') %>">
-       <% get_widgets('right', column_count()).forEach(widget => {%>
+		    <% get_widgets(right, column_count()).forEach(widget => { if(widget.type !== 'tag'){%>
                <%- partial('widget/' + widget.type, { widget, post: page }) %>
-        <% }) %>
+        <% }}) %>
        </div>
    <% } %>
</div>

修改组件的方向和位置

比如我想把最新文章widget在两栏的时候放到左边的靠近上方的位置。

先在 includes/helpers/layout.js 中添加函数。

hexo.extend.helper.register('get_widgets_by_columns', function(position, column) {
  const hasWidgets = hexo.extend.helper.get('has_config').bind(this)('widgets');
  if (!hasWidgets) {
    return [];
  }
  const widgets = hexo.extend.helper.get('get_config').bind(this)('widgets');
  for(var i=0; i<widgets.length; ++i){
    if(widgets[i].type === 'recent_posts'){
      widgets[i].position = (column === 2 ? 'left' : 'right');
    }
  }
  return widgets.filter(widget => widget.hasOwnProperty('position') && widget.position === position); 
});

然后再修改 layout/common/widget.ejs 中的get_widgetsget_widgets_by_columns 函数。

<div class="column <%= side_column_class() %> <%= visibility_class() %> <%= order_class() %> column-<%= position %> <%= sticky_class(position) %>">
-   <% get_widgets(position, column_count()).forEach(widget => {%>
+   <% get_widgets_by_columns(position, column_count()).forEach(widget => {%>
        <%- partial('widget/' + widget.type, { widget, post: page }) %>
    <% }) %>
    <% if (position === 'left') { %>
        <div class="column-right-shadow <%= (page.__page !== true && page.__post !== true) ? 'is-hidden-widescreen' : '' %> <%= sticky_class('right') %>">
-        <% get_widgets('right', column_count()).forEach(widget => { if(widget.type !== 'tag'){%>
+        <% get_widgets_by_columns('right', column_count()).forEach(widget => { if(widget.type !== 'tag'){%>
                <%- partial('widget/' + widget.type, { widget, post: page }) %>
        <% }}) %>
        </div>
    <% } %>
</div>

随机图片路径

includes/helpers/page.js 中添加两个函数:

hexo.extend.helper.register('has_random_recent_thumbnail', function (post) {
    const getConfig = hexo.extend.helper.get('get_config').bind(this);
    return getConfig('article.random_recent_thubnail', true);
});

hexo.extend.helper.register('get_random_recent_thumbnail', function (post) {
  const autothubnail = hexo.extend.helper.get('has_random_recent_thumbnail').bind(this)(post);
  var url = "images/thumbnail.svg";
  if(autothubnail){
    url = "/photos/life/image" + Math.floor(Math.random()*26) + ".jpg";
  }
  return this.url_for(url);
});

注意上面的 /photos/post_w 文件夹中的图片名字的规则,必须是数字递增,这里的23是该文件夹中的图片数量,图片序列下标从0开始。

_config.yml 中添加 article setting

article:
		random_recent_thubnail: true

修改 layout/widget/recent_posts.ejs 文件:

- <% if(!has_config('thumbnail') || get_config('thumbnail') !== false) { %>
+ <% if (!has_config('article.random_recent_thubnail') || get_config('article.random_recent_thubnail') !== false) { %>
<a href="<%- url_for((post.link?post.link:post.path)) %>" class="media-left">
    <p class="image is-64x64">
- 			 <img class="thumbnail" src="<%= get_thumbnail(post) %>" alt="<%= post.title %>">
+        <img class="thumbnail" src="<%= get_random_recent_thumbnail(post) %>" alt="<%= post.title %>">
    </p>
</a>
<% } %>

修改Gallery样式

<div class="justified-gallery">
![Elephant](/hexo-theme-icarus/gallery/animals/elephant.jpeg)
......
    
</div>

在icarus中有两个gallery插件,一个是 light Gallery 另一个是 justified Gallery

前者是一个轻量级的模块化、响应式的灯箱画廊,它允许您创建美丽的图像和视频画廊。借助缩略图插件的帮助,还允许您创建缩略图画廊。它支持触摸屏设备上滑动导航以及桌面设备的鼠标拖动,还允许用户浏览缩略图和原图之间通过滑动手指或鼠标拖动。

document.addEventListener('DOMContentLoaded', function () {
    if (typeof ($.fn.lightGallery) === 'function') {
-       $('.article').lightGallery({ selector: '.gallery-item'});
+       $('.article').lightGallery({ selector: '.gallery-item', loop: true, escKey: true, keyPress: true});
    }
    if (typeof ($.fn.justifiedGallery) === 'function') {
        $('.justified-gallery').justifiedGallery({rowHeight:160, margins:4});
    }
});

例如上面我添加了几项配置,灯箱画廊有很多配置的,一些核心配置如下:

参数类型默认值描述
modestring‘lg-slide’图片过渡的动画类型。可用的动画有:'lg-slide''lg-fade''lg-zoom-in''lg-zoom-in-big''lg-zoom-out''lg-zoom-out-big''lg-zoom-out-in''lg-zoom-in-out''lg-soft-zoom''lg-scale-up''lg-slide-circular''lg-slide-circular-vertical''lg-slide-vertical''lg-slide-vertical-growth''lg-slide-skew-only''lg-slide-skew-only-rev''lg-slide-skew-only-y''lg-slide-skew-only-y-rev''lg-slide-skew''lg-slide-skew-rev''lg-slide-skew-cross''lg-slide-skew-cross-rev''lg-slide-skew-ver''lg-slide-skew-ver-rev''lg-slide-skew-ver-cross''lg-slide-skew-ver-cross-rev''lg-lollipop''lg-lollipop-rev''lg-rotate''lg-rotate-rev''lg-tube'
cssEasingstring‘ease’easing过渡动画类型。
speednumber600过渡动画的持续时间。单位毫秒。
heightstring‘100%’图片画廊的高度。
widthstring‘100%’图片画廊的宽度。
addClassstring为画廊添加自定义的class。
startClassstring‘lg-start-zoom’画廊的开始动画类型。
backdropDurationnumber150Lightgallery backdrop transtion duration
hideBarsDelaynumber6000隐藏图片画廊控制按钮的延迟时间,单位毫秒。
useLeftbooleanfalse强制lightgallery使用left 属性来代替transform。
closablebooleantrue允许用户点击暗处关闭图片画廊。
loopbooleantrue是否循环播放。
escKeybooleantrue是否允许通过”Esc”键来关闭图片画廊。
keyPressbooleantrue是否允许键盘导航。
controlsbooleantrue是否显示前后导航按钮。
slideEndAnimatoinboleantrue是否允许slideEnd 动画。
hideControlOnEndbooleanfalse如果设置为false,第一幅和最后一幅图片不显示前后导航按钮。
getCaptionFromTitleOrAltbooleantrue从图片的alt或title标签获取图片描述信息。
appendSubHtmlTostring’.lg-sub-html’指定添加sub-html:'.lg-sub-html''.lg-item'
subHtmlSelectorRelativebooleanfalse如果使用”data-sub-html”选择器作为当前项目的源,设置为true。
preloadnumber1预加载slider的数量。
showAfterLoadbooleantrue是否在完全加载后显示内容。
selectorstring自定义选择器来替代子元素。
selectWithinstringBy default selectror element is taken from only inside the gallery element. Instead of that you can tell lightgallery to select element within a specific
nextHtmlstringNext按钮的html标记。
prevHtmlstringPrev按钮的html标记。
indexnumber0设置被立刻加载的图片/视频的索引。
iframeMaxWidthstring‘100%’设置iframe的最大宽度。
downloadbooleantrue是否使用下载按钮。
counterbooleantrue是否显示图片的总数和当前图片的索引。
appendCounterTostring’.lg-toolbar’指定被添加的计数器。
swipeThresholdnumber50触摸滑动的阈值。
enableDragbooleantrue在桌面设备是否允许鼠标拖动。
enableTouchbooleantrue是否允许移动触摸。
dynamicbooleanfalse通过编程的方式动态调用插件。
dynamicElarray[]代表画廊元素的数组。

我博客的相册使用的是后者,后者是我喜欢的瀑布流样式,但是默认的这个样式高度和间隙我不是很喜欢,所以下面修改一下文件 source/js/gallery.js 代码如下:

document.addEventListener('DOMContentLoaded', function () {
    if (typeof ($.fn.lightGallery) === 'function') {
        $('.article').lightGallery({ selector: '.gallery-item' });
    }
    if (typeof ($.fn.justifiedGallery) === 'function') {
-       $('.justified-gallery').justifiedGallery();
+       $('.justified-gallery').justifiedGallery({rowHeight:160, margins:4});
    }
});

上面修改了瀑布流布局的每行高度为160px,间隔为4px。

事实上justifiedGallery有很多可设置选项,具体可参考官方文档:http://miromannino.github.io/Justified-Gallery/getting-started/

文章图片居中

主题文章图片居中问题的解决办法很简单,只需要修改 source/js/main.js 的文件内容如下:

$('.article img:not(".not-gallery-item")').each(function () {
    // wrap images with link and add caption if possible
    if ($(this).parent('a').length === 0) {
-       $(this).wrap('<a class="gallery-item" href="' + $(this).attr('src') + '"></a>');        
+       $(this).wrap('<a class="gallery-item" style="display:block;text-align:center;" href="' + $(this).attr('src') + '"></a>');
        if (this.alt) {
            $(this).after('<div class="has-text-centered is-size-6 has-text-grey caption">' + this.alt + '</div>');
        }
    }
});

还有一种比较优雅的方式就是修改 layout/css/style.styl 文件的内容如下:

.article
    .article-meta
        margin-bottom: 0.5rem !important
    .content
        font-size: 1.1rem
        blockquote.pullquote
            float: right
            max-width: 50%
            font-size: 1.15rem
            position: relative
+       a
+           img
+               margin: auto
+               display: block

我的博客首页图片轮播效果

我单独写了一篇文章,请参考:https://dp2px.com/2019/08/13/hexo-carousel/

最后

这个主题也存在几个问题,比如文章页的表格影响小尺寸上响应式布局会有超出内页的问题,有时候打开页面布局会出问题。

目前(2019年6月26日)作者已经修复上面的超出内页问题,详细请查看Github上面的issues#480,另一个问题也已经提了issues#481,你如果关心可以持续关注。

最后再记录一个你可能会遇到的 node 运行时内存不够的问题,如果你的文章特别多肯定会遇到,解决方法:issues#3350