28

动手魔改hexo-theme-icarus主题

 3 years ago
source link: https://gylidian.js.org/Qa1Ssx/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

动手魔改hexo-theme-icarus主题

Icarus 是一款 简单、优雅、现代化 的 Hexo 博客主题。

“最新文章”卡片 修改缩略图链接

如下图所示,recent_posts 中显示的文章列表的缩略图都是原图(文件较大),虽然每个图片大小只有几百KB而已,但是为了节省流量,我们还是选择将这里的缩略图压缩,压缩后往往只有十几KB。

STEP1 新增图片处理自定义版本

首先,我在 又拍云 新增了一个图片处理的自定义版本,功能配置中 把你认为能削减文件大小的选项都勾上。

至于间隔标识符,我延用默认的 !

这样,通过在图床图片URL结尾添加 !small,你就可以获取其压缩后的图片版本。

STEP2 魔改主题模板

进入hexo所在文件目录

打开 theme/icarus/_config.yml,放入以下配置

# 图床(啊哈呵嗨)
imghosting: 你的图床的BaseUrl

打开 themes/icarus/includes/helpers/page.js,修改辅助函数 get_thumbnail

hexo.extend.helper.register("get_thumbnail", function(post, version) {
const hasThumbnail = hexo.extend.helper.get("has_thumbnail").bind(this)(post);
const imgHosting = hexo.theme.config.imghosting;
const thumbnailUrl =
hasThumbnail && post.thumbnail.startsWith(imgHosting) && version
? `${post.thumbnail}${version}`
: post.thumbnail;
return this.url_for(hasThumbnail ? thumbnailUrl : "images/thumbnail.svg");
});

打开 theme/icarus/layout/widget/recent_posts.ejs,找到

get_thumbnail(post) 改成 get_thumbnail(post,'!small')

STEP3 查看效果

执行 hexo clean && hexo g && hexo s 即可。

为鼠标点击添加烟花效果

打开 themes/icarus/layout/layout.ejs,在 下方添加:

至于 CSS 样式,我选择放入 themes/icarus/source/css/style.styl

/* ---------------------------------
* 鼠标点击烟花效果
* --------------------------------- */
.fireworks
position: fixed
z-index: -1
pointer-events: none

新建文件 themes/icarus/source/js/firework.js,放入代码:

var canvasEl = document.querySelector(".fireworks");
if (canvasEl) {
var ctx = canvasEl.getContext("2d"),
numberOfParticules = 30,
pointerX = 0,
pointerY = 0,
tap = "mousedown",
colors = ["#FF1461", "#18FF92", "#5A87FF", "#FBF38C"],
setCanvasSize = debounce(function() {
(canvasEl.width = window.innerWidth),
(canvasEl.height = window.innerHeight),
(canvasEl.style.width = window.innerWidth + "px"),
(canvasEl.style.height = window.innerHeight + "px"),
canvasEl.getContext("2d").scale(1, 1);
}, 500),
render = anime({
duration: 1 / 0,
update: function() {
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
}
});
document.addEventListener(
tap,
function(e) {
"sidebar" !== e.target.id &&
"toggle-sidebar" !== e.target.id &&
"A" !== e.target.nodeName &&
"IMG" !== e.target.nodeName &&
(render.play(), updateCoords(e), animateParticules(pointerX, pointerY));
},
!1
),
setCanvasSize(),
window.addEventListener("resize", setCanvasSize, !1);
}
function updateCoords(e) {
(pointerX = (e.clientX || e.touches[0].clientX) - canvasEl.getBoundingClientRect().left),
(pointerY = e.clientY || e.touches[0].clientY - canvasEl.getBoundingClientRect().top);
}
function setParticuleDirection(e) {
var t = (anime.random(0, 360) * Math.PI) / 180,
a = anime.random(50, 180),
n = [-1, 1][anime.random(0, 1)] * a;
return {
x: e.x + n * Math.cos(t),
y: e.y + n * Math.sin(t)
};
}
function createParticule(e, t) {
var a = {};
return (
(a.x = e),
(a.y = t),
(a.color = colors[anime.random(0, colors.length - 1)]),
(a.radius = anime.random(16, 32)),
(a.endPos = setParticuleDirection(a)),
(a.draw = function() {
ctx.beginPath(),
ctx.arc(a.x, a.y, a.radius, 0, 2 * Math.PI, !0),
(ctx.fillStyle = a.color),
ctx.fill();
}),
a
);
}
function createCircle(e, t) {
var a = {};
return (
(a.x = e),
(a.y = t),
(a.color = "#F00"),
(a.radius = 0.1),
(a.alpha = 0.5),
(a.lineWidth = 6),
(a.draw = function() {
(ctx.globalAlpha = a.alpha),
ctx.beginPath(),
ctx.arc(a.x, a.y, a.radius, 0, 2 * Math.PI, !0),
(ctx.lineWidth = a.lineWidth),
(ctx.strokeStyle = a.color),
ctx.stroke(),
(ctx.globalAlpha = 1);
}),
a
);
}
function renderParticule(e) {
for (var t = 0; t < e.animatables.length; t++) e.animatables[t].target.draw();
}
function animateParticules(e, t) {
for (var a = createCircle(e, t), n = [], i = 0; i < numberOfParticules; i++)
n.push(createParticule(e, t));
anime
.timeline()
.add({
targets: n,
x: function(e) {
return e.endPos.x;
},
y: function(e) {
return e.endPos.y;
},
radius: 0.1,
duration: anime.random(1200, 1800),
easing: "easeOutExpo",
update: renderParticule
})
.add({
targets: a,
radius: anime.random(80, 160),
lineWidth: 0,
alpha: {
value: 0,
easing: "linear",
duration: anime.random(600, 800)
},
duration: anime.random(1200, 1800),
easing: "easeOutExpo",
update: renderParticule,
offset: 0
});
}

打开 themes/icarus/layout/common/scripts.ejs,追加代码:

<%- _js(cdn('animejs', '3.0.1', 'lib/anime.min.js')) %>
<%- _js('js/firework', true) %>

这就🆗啦,赶快 素质三连( hexo clean && hexo g && hexo s ) 查看效果吧!(´▽`ʃ♡ƪ)

改造”TOC”卡片

添加 sticky 效果

🔔 BUG 待修复:在中屏自适应情况下无法 sticky

打开 themes/icarus/source/css/style.styl,找到以下样式所在位置

@media screen and (min-width: screen-tablet)
.column-main,
.column-left,
.column-right,
.column-right-shadow
&.is-sticky
align-self: flex-start
position: -webkit-sticky
position: sticky
top: .75rem
.column-right-shadow
&.is-sticky
top: 1.5rem

将其修改为

@media screen and (min-width: screen-tablet)
.column-main,
.column-left,
.column-right,
.column-right-shadow,
#toc
&.is-sticky
align-self: flex-start
position: -webkit-sticky
position: sticky
top: .75rem
.column-right-shadow
&.is-sticky
top: 1.5rem
#toc
&.is-sticky
max-height: calc(100vh - 2rem)
overflow-y: auto

打开 theme/icarus/_config.yml,对 sidebar 追加以下配置

sidebar:
# left sidebar settings
left:
sticky: false
# right sidebar settings
right:
sticky: false
# 使TOC支持sticky效果(啊哈呵嗨)
toc:
sticky: true

打开 themes/icarus/layout/widget/toc.ejs,修改代码:

<% function toc_sticky_class() {
let canTocSticky, position;
get_widgets('left').forEach(widget => {
if(widget.type === 'toc') position = widget.position;
});
get_widgets('right').forEach(widget => {
if(widget.type === 'toc') position = widget.position;
});
if(position && get_config('sidebar.' + position + '.sticky', false)) canTocSticky = false;
else canTocSticky = get_config('sidebar.toc.sticky', false);
return canTocSticky ? 'is-sticky' : '';
} %>


<%= _p('widget.catalogue', Infinity) %>

<%- buildToc(_toc(post.content)) %>

TOC 标题随滚动定位

新建 themes/icarus/layout/plugin/tocbot.ejs

<% if (get_config('toc') === true && plugin !== false && (page.layout === 'page' || page.layout === 'post')) { %>
<% if (head) { %>

<% } else { %>
<%- _js('js/reading-progress.js') %>
<%- _js(cdn('tocbot', '4.5.0', 'dist/tocbot.min.js')) %>

<% } %>
<% } %>

打开 theme/icarus/_config.yml,新增配置

plugins:
# tocbot(啊哈呵嗨)
tocbot: true

修改 toc.ejs,找到代码

<%- buildToc(_toc(post.content)) %>
<% if (get_config('plugins.tocbot') !== false) {%>

<% } else { %>
<%- buildToc(_toc(post.content)) %>
<% } %>

添加 阅读进度滚动条

打开 themes/icarus/layout/widget/toc.ejs,找到代码:

在下方加入

添加样式,不用说都知道修改哪个文件了吧?

/* ---------------------------------
* 阅读进度滚动条
* --------------------------------- */
.reading-progress
width: 100%;
height: 4px;
opacity: .8;
background-color: rgba(37, 117, 252, .3);
overflow: hidden

.reading-progress-bar
background-image: linear-gradient(to right,#4cbf30 0,#0f9d58 100%);
height: 4px;
width: 0;

页面浏览器标题修改

打开 themes/icarus/includes/helpers/page.js,在辅助函数 page_title 中找到以下代码片段

const siteTitle = hexo.extend.helper.get("get_config").bind(this)("title", "", true);
return [title, siteTitle]
.filter(str => typeof str !== "undefined" && str.trim() !== "")
.join(" - ");

将其修改为

const getConfig = hexo.extend.helper.get("get_config").bind(this);
const siteTitle = getConfig("title", "", true),
siteSubTitle = getConfig("subtitle", "", true);
return [title, siteTitle, siteSubTitle]
.filter(str => typeof str !== "undefined" && str.trim() !== "")
.join(" - ");

执行 hexo clean && hexo g && hexo s 即可查看效果。

百度站长自动推送工具

打开 themes/icarus/layout/common/scripts.ejs,追加

<script>
(function () {
var bp = document.createElement('script');
var curProtocol = window.location.protocol.split(':')[0];
if (curProtocol === 'https') {
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
}
else {
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
script>

支持百度 MIP 和 AMP

打开 themes/icarus/layout/common/head.ejs,追加

<link rel="stylesheet" type="text/css" href="https://c.mipcdn.com/static/v2/mip.css">
<link rel="canonical" href="<%= url %>">

打开 themes/icarus/layout/common/scripts.ejs,追加

<script src="https://c.mipcdn.com/static/v2/mip.js">script>

打开 themes\icarus\layout\layout.ejs,给 html 标签 添加属性 mip

使 MathJax 和 Gallery 插件支持文章中开启

数学公式 和 图片展览 的使用频次实在是太少了,当你用不到的时候页面上就会加载 mathjax 和 gallery 的诸多额外 js,对网站速度还是有一定的影响的。

那么我们就修改一下配置,让 post 支持选择性启用这些插件吧!

支持 QQ 分享

hexo 是如何通过 分割文章从而获得 excerpt 的。

https://github.com/hexojs/hexo/blob/master/lib/plugins/filter/after_post_render/excerpt.js

打开 themes/icarus/layout/common/head.ejs,在合适的位置添加

<meta itemprop="name" content="<%= page_title() %>" />
<meta itemprop="description" content="<%= aha_description() %>" />
<meta itemprop="image" content="<%= page.thumbnail || get_config('url') + '/images/avatar.png' %>" />

打开 themes/icarus/includes/helpers/page.js,添加新辅助函数 aha_description

hexo.extend.helper.register("aha_description", function(page = null) {
page = page === null ? this.page : page;
const configDescription = hexo.extend.helper.get("get_config").bind(this)("description","");
let description = page.excerpt || page.description || configDescription;
if (description) {
description = stripHTML(description).substring(0, 200)
.trim()
.replace(/, "<")
.replace(/>/g, ">")
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/'/g, "'")
.replace(/\n/g, " ");
}
return description;
});

文章标题下划线动画

打开 themes/icarus/source/css/style.styl,追加

/* ---------------------------------
* 标题下划线动画
* --------------------------------- */
.title
a
background-image: linear-gradient(transparent 0%, transparent 65%,#a4c7ff 65%,#a4c7ff 90%, transparent 90%, transparent);
background-repeat: no-repeat;
background-size: 0 100%;
transition: background-size .3s cubic-bezier(0.4, 0, 1, 1);

&:hover
background-size: 100% 100%;

添加”待办事项”卡片

/* ---------------------------------
* TODO卡片
* --------------------------------- */
.clearfix
overflow: auto
zoom: 1
.todo-header
border-radius: 6px 6px 0 0
background: url("/images/aha-todo-bg.jpg") no-repeat
background-size: cover
padding: 30px
position: relative
time
color: white
display: block
letter-spacing: .125rem
.day
font-size: 4rem
float: left
margin-right: 10px
.dayofweek
display: block
margin-top: 13px
font-size: 1.4rem
font-weight: 700
.add-circle
position: absolute
background: #ff3c41
color: white
border-radius: 50%
width: 40px
height: 40px
line-height: 40px
text-align: center
right: 20px
bottom: -20px
box-shadow: 0 0 13px #ff3c41
cursor: pointer
.todo-form
margin: 15px 0
width: 100%
input
width: 100%
border: 0
border-bottom: 1px solid #e6e6e6
font-family: inherit
padding: 5px 0
outline: none
&:focus
border-bottom-color: #ff3c41
.todo-content
.todo-content-title
padding: 20px
h2
font-size: 1.5rem
font-weight: normal
color: #4c4646
.task-report
font-size: 0.9rem
color: #afafaf
.todo-list
list-style: none
.task-item
padding: 15px 20px
border-top: 1px solid #f1f1f1
position: relative
&.task-done
.text
color: #ccc
.label
display: inline-block
background: #ff3c41
width: 15px
height: 15px
border-radius: 50%
float: left
margin-right: 10px
&.normal
background: #37eaa0
.text
position: relative
top: -2px
.task-action
position: absolute
right: 20px
top: 12px
z-index: 99999
.more
cursor: pointer
span
display: inline-block
margin: 0 1px
width: 6px
height: 6px
border-radius: 50%
background: #d0d0d0
.action-list
position: absolute
right: 0px
top: 23px
border: 1px solid #ccc
list-style: none
padding: 15px 10px
border-radius: 4px
background: #fff
box-shadow: 0 4px 7px rgba(0, 0, 0, 0.1)
display: none
li
padding: 3px 5px
cursor: pointer
white-space: nowrap
&:hover
background: #0ebeff
color: white
border-radius: 3px
&:focus
.action-list
display: block

向配置文件中添加配置:

widgets:
- type: todo # 待办事项 Widget(啊哈呵嗨)
position: right
tasks:
About 页面大修:
done: false # 默认为false,即未完成
type: 1 # 0代表normal 1代表urgent 默认为0,即normal
TOC 滚动更新位置:
done: true
type: 0
设计 Logo SVG:
done: false
type: 0
todo 小组件大小单位用 rem:
done: false
type: 0

新建文件 themes/icarus/layout/widget/todo.ejs

<% const weekdayArr= new Array("星期日","星期一","星期二","星期三","星期四","星期五","星期六");
const monthArr = ['一月','二月','三月','四月','五月','六月','七月','八月','九月','十月','十一月','十二月'];
const myDate = new Date();
const dayofweek = weekdayArr[myDate.getDay()], day = myDate.getDate(), month = monthArr[myDate.getMonth()]; %>
<% const tasks = get_config_from_obj(widget, 'tasks');
let doneNum = 0, taskNum = 0;
for(const i in tasks) {
taskNum++;
if(tasks[i].hasOwnProperty('done') && tasks[i].done === true) doneNum++;
} %>
<%= day %>
<%= dayofweek %>
<%= month %>

Blog tasks

<%= doneNum %> / <%= taskNum %> tasks


  • <% for (const i in tasks) { %>
    <% const hasDone = tasks[i].hasOwnProperty('done') && tasks[i].done; %>
  • <% if (hasDone) { %>
    <%= i %>
    <% } else { %>

    <%= i %>

    <% } %> <% } %>

首页和非首页两种 widget

首先将 配置中的 widgets 拷贝一份到 index_widgets 后自行配置。

接着把 themes/icarus/includes/helpers/layout.js 中的所有辅助函数都加上一个参数用于判断是否首页

/**
* Helper functions for controlling layout.
*
* @example
* <%- get_widgets(position) %>
* <%- has_column() %>
* <%- column_count() %>
*/
module.exports = function(hexo) {
hexo.extend.helper.register("has_widget", function(type, isHome = false) {
const hasWidgets = hexo.extend.helper.get("has_config").bind(this)(
isHome ? "index_widgets" : "widgets"
);
if (!hasWidgets) {
return false;
}
const widgets = hexo.extend.helper.get("get_config").bind(this)(
isHome ? "index_widgets" : "widgets"
);
return widgets.some(widget => widget.hasOwnProperty("type") && widget.type === type);
});

hexo.extend.helper.register("get_widgets", function(position, isHome = false) {
const hasWidgets = hexo.extend.helper.get("has_config").bind(this)(
isHome ? "index_widgets" : "widgets"
);
if (!hasWidgets) {
return [];
}
const widgets = hexo.extend.helper.get("get_config").bind(this)(
isHome ? "index_widgets" : "widgets"
);
return widgets.filter(
widget => widget.hasOwnProperty("position") && widget.position === position
);
});

hexo.extend.helper.register("has_column", function(position, isHome = false) {
const getWidgets = hexo.extend.helper.get("get_widgets").bind(this);
return getWidgets(position, isHome).length > 0;
});

hexo.extend.helper.register("column_count", function(isHome = false) {
let columns = 1;
const hasColumn = hexo.extend.helper.get("has_column").bind(this);
columns += hasColumn("left", isHome) ? 1 : 0;
columns += hasColumn("right", isHome) ? 1 : 0;
return columns;
});
};

接着修改所有出现以上3个辅助函数的模板,比如 themes/icarus/layout/common/widget.ejs、themes/icarus/layout/layout.ejs,把 is_home() 用做参数即可。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK