1 引入音乐播放器
1
2
3
4
5
6
7
8
9
10
11
|
<!-- 【custom.html】 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css">
<div id="aplayer"></div>
<script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script>
<script>
const ap = new APlayer({
container: document.getElementById('aplayer'),
audio: []
});
</script>
|
1
2
3
4
5
|
const ap = new APlayer({
...,
// 吸底模式
fixed: true
});
|
- (4)填写 audio 数组,引入歌曲,封面和歌词
- lrcType 的值具体看【文档】
- 歌曲,封面,歌词支持远程链接 or 本地路径
- 若想通过本地路径引入,请将资源文件放在
static
文件夹下,通过hugo方法{{ .Site.Home.Permalink }}
,获取网站主路径来拼接文件路径,进行引入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
const staticDir = {{ .Site.Home.Permalink }}
const ap = new APlayer({
...,
lrcType: 3,
audio: [
{
name: 'name', // 歌名
artist: 'artist', // 歌手
url: 'url.mp3', // 歌曲路径
cover: 'cover.jpg', // 封面路径
lrc: 'lrc.lrc', // 歌词路径
},
{
// 本地路径引入写法(有子目录就在staticDir后面继续拼接)
name: 'name',
artist: 'artist',
url: staticDir + 'url.mp3',
cover: staticDir + 'cover.jpg',
lrc: staticDir + 'lrc.lrc',
}
]
});
|
到这一步音乐播放器已经引入完了
2 音乐播放器样式切换
-
(1)通过阅读Stack主题的源码可以看到,主题样式的切换是通过[data-scheme="light/darck"] {...}
-
(2)所以我们可以准备两种Aplayer的css,用[data-scheme="light"]{ 亮的css样式 }
包裹亮的,用[data-scheme="dark"]{ 暗的css样式 }
包裹暗的,这里直接给各位准备好了
-
(3)在博客主目录中创建文件assets\scss\custom.scss
,此文件为Stack主题作者留给我们加入自定义样式用的文件(可以查看主题源码同路径文件找到)
-
(4)将上述两个scss文件放到跟 custom.scss同目录下,并通过@import
来进行引入文件
1
2
3
4
5
|
/**
* 【custom.scss】
*/
@import "aplayer-light.scss";
@import "aplayer-dark.scss";
|
- (5)因为音乐播放器的css改为我们本地文件引入了,所以 custom.html 中的link标签可以将它注释掉或者删掉了
1
2
3
|
<!-- custom.html -->
...
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"> -->
|
到这一步样式随主题切换已经完成了
3 音乐播放进度保留
-
(1)实现思路:页面切换前,把歌曲的 歌曲序号,播放时长,播放状态 记录下来;在页面切换加载完之后,我们调用Aplayer的接口切换歌曲,调整时长,并选择播放or暂停
-
(2)通过console.log(ap)
打印ap对象,或者官方文档,可以找到我们需要的参数
- list.index:歌曲序号
- audio.currentTime:播放时长
- paused :播放状态(是否暂停)
-
(3)通过【官方文档】可以查找到我们需要调用的接口
- ap.list.switch(index: number):切换歌曲
- ap.seek(time: number):调整时长
- ap.play():播放歌曲
-
(4)需要的东西已经找齐,只需在 custom.html 加入以下代码即可
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
|
<!-- 【custom.html】 -->
...
<script>
...
/**
* 页面销毁前监听
*/
window.onbeforeunload = () => {
// 将播放信息用对象封装,并存入到localStorage中
const playInfo = {
index: ap.list.index,
currentTime: ap.audio.currentTime,
paused: ap.paused
};
localStorage.setItem("playInfo", JSON.stringify(playInfo));
};
/**
* 页面加载后监听
*/
window.onload = () => {
// 从localStorage取出播放信息
const playInfo = JSON.parse(localStorage.getItem("playInfo"));
if (!playInfo) {
return;
}
// 切换歌曲
ap.list.switch(playInfo.index);
// 等待500ms再执行下一步(切换歌曲需要点时间,不能立马调歌曲进度条)
setTimeout(() => {
// 调整时长
ap.seek(playInfo.currentTime);
// 是否播放
if (!playInfo.paused) {
ap.play()
}
}, 500);
};
</script>
|
到这一步音乐播放进行已经完成了
4 引入PJAX
4.1 基本引入
-
(1)PJAX主要分为两个版本,带JQuery 和 不带JQuery的,下面我演示的是不带JQuery版本的
-
(2)分析页面元素,看哪些是需要我们重新加载的,可以发现是左侧边栏,中间内容,右侧边栏
-
(3)查看页面源代码,可以发现这些元素都被一个<div class="main-container">...</div>
包裹着,所以我们将这元素定为要刷新的对象
-
(4)根据官方文档,在 custom.html 加入以下代码来引入PJAX
1
2
3
4
5
6
7
8
9
10
|
<!-- 【custom.html】 -->
...
<script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script>
<script>
var pjax = new Pjax({
selectors: [
".main-container"
]
})
</script>
|
这样算是已经基本引入了PJAX,但也带来了不少问题,我们一步一步进行修复
4.2 文章样式修复
-
(1)随便点进其中一篇文章,可以发现文章内容的样式丢失,缺少文章该有的边框
-
(2)通过手动刷新,对比页面元素发现,决定文章样式的是<body>
标签中的class名: article-page,存在此class名就会识别到文章该有的css
-
(3)因为<body>
内包含整个页面的所有内容,包括我们的音乐播放器,所以不能让PJAX监听<body>
标签
- 阅读官方文档发现,官方提供了数据预处理方法,让我们处理数据
- 我们通过预处理数据,获取到新页面的className,然后我们手动将这className设置到
<body>
上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<!-- 【custom.html】 -->
...
<script>
...
pjax._handleResponse = pjax.handleResponse;
pjax.handleResponse = function(responseText, request, href, options) {
if (request.responseText.match("<html")) {
// 将新页面的html字符串解析成DOM对象
let newDom = new DOMParser().parseFromString(responseText, 'text/html');
// 获取新页面中body的className,并设置回当前页面
let bodyClass = newDom.body.className;
document.body.setAttribute("class", bodyClass)
// 放行,交给pjax自己处理
pjax._handleResponse(responseText, request, href, options);
} else {
// handle non-HTML response here
}
}
</script>
|
这样我们切页面后,都会自动把body中的className更新,就不会丢失样式了
4.3 主题切换修复
-
(1)当我们切换页面后,点击左下角切换主题颜色的按钮,会发现没有效果,主题颜色切换失效了
-
(2)阅读Stack主题源码\assets\ts\colorScheme.ts
发现,在脚本初始化时,会给元素绑定一个点击事件。但因为页面切换了,替换了该元素,但该元素没有重新绑定点击事件,导致点击主题切换失效
-
(3)解决思路:在PJAX切换完页面后,重新执行一遍colorScheme.ts
的初始化,使元素重新绑定点击事件。
- 阅读源码,发现
colorScheme.ts
被main.ts
引用,在main.ts
中执行了初始化,并且main.ts
生成了个全局变量 Stack
- 所以在PJAX执行完后,使用全局变量 Stack ,执行里面的初始化方法,重新执行一遍脚本,来绑定点击事件
- 阅读PJAX文档,发现官方也提供了PJAX执行完后的事件,我们执行监听这个事件,Stack 执行初始化就好
1
2
3
4
5
6
7
8
9
|
<!-- 【custom.html】 -->
...
<script>
...
document.addEventListener('pjax:complete', () => {
// Stack脚本初始化
window.Stack.init();
})
</script>
|
这样元素的点击事件重新绑定,主题颜色就能正常切换了
4.4 文章搜索修复
-
(1)使用文章搜索功能时,输入关键词,无任何搜索记录,搜索功能失效
-
(2)查看layouts\ts\search.tsx
文件,发现情况和上面的colorScheme.ts
类似,存在绑定事件
-
(3)解决思路类似上面的,把 search.tsx 初始化内容,封装为一个函数,并把函数 export 出来,由 main.ts 引入这个函数,并放到 Stack.init() 的方法中,利用此方法来重新初始化搜索脚本 - (以下操作请复制同路径同名文件到自己主目录下修改,不要在主题源码中修改)
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
|
/**
* 【search.tsx】
* 记得把window.addEventListener('load' ...这部分代码注释掉
* 初始化工作交给Stack.init()处理了,不需要这个了
*/
...
function searchInit() {
let search = document.querySelector('.search-result');
if (search) {
const searchForm = document.querySelector('.search-form') as HTMLFormElement,
searchInput = searchForm.querySelector('input') as HTMLInputElement,
searchResultList = document.querySelector('.search-result--list') as HTMLDivElement,
searchResultTitle = document.querySelector('.search-result--title') as HTMLHeadingElement;
new Search({
form: searchForm,
input: searchInput,
list: searchResultList,
resultTitle: searchResultTitle,
resultTitleTemplate: window.searchResultTitleTemplate
});
}
}
export {
searchInit
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
/**
* 【main.ts】
*/
...
import { searchInit } from "ts/search";
let Stack = {
init: () => {
...
// 调用search脚本初始化方法
searchInit();
}
}
|
- (4)tsx 类型的文件引入方式有点特殊,需要我们修改以下 main.ts 的引入方式,修改
layouts\partials\footer\components\script.html
,改法参考layouts\page\search.html
,把"JSXFactory" "createElement"
补充上就好
这样search.tsx能正常初始化,文章搜索功能恢复
4.5 搜索内容跳转修复
1
2
3
4
5
6
7
8
9
10
11
12
|
/**
* 【search.tsx】
*/
private async doSearch(keywords: string[]) {
...
/*
方法末尾,让pjax重新解析文档数据,识别动态渲染的数据
虽然当前文件没有pjax对象,但最后静态页面会生成一个整体的js文件
pjax对象那时就能识别到,就可成功调用
*/
pjax.refresh(document);
}
|
这样动态渲染出的页面数据就能被PJAX识别到,就不会刷新页面跳转了
4.6 文章评论修复
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<!-- custom.thml -->
...
<script>
var pjax = new Pjax({
selectors: [
...
".js-Pjax"
]
})
</script>
<div class="js-Pjax">
<script>
// TODO: 判断当前是否是文档,且是否开启评论功能
(function() {
let script = document.createElement('script');
// 将对应评论的脚本内容填进去
script.setAttribute('key', 'value');
...
// 寻找合适的元素,添加脚本进去
document.querySelector('xxx').appendchild(script)
})(document)
</script>
<div>
|
这样PJAX会自动加载我们的评论脚本
5 引入进度条
-
(1)由于使用了PJAX后,无法得知页面的加载情况是否完成,所以引入一个伪进度条,来显示页面内容进度
-
(2)前往【topbar】,点击下载zip包,将解压后的 topbar.min.js 放到assets\js\topbar.min.js
-
(3)通过监听PJAX两个事件 pjax:send 和 pjax:complete 实现伪进度条
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
|
<!--custom.html-->
...
{{ with resources.Get "js/topbar.min.js" }}
<!-- 引入本地JS脚本 -->
<script src={{ .Permalink }}></script>
{{ end }}
<script>
// 修改进度条颜色
topbar.config({
barColors: {
'0': 'rgba(255, 255, 255, 1)', // 进度0%白色
'1.0': 'rgba(0, 149, 234, 1)' // 进度100%蓝色
}
})
document.addEventListener('pjax:send', () => {
// 显示顶部进度条
topbar.show();
})
document.addEventListener('pjax:complete', () => {
....
// 隐藏顶部进度条
topbar.hide();
})
</script>
|
这样伪进度条就成功引入了,能大概知道页面的加载情况了