很久之前我就有将自己喜欢的歌曲分享给别人的梦想,而现在正值我的博客搭建完成,我终于可以通过在博客中加入歌词栏实现我的梦想了。这篇文章就来简单介绍一下在博客中添加歌词栏的流程。
一、添加html文件
首先,在source/_includes/custom/
路径下新建lyrics.html
文件,我们要添加的歌词栏就是在该文件中实现的。该文件的内容暂且按下不表,我们首先要让我们添加的html文件在博客中显示出来。
我们知道,source/_layouts/default.html
文件定义了博客页面的基本框架,当我们需要添加一些内容的时候,可以修改该文件,这样在博客的任何页面,我们添加的内容都可以显示出来。如大家所见,本博客中歌词栏的位置位于导航栏的下面,正文部分的上面。通过查看default.html
文件,我们发现,{% include navigation.html %}
一行对应着博客的导航栏,而我们要加入的lyrics.html
,则应该放在这一行的下面。
在这一行的下面加入{% include custom/lyrics.html %}
一行,这样一来,我们添加的歌词栏就在博客页面中找到了自己的归属。
二、歌词栏的实现
关于歌词栏,我的想法是这样的:中间部分显示歌词,包括日文原文、中文译文以及歌曲名称和歌手名称,其中歌曲名称是一个超链接,指向网易云音乐中对应歌曲的页面。两侧分别是两个按钮,即切换到上一句歌词和下一句歌词。
我在source/
文件夹下新建了lyrics/
文件夹,歌词栏显示的每句歌词都对应着该文件夹下的一个txt文件,文件名形如1.txt
、2.txt
这样。除了这些歌词文件,该文件夹下还有一个num.txt
文件,记录着当前歌词的总数量。在博客页面第一次加载的时候,首先访问num.txt
文件,获取歌词总数量totNumber
,然后随机生成一个介于1
到totNumber
之间的数字curNumber
,并且访问[curNumber].txt
文件,将该文件对应的歌词显示在歌词栏中。每当点击上一句歌词或下一句歌词按钮时,将curNumber
减一或加一,并且访问对应的文件并且更新歌词栏的信息。
那么问题来了:如何访问服务器端的一个文件呢? 我们需要用到XMLHttpRequest。通过XMLHttpRequest,我们可以实现很多功能,比如:
1. 在不重新加载页面的情况下更新网页;
2. 在页面已加载后从服务器请求数据;
3. 在页面已加载后从服务器接收数据;
4. 在后台向服务器发送数据。
而我们则是利用了第二点“在页面已加载后从服务器请求数据”的功能。相关代码如下:
XMLHttpRequest的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function loadXMLDoc ( url ){
xmlhttp = null ;
if ( window . XMLHttpRequest ){ // code for Firefox, Opera, IE7, etc.
xmlhttp = new XMLHttpRequest ();
} else if ( window . ActiveXObject ){ // code for IE6, IE5
xmlhttp = new ActiveXObject ( "Microsoft.XMLHTTP" );
}
if ( xmlhttp != null ){
xmlhttp . onreadystatechange = state_change ;
xmlhttp . open ( "GET" , url , true );
xmlhttp . send ( null );
}
}
function state_change (){
if ( xmlhttp . readyState == 4 ){ // 4="loaded"
if ( xmlhttp . status == 200 ){ // 200="OK"
var text = xmlhttp . responseText ;
}
}
}
在上述代码中,onreadystatechange
是一个事件句柄,它的值是一个函数名称。当我们建立的XMLHttpRequest
对象的状态发生改变后,会触发此函数。该对象的状态从0到4进行变化,其中状态为4意味着请求完成,然后我们才可以进行后续处理。
在该对象状态发生改变后,如果是请求完成状态,我们就可以通过该对象的status
属性判断我们的请求是否成功。status
属性对应着该请求的http状态代码,如果成功,状态代码即为200
。如果成功,我们即可通过responseText
属性获得我们请求的内容。
明确了XMLHttpRequest的作用,完整的html代码也就呼之欲出:
lyrics.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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<div id= "lyrics" >
<a id= "leftArrow" onclick= "last();" href= "javascript:void(0)" ></a>
<a id= "rightArrow" onclick= "next();" href= "javascript:void(0)" ></a>
<script type= "text/javascript" >
var xmlhttp ;
var totNumber = - 1 ;
var curNumber ;
function getLRCNum (){
loadXMLDoc ( "/lyrics/num.txt" );
}
function adjustPos (){
var pos = $ ( "#lyrics" ). height () / 2 - 45 ;
$ ( "#leftArrow" ). css ( "margin-top" , pos );
$ ( "#rightArrow" ). css ( "margin-top" , pos );
}
function last (){
curNumber -- ;
if ( curNumber <= 0 )
curNumber = totNumber ;
loadXMLDoc ( "/lyrics/" + curNumber + ".txt" );
}
function next (){
curNumber ++ ;
if ( curNumber > totNumber )
curNumber = 1 ;
loadXMLDoc ( "/lyrics/" + curNumber + ".txt" );
}
function loadXMLDoc ( url ){
xmlhttp = null ;
if ( window . XMLHttpRequest ){ // code for Firefox, Opera, IE7, etc.
xmlhttp = new XMLHttpRequest ();
} else if ( window . ActiveXObject ){ // code for IE6, IE5
xmlhttp = new ActiveXObject ( "Microsoft.XMLHTTP" );
}
if ( xmlhttp != null ){
xmlhttp . onreadystatechange = state_change ;
xmlhttp . open ( "GET" , url , true );
xmlhttp . send ( null );
}
}
function state_change (){
if ( xmlhttp . readyState == 4 ){ // 4="loaded"
if ( xmlhttp . status == 200 ){ // 200="OK"
//处理传回的数据:
var text = xmlhttp . responseText ;
if ( totNumber < 0 ){ //如果是获取歌词数量;
totNumber = parseInt ( text );
curNumber = Math . floor ( Math . random () * totNumber ) + 1 ;
loadXMLDoc ( "/lyrics/" + curNumber + ".txt" );
} else {
var splited = text . split ( "\n" );
$ ( "#lyric" ). html ( splited [ 0 ]);
$ ( "#translate" ). html ( splited [ 1 ]);
$ ( "#singer" ). html ( "————" + splited [ 2 ]);
$ ( "#song" ). html ( "「" + splited [ 3 ] + "」" );
$ ( "#song" ). attr ( "href" , splited [ 4 ]);
//调节两个按钮的位置:
$ ( "#leftArrow" ). show ();
$ ( "#rightArrow" ). show ();
adjustPos ();
}
}
}
}
</script>
<div id= "lyricsContent" " >
<h3 id= "lyric" ></h3>
<p id= "translate" ></p>
<p id= "from" ><span id= "singer" ></span><a id= "song" href= "javascript:void(0)" ></a></p>
</div>
</div>
我们注意到,getLRCNum()
函数是用来获取歌词总数量的,需要在文档加载完成时调用。source/javascripts/octopress.js
文件中的$('document').ready(function())
函数是用来定义文档加载完成时所触发的函数的,我们需要在该函数的末尾调用getLRCNum()
函数,确保在文档加载完成时,可以立刻获取当前歌词的总数量。
然后,last()
和next()
函数则是分别在两个按钮被按下时触发的,它们更改了curNumber
的值,并且请求了新的歌词内容。在新的歌词内容请求完成后,我们利用jQuery动态地改变了歌词栏内部的一些html标签所显示的文本及其href
属性,并且通过adjustPos()
函数调节了两个按钮的位置,使其在垂直方向上一直处于正中央。
三、定制css样式表
当然,为了显示出正常的效果,我们需要对html文件中涉及到的标签定制css样式表。这些工作仍然是在sass/custom/_styles.scss
文件中完成的。由于笔者不是学美工出身,最后显示的效果或多或少会缺乏美感,这里贴出我个人定制的样式表,仅供抛砖引玉之用。
我个人定制的样式表
展开
_styles.scss
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//Lyrics:
#lyrics {
background-color : #eeeeee ;
border-bottom-width : 1 px ;
border-bottom-style : solid ;
border-bottom-color : rgb ( 140 , 140 , 140 );
}
#lyricsContent {
padding-top : 22 px ;
padding-bottom : 10 px ;
padding-left : 50 px ;
padding-right : 50 px ;
background-color : rgba ( 0 , 0 , 0 , 0 );
}
#leftArrow {
width : 30 px ;
height : 90 px ;
background-image : url("/images/lyrics_left.png") ;
float : left ;
display : none ;
}
#leftArrow :hover {
background-position : -30 px 0 px
}
#rightArrow {
width : 30 px ;
height : 90 px ;
background-image : url("/images/lyrics_right.png") ;
float : right ;
display : none ;
}
#rightArrow :hover {
background-position : -30 px 0 px
}
#lyric {
font-family : MS Mincho ;
font-size : 28 px ;
margin-bottom : 6 px ;
text-align : center ; //居中显示
line-height : 32 px ;
}
#translate {
font-family : KaiTi ;
font-size : 22 px ;
margin-bottom : 6 px ;
text-align : center ; //居中显示
line-height : 26 px ;
}
#from {
font-family : MS gothic ;
font-size : 20 px ;
font-weight : bold ;
margin-bottom : 0 px ;
text-align : right ; //向右对齐
line-height : 24 px ;
}
四、总结与展望
本文简单介绍了我为博客添加歌词栏的流程。在歌词栏添加好后,如果要添加新的歌词,只需要改动两处:一是更新source/lyrics/num.txt
中记录的歌词总数;二是在source/lyrics/
目录下建立一个新的文件保存要添加的歌词。已经写好的html代码和javascript代码均不需改动。这样的歌词栏已经大致满足了我的要求,但是必须承认,它还有一些可以改进的地方,在这里列出三点:
1. 不需要利用一个额外的文件num.txt
来存储歌词总数,而是直接通过文件夹下的文件数量获得歌词总数。我相信肯定有相关的API可供调用,但是到目前为止还仍未找到。
2. 完善多分辨率支持,使得歌词栏在电脑、平板和手机上都可以正常显示。
3. 改进css样式表,使得歌词栏的外观更加漂亮。