前言
制作粗糙
一、界面
初始:
搜索:
鼠标悬浮选择:
点击进入歌词播放界面:
播放效果:
可以切换回列表(点击歌词回到歌词界面):
二、源代码
js使用了自己封装的ajax,css样式使用了过渡效果
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./Search_song.css">
<link rel="stylesheet" href="./font_0drpqg1pu9nr/iconfont.css">
</head>
<body>
<div class="wrap">
<div class="search_top">
<!-- 搜索框 -->
<input type="text" id="search-input">
<input type="button" value="搜索" id="search-btn">
<span class="iconfont icon-geci-copy" title="歌词"></span>
<span class="iconfont icon-gedan" title="歌单"></span>
</div>
<!-- 播放 -->
<audio src="" controls id="mp3"></audio>
<br>
<!-- 歌曲列表 -->
<ul class="songs-list">
<li>
</li>
</ul>
<!-- 歌词显示 -->
<div class="lyric-show">
<ul class="lyric-list">
<li class="every-lyric"></li>
</ul>
</div>
</div>
<script src="./Search_song.js"></script>
</body>
</html>
css(less生成):
* {
padding: 0;
margin: 0;
}
.wrap {
width: 800px;
height: 500px;
border: 1px solid black;
margin: 0 auto;
position: relative;
top: 130px;
background-color: aliceblue;
}
.wrap #mp3 {
height: 40px;
width: 680px;
position: absolute;
bottom: 10px;
left: 50px;
}
.wrap .search_top {
height: 50px;
width: 800px;
line-height: 50px;
position: relative;
}
.wrap .search_top #search-input {
height: 30px;
width: 270px;
line-height: 50px;
position: absolute;
left: 220px;
top: 10px;
}
.wrap .search_top #search-btn {
height: 30px;
width: 40px;
line-height: 50px;
position: absolute;
left: 500px;
top: 12px;
cursor: pointer;
}
.wrap .search_top .iconfont.icon-geci-copy {
width: 50px;
height: 50px;
line-height: 50px;
font-size: 50px;
margin: 0 auto;
position: absolute;
right: 120px;
top: 6px;
cursor: pointer;
}
.wrap .search_top .iconfont.icon-gedan {
width: 50px;
height: 50px;
line-height: 50px;
font-size: 32px;
margin: 0 auto;
position: absolute;
right: 50px;
top: 6px;
cursor: pointer;
}
.wrap .songs-list {
width: 500px;
height: 360px;
overflow-y: scroll;
list-style: none;
position: relative;
margin: 0 auto;
}
.wrap .songs-list li {
cursor: pointer;
height: 50px;
border-radius: 5px;
}
.wrap .songs-list li h1 {
font-size: 18px;
color: cadetblue;
}
.wrap .songs-list li .s1,
.wrap .songs-list li .s2 {
font-size: 12px;
color: #099e6d;
}
.wrap .songs-list li:hover {
background-color: darkgrey;
opacity: 0.7;
}
.wrap .lyric-show {
width: 500px;
height: 360px;
text-align: center;
list-style: none;
margin: 0 auto;
overflow-y: hidden;
}
.wrap .lyric-show .lyric-list {
list-style: none;
position: relative;
top: 180px;
}
.wrap .lyric-show .lyric-list .every-lyric {
height: 30px;
text-align: center;
font-size: 16px;
color: dimgray;
transition: all 0.5s linear;
}
.wrap .lyric-show .lyric-list .every-lyric.active {
font-size: 20px;
color: #09e29a;
opacity: 0.5;
}
js
var songsList=document.getElementsByClassName('songs-list')[0]
var searchBtn=document.getElementById('search-btn')
var searchInput=document.getElementById('search-input')
var lyricList=document.getElementsByClassName('lyric-list')[0]
var listBtn=document.getElementsByClassName('iconfont icon-gedan')[0]
var lyricBtn=document.getElementsByClassName('iconfont icon-geci-copy')[0]
//定义歌词数组,初始化为空
var lyricArr=[]
listBtn.addEventListener('click',function(){
songsList.style.display = '';
lyricList.style.display = 'none';
})
lyricBtn.addEventListener('click',function(){
songsList.style.display = 'none';
lyricList.style.display = '';
})
var init=function(){
lyricList.style.display = 'none';
songsList.style.display = 'none';
}
init()
//封装ajax方法
//参数: pathname路径 bixuancanshu--传进来的参数,例如id、name... shijicanshu---对应的值
var SearchAjax=function(pathname,bixuancanshu,shijicanshu,callback){
var xhr=new XMLHttpRequest();
var url='http://localhost:3000'+pathname+'?'+bixuancanshu+'='+shijicanshu;
xhr.open('GET',url,true);
xhr.send();
xhr.onreadystatechange=function(){
if(xhr.status==200&&xhr.readyState==4){
callback(JSON.parse(xhr.response))
}
}
}
//封装获取歌曲url的方法
var getSongUrl=function(id_num){
SearchAjax('/song/url','id',id_num,function(jieguo){
var songUrl=jieguo.data[0].url;
mp3.src=songUrl;
})
}
//封装渲染歌词的函数,显示在指定部分
var renderLyricList=function(){
//设置空字符串
var lyricStr=''
//定义基本字符串
var lyricTpl='<li class="every-lyric">歌词显示</li>'
//拼接替换歌词数组中的字符串
lyricArr.forEach(function(item){
lyricStr+=lyricTpl.replace('歌词显示',item.content)
})
console.log(lyricStr)
lyricList.innerHTML=lyricStr
topNum=160
index=0
lyricList.style.top=topNum+'px'
}
//处理由ajax请求得到的大的字符串
var handleLyric=function(baseStr){
//可知字符串可由\n分割
var bigArr=baseStr.split('\n')
console.log(bigArr)
//得到一个由很多项字符串构成的字符数组
//由于字符串包含时间和歌词内容,所以将其由']'分割
//因为是一个字符串数组,所以使用forEach()遍历
bigArr.forEach(function(item){
//对于每一个字符串使用']'分割
var doubleArr=item.split(']');
//在歌词字符串数组中有空行项去掉空行
if(doubleArr[1]==undefined){
doubleArr[1]='';
}
//去掉换行之后,剩下的doubleArr[1]对应的就是歌词的文本内容content
var contentStr=doubleArr[1];
//去掉时间的'['
//利用slice分割,左闭右开,所以想要去掉第一个数,要从1到length
var timeStr=doubleArr[0].slice(1,doubleArr[0].length);
//计算秒数,最后对于歌词的显示行,要根据当前时间判断,
//所以需将timeStr转换成秒数,此时的时间格式1:2.1564,所以需要对其进行再次分割
var tArr=timeStr.split(':');
//计算实际秒数
var realTime=tArr[0]*60+parseFloat(tArr[1]);
//定义一个新的对象,属性:时间+歌词内容
var newObj={
time:realTime,
content:contentStr
}
//将得到的时间、歌词内容的新对象存放入歌词数组
lyricArr.push(newObj);
})
//获得处理后的歌词数组后将其显示在页面中,调用render方法渲染
//渲染一波
renderLyricList();
}
//封装获取歌词的方法
var getLyric=function(id_num){
//进行ajax请求获取歌词
SearchAjax('/lyric','id',id_num,function(jieguo){
// console.log(jieguo.lrc.lyric)
//得到的结果:
// [00:23.75]什么叫做1 什么叫做2 什么叫做3
// [00:29.33]问题太巧妙 数字太难搞 我全不知道...
//得到的结果是一个大的字符串,并且因为含有\n而自动换行
//所以需要将这个大的字符串分割处理
handleLyric(jieguo.lrc.lyric)
})
}
//封装一个给每个li挂监听器的方法
var addEventListenerForLi=function(){
var everysongs=document.getElementsByClassName("every-song")
for(var i=0;i<everysongs.length;i++){
everysongs[i].addEventListener('click',function(){
// 利用这个值发起url的ajax请求
getSongUrl(this.getAttribute('idnum'))
//获取歌词,同样根据id获取歌词,发起对音乐歌词的ajax请求
lyricArr=[]
lyricList.innerHTML=''
getLyric(this.getAttribute('idnum'))
songsList.style.display = 'none';
lyricList.style.display = '';
})
}
}
//封装填充1歌单的方法
var render=function(shuzu){
// 字符串拼接替换
var str=''
var tpl = '<li idnum=$123456$ class="every-song">' +
'<h1>$歌名$</h1>' +
'<span class="s1">歌手名字:$123$</span>' +'  '+
'<span class="s2">专辑名字:&22&</span>' +
'</li>'
//forEach() 方法对数组的每个元素执行一次提供的函数
shuzu.forEach(function(item){
str += tpl.replace('$歌名$', item.name)
.replace('$123$', item.artists[0].name)
.replace('&22&', item.album.name)
.replace('$123456$',item.id)
})
songsList.innerHTML=str
// 给每一个li挂载监听器
addEventListenerForLi()
}
//封装搜索方法--根据关键字搜索
var searchSong=function(keyw){
SearchAjax('/search','keywords',keyw,function(res){
var songsArr=res.result.songs;
render(songsArr);
})
}
// 搜索键挂载监听器
searchBtn.addEventListener('click',function(){
searchSong(searchInput.value)
lyricList.style.display = 'none';
songsList.style.display = '';
})
var index=0
var topNum=180
mp3.addEventListener('timeupdate',function(){
var everyLyrics=document.getElementsByClassName('every-lyric')
// mp3.currentTime 是以s为单位的
if((mp3.currentTime-lyricArr[index].time)>0&&(mp3.currentTime-lyricArr[index].time)<0.3){
// 歌词滚动
topNum-=30
lyricList.style.top=topNum+'px'
// 当前li特殊展示
everyLyrics[index].className+=' active'
if(index>0){
everyLyrics[index-1].className='every-lyric'
}
index++
}
})
百度网盘提取
链接:https://pan.baidu.com/s/1s-9FJeP4c3QBNI4TOjx2MA
提取码:4w3o