简介:
这是一个练习的小案例,支持在线搜索,调用的是免费的音乐接口,
主要用到了:弹性盒,3D效果,Ajax 等技术
效果如下:

接口:
接口地址: https://api.apiopen.top/searchMusic
请求示例: https://api.apiopen.top/searchMusic?name=雅俗共赏
请求结果:
{ "author": "许嵩", "link": "http://music.163.com/#/song?id=411214279", "pic": "http://p2.music.126.net/Wcs2dbukFx3TUWkRuxVCpw==/3431575794705764.jpg?param=300x300", "type": "netease", "title": "雅俗共赏", "lrc": "", "songid": 411214279, "url": "http://music.163.com/song/media/outer/url?id=411214279.mp3" }
主体界面:
整体采用三段式样式,分为左中右三块,
左侧为装饰性样式没有实际作用,计划添加 点击切换歌词面板功能
中间部分为播放器主体,包括搜索框,搜索按钮,歌曲封面,歌曲名,作者,以及audio播放器,
右侧没有进行功能添加,计划添加喜欢收藏 下一首 上一首 分享 。
HTM代码如下:
<body> <div id="box"> <!-- 搜索信息展示版块 ==>默认display:none --> <div id="display"> <div class="close"><span>X</span></div> <h2>歌曲列表</h2> <div id="infoBox"> <!-- <p>歌曲名-Redbone <span>听这首</span></p> --> </div> </div> <!-- 左侧版块 --> <div class="leftBg"> <!-- 滚动信息 --> <marquee behavior="scroll" direction="left">Current version of Baidu Music Interface</marquee> <!-- 文字 ==> THIS IS MUSIC --> <p class="p1">THIS <br>IS </p> <p class="p2">MUSIC</p> <!-- 白色装饰条 --> <div></div> <div></div> </div> <!-- 中间板块 --> <div class="centerBg"> <!-- 搜索框+搜索按钮 --> <input type="text" value="" id="search"><span id="searchSpan">搜索</span> <div id="bfBox"> <!-- 歌曲图片 --> <div class="imgBox"> <img src="http://p2.music.126.net/5i5SKVW_F1ub2BgDeyjI5A==/3225967119049341.jpg?param=300x300" alt=""> </div> <!-- 歌曲信息 --> <div class="info"> <p>Come and Get Your Love</p> <p>Redbone</p> <!-- 播放器 --> <audio src="http://music.163.com/song/media/outer/url?id=28864241.mp3" controls></audio> </div> </div> </div> <!-- 右侧板块 ==> 未开发 --> <div class="rightBg"> </div> </div> </body>
CSS代码如下:
<style> body{ /* 开启视距 */ perspective: 1400px; position: relative; } /* 主页面 */ #box{ width: 800px; height:500px; background-color: rgb(255, 255, 255); margin: 80px auto; /* transition: 2s; */ /* 开启3D空间 */ transform-style: preserve-3d; display: flex; justify-content: flex-start; box-shadow: 0 15px 35px -5px rgba(50,88,130,.32); } /* 左侧版块 */ #box .leftBg{ width: 190px; height: 440px; background-color: #222; padding: 30px; position: relative; } /* 中间板块 */ #box .centerBg{ width: 500px; height: 500px; background-color: rgb(241, 241, 241); } /* 右侧版块 */ #box .rightBg{ width: 100px; height: 500px; background-color: #222; } /* 左侧滚动信息 */ #box .leftBg marquee{ margin-top: 15px; color: #fff; font-size: 12px; } /* 左侧文字 ==> THIS IS MUSIC */ #box .leftBg p{ transform: translateZ(100px) } #box .leftBg .p1{ color: rgb(253, 0, 0); font-size: 50px; margin: 0; padding-top:50px; } #box .leftBg .p2 { color: rgb(253, 0, 0); font-size: 75px; font-weight: bold; margin: 10px auto; } /* 左侧装饰条两根 */ #box .leftBg div{ width: 100%; height: 5px; background-color: rgb(241, 241, 241); position: absolute; } #box .leftBg div:nth-child(4){ left: 0; bottom:60px; } #box .leftBg div:nth-child(5){ left: 0; bottom:45px; } /* 搜索框样式 */ #box #search{ outline:none; border: none; width: 250px; height: 25px; border-top-left-radius: 30px; border-bottom-left-radius: 30px; background-color: #fff; margin-top: 20px; margin-left: 50px; padding: 5px 15px; } /* 搜索按钮样式 */ #box #searchSpan{ display: inline-block; padding: 5px 15px; width: 25px; height: 25px; text-align: center; line-height: 25px; background-color: #222; color: #fff; font-size: 12px; border-top-right-radius: 30px; border-bottom-right-radius: 30px; cursor: pointer; } /* 中间板块 播放器盒子 */ #box .centerBg #bfBox{ width: 240px; height: 330px; border-radius: 10px; background-color: #eef3f7; margin-left:130px; margin-top: 20px; position: relative; box-shadow: 0 15px 35px -5px rgba(50,88,130,.32); display: flex; align-items: flex-end; padding: 30px; } /* 歌曲图片样式 */ #box .centerBg .imgBox{ width: 160px; height: 160px; border-radius: 10px; position: absolute; top: 30px; left: -30px; box-shadow: 0 10px 20px 0 rgba(76,70,124,.5); transform: translateZ(100px) } #box .centerBg .imgBox img{ width: 160px; height: 160px; border-radius: 5px; } /* 歌曲信息样式 */ #box .centerBg #bfBox .info { width: 100%; height: 45%; } #box .centerBg #bfBox .info p{ margin: 0; color:#71829e; } #box .centerBg #bfBox .info p{ margin-top:15px; font-size: 20px; font-weight: bolder; } #box .centerBg #bfBox .info p:nth-of-type(2){ margin-top: 5px; font-size: 16px; } /* audio播放器样式 */ #box .centerBg #bfBox .info audio{ width: 100%; padding-top: 10px; outline:none; } /* 搜索展示界面样式 */ #display{ width: 400px; height: 500px; background-color: rgb(245, 238, 238); box-shadow: 0 10px 20px 0 rgba(76,70,124,.5); display: none; border-radius: 10px; position: absolute; top: 0; left: 25%; transform: translateZ(110px); padding: 30px; } /* 搜索展示界面 标题====>(歌曲列表) */ #display h2{ text-align: center; color: rgb(255, 0, 0); } #display p { color: #607391; font-size: 12px; margin: 5px; 0; padding:0 10px; height: 35px; line-height: 35px; } #display p:hover{ background-color: #fff; } #display p span { margin-top: 5px; float: right; width: 45px; height: 25px;; box-sizing: border-box; border-radius: 5px; background-color: rgb(55, 110, 214); box-shadow: inset 1px 1px 5px 0px rgba(50,88,130,.32); text-align: center; line-height: 25px; font-size: 12px; color: #ccc; cursor: pointer; } /* 展示版块关闭按钮样式 */ #display .close{ position: relative; } #display .close span{ width: 30px; height: 30px; border-radius: 50%; position: absolute; top: 10px; right: 10px; background-color: lightblue; box-shadow: inset 1px 1px 5px 0px rgba(50,88,130,.32); text-align: center; line-height: 30px; cursor: pointer; } </style>
视差风格的实现
视差滚动(Parallax Scrolling)是指让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验。作为网页设计的热点趋势,越来越多的网站应用了这项技术。
这是借鉴了视差滚动的原理,使页面产生实时改变的3D效果,具体实现如下:
1.思路:
将播放器开启3D空间,
在鼠标横向移动时,让播放器沿Y轴旋转,在鼠标纵向移动时,让播放器沿X轴旋转,
将视距增大至1400(此时的视距刚刚好)。
2.代码实现:
//body{perspective: 1400px;} 开启视距 // 鼠标在body上移动,将坐标转换成旋转角度,形成视差 document.onmousemove = function (eve){ var e = eve || window.event; that.x = (e.clientX-that.ele.offsetWidth/2)*0.01; that.y = -(e.clientY-that.ele.offsetTop/2)*0.01;; that.display(); } //形成视差 display(){ this.box.style.transform = "rotateY(" + this.x + "deg) rotateX(" + this.y + "deg)"; }
控制台信息:

ajax请求接口数据:
ajax是前端向后端请求发送数据的重要手段,并且可以实现无刷新加载数据。
该案例使用的接口返回的数据是json,使用ajax可以正确的解析数据
1.思路:
当用户点击搜索按钮时,获取输入框的数据,拼接到接口地址上
使用ajax请求拼接好的地址,拿到数据
2.实现如下:
//搜索框发生改变,生成搜索内容 this.searchBtn.onclick = function(){ var str = ""; that.url = "https://api.apiopen.top/searchMusic"; str = "?name="+that.text.value; that.url += str; that.load(); } // ajax请求数据 load(){ var that = this; ajax({ url:this.url, success:function(res){ that.res = JSON.parse(res); console.log(that.res.result); } }) }
这里使用的是封装好的ajax函数,函数如下
//ajax封装函数 function ajax(options) { var { type, url, success, error, data, timeout } = options; type = type || "get"; data = data || {}; timeout = timeout || 2000; var str = ""; for (var i in data) { str += `${i}=${data[i]}&`; } if (type == "get") { var d = new Date(); url = url + "?" + str + "__qft=" + d.getTime(); } var xhr = new XMLHttpRequest(); xhr.open(type, url, true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { success && success(xhr.responseText); error = null; } else if (xhr.readyState == 4 && xhr.status != 200) { error && error(xhr.status); success = null; error = null; } } setTimeout(() => { error && error("timeout"); success = null; }, timeout); if (type == "post") { xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.send(str) } else { xhr.send() } }
请求到的数据如下

这里我们需要的是result数组中的信息
所以console.log(that.res.result);得到正确的数据

展示数据
得到数据之后下面要做的就是将请求到的数据展示出来
在页面结构中,有展示页面的结构,需求是点击搜索按钮,展示页面将数据展示出来
展示格式为:歌曲名-歌手 并且可以选择指定歌曲播放
1.思路:
点击搜索按钮后,展示页面显示(dispaly:“block”),
将数据拼接为HTML代码插入到展示页面中,
给所有的信息绑定事件,点击信息记录当前信息的索引(可以使用事件委托优化性能)。
2.代码实现:
//生成搜索内容 //请求完成,获得数据,将数据展示到搜索页面上 display2(){ this.displayPage.style.display = "block" var str = ""; // 如果搜索为空,显示搜索内容为空 //否则,展示搜索内容 if(this.res.result.length == 0){ str = "<h3>搜索内容为空</h3>" }else{ for(var i=0;i<this.res.result.length;i++){ str+=`<p>${this.res.result[i].title}-${this.res.result[i].author} <span>听这首</span></p>` } } this.infoBox.innerHTML = str; this.change(); this.disClose(); } //点击选中的歌曲,改变播放器页面的信息 change(){ var that = this; this.span = document.querySelectorAll("#infoBox p span"); console.log(this.span); // 遍历所有的歌曲span,找到点击所在的索引,保存到num中 //同时:如果选中了歌曲,就关闭搜索展示页面 for(var i=0;i<this.span.length;i++){ this.span[i].index = i; this.span[i].onclick = function (){ this.num = that.span[this.index].index that.reMove(); that.display3(this.num); // console.log(this.num); } } }
关闭展示
点击关闭按钮后,或者选择完成歌曲后,我们需要关闭展示页面
1.思路:
点击关闭很简单实现,选择关闭需要我们调用点击关闭的子方法
2.代码实现:
// 关闭按钮方法 ===> 点击关闭按钮(disPageClose) 执行关闭搜索页面 disClose(){ var that = this; this.disPageClose.onclick = function(){ that.reMove(); } } //移除方法 ====> 执行该函数,清空搜索界面所有内容,并隐藏 reMove(){ this.displayPage.style.display = "none"; this.infoBox.innerHTML = ""; }
展示界面如下:

改变播放器主页面信息
在选择歌曲后,需要根据选择的歌曲改变播放器的封面图 歌曲名 歌手名 以及要播放的歌曲
1.思路:
查看请求的数据我们知道(以搜索成都为例),根据数据的键名可以找到我们需要的信息
{ author: "赵雷" link: "http://music.163.com/#/song?id=436514312" lrc: "" pic: "http://p1.music.126.net/34YW1QtKxJ_3YnX9ZzKhzw==/2946691234868155.jpg?param=300x300" songid: 436514312 title: "成都" type: "netease" url: "http://music.163.com/song/media/outer/url?id=436514312.mp3" }
故而需要将数据写入播放器的html中
2.代码实现:
//播放器音乐信息展示方法 ===> 将ajax请求到的数据,写入到播放内容区(bfBox) display3(num){ var str = ""; for(var i=0;i<this.res.result.length;i++){ str = `<div class="imgBox"> <img src="${this.res.result[num].pic}" alt=""> </div> <div class="info"> <p>${this.res.result[num].title}</p> <p>${this.res.result[num].author}</p> <audio src="${this.res.result[num].url}" controls></audio> </div>` } this.bfBox.innerHTML = str; }
由于使用的是H5的audio标签,这时我们就完成了,播放器的基本功能。
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body{
/* 开启视距 */
perspective: 1400px;
position: relative;
}
/* 主页面 */
#box{
800px;
height:500px;
background-color: rgb(255, 255, 255);
margin: 80px auto;
/* transition: 2s; */
/* 开启3D空间 */
transform-style: preserve-3d;
display: flex;
justify-content: flex-start;
box-shadow: 0 15px 35px -5px rgba(50,88,130,.32);
}
/* 左侧版块 */
#box .leftBg{
190px;
height: 440px;
background-color: #222;
padding: 30px;
position: relative;
}
/* 中间板块 */
#box .centerBg{
500px;
height: 500px;
background-color: rgb(241, 241, 241);
}
/* 右侧版块 */
#box .rightBg{
100px;
height: 500px;
background-color: #222;
}
/* 左侧滚动信息 */
#box .leftBg marquee{
margin-top: 15px;
color: #fff;
font-size: 12px;
}
/* 左侧文字 ==> THIS IS MUSIC */
#box .leftBg p{
transform: translateZ(100px)
}
#box .leftBg .p1{
color: rgb(253, 0, 0);
font-size: 50px;
margin: 0;
padding-top:50px;
}
#box .leftBg .p2 {
color: rgb(253, 0, 0);
font-size: 75px;
font-weight: bold;
margin: 10px auto;
}
/* 左侧装饰条两根 */
#box .leftBg div{
100%;
height: 5px;
background-color: rgb(241, 241, 241);
position: absolute;
}
#box .leftBg div:nth-child(4){
left: 0;
bottom:60px;
}
#box .leftBg div:nth-child(5){
left: 0;
bottom:45px;
}
/* 搜索框样式 */
#box #search{
outline:none;
border: none;
250px;
height: 25px;
border-top-left-radius: 30px;
border-bottom-left-radius: 30px;
background-color: #fff;
margin-top: 20px;
margin-left: 50px;
padding: 5px 15px;
}
/* 搜索按钮样式 */
#box #searchSpan{
display: inline-block;
padding: 5px 15px;
25px;
height: 25px;
text-align: center;
line-height: 25px;
background-color: #222;
color: #fff;
font-size: 12px;
border-top-right-radius: 30px;
border-bottom-right-radius: 30px;
cursor: pointer;
}
/* 中间板块 播放器盒子 */
#box .centerBg #bfBox{
240px;
height: 330px;
border-radius: 10px;
background-color: #eef3f7;
margin-left:130px;
margin-top: 20px;
position: relative;
box-shadow: 0 15px 35px -5px rgba(50,88,130,.32);
display: flex;
align-items: flex-end;
padding: 30px;
}
/* 歌曲图片样式 */
#box .centerBg .imgBox{
160px;
height: 160px;
border-radius: 10px;
position: absolute;
top: 30px;
left: -30px;
box-shadow: 0 10px 20px 0 rgba(76,70,124,.5);
transform: translateZ(100px)
}
#box .centerBg .imgBox img{
160px;
height: 160px;
border-radius: 5px;
}
/* 歌曲信息样式 */
#box .centerBg #bfBox .info {
100%;
height: 45%;
}
#box .centerBg #bfBox .info p{
margin: 0;
color:#71829e;
}
#box .centerBg #bfBox .info p{
margin-top:15px;
font-size: 20px;
font-weight: bolder;
}
#box .centerBg #bfBox .info p:nth-of-type(2){
margin-top: 5px;
font-size: 16px;
}
/* audio播放器样式 */
#box .centerBg #bfBox .info audio{
100%;
padding-top: 10px;
outline:none;
}
/* 搜索展示界面样式 */
#display{
400px;
height: 500px;
background-color: rgb(245, 238, 238);
box-shadow: 0 10px 20px 0 rgba(76,70,124,.5);
display: none;
border-radius: 10px;
position: absolute;
top: 0;
left: 25%;
transform: translateZ(110px);
padding: 30px;
}
/* 搜索展示界面 标题====>(歌曲列表) */
#display h2{
text-align: center;
color: rgb(255, 0, 0);
}
#display p {
color: #607391;
font-size: 12px;
margin: 5px; 0;
padding:0 10px;
height: 35px;
line-height: 35px;
}
#display p:hover{
background-color: #fff;
}
#display p span {
margin-top: 5px;
float: right;
45px;
height: 25px;;
box-sizing: border-box;
border-radius: 5px;
background-color: rgb(55, 110, 214);
box-shadow: inset 1px 1px 5px 0px rgba(50,88,130,.32);
text-align: center;
line-height: 25px;
font-size: 12px;
color: #ccc;
cursor: pointer;
}
/* 展示版块关闭按钮样式 */
#display .close{
position: relative;
}
#display .close span{
30px;
height: 30px;
border-radius: 50%;
position: absolute;
top: 10px;
right: 10px;
background-color: lightblue;
box-shadow: inset 1px 1px 5px 0px rgba(50,88,130,.32);
text-align: center;
line-height: 30px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="box">
<!-- 搜索信息展示版块 ==>默认display:none -->
<div id="display">
<div class="close"><span>X</span></div>
<h2>歌曲列表</h2>
<div id="infoBox">
<!-- <p>歌曲名-Redbone <span>听这首</span></p> -->
</div>
</div>
<!-- 左侧版块 -->
<div class="leftBg">
<!-- 滚动信息 -->
<marquee behavior="scroll" direction="left">Current version of Baidu Music Interface</marquee>
<!-- 文字 ==> THIS IS MUSIC -->
<p class="p1">THIS <br>IS </p>
<p class="p2">MUSIC</p>
<!-- 白色装饰条 -->
<div></div>
<div></div>
</div>
<!-- 中间板块 -->
<div class="centerBg">
<!-- 搜索框+搜索按钮 -->
<input type="text" value="" id="search"><span id="searchSpan">搜索</span>
<div id="bfBox">
<!-- 歌曲图片 -->
<div class="imgBox">
<img src="http://p2.music.126.net/5i5SKVW_F1ub2BgDeyjI5A==/3225967119049341.jpg?param=300x300" alt="">
</div>
<!-- 歌曲信息 -->
<div class="info">
<p>Come and Get Your Love</p>
<p>Redbone</p>
<!-- 播放器 -->
<audio src="http://music.163.com/song/media/outer/url?id=28864241.mp3" controls></audio>
</div>
</div>
</div>
<!-- 右侧板块 ==> 未开发 -->
<div class="rightBg">
</div>
</div>
</body>
<!-- <script src="./js/ajax.js"></script> -->
<script>
class Music{
constructor(ele){
this.ele = ele;
this.box = document.getElementById("box");
this.text = document.getElementById("search");
this.bfBox = document.getElementById("bfBox");
this.url = "https://api.apiopen.top/searchMusic";
this.infoBox = document.getElementById("infoBox");
this.displayPage = document.getElementById("display");
this.disPageClose = document.querySelector("#display .close span");
this.searchBtn = document.getElementById("searchSpan");
this.addEvent();
}
// 绑定事件函数
addEvent(){
var that = this;
// 鼠标在body上移动,将坐标转换成旋转角度,形成视差
document.onmousemove = function (eve){
var e = eve || window.event;
that.x = (e.clientX-that.ele.offsetWidth/2)*0.01;
that.y = -(e.clientY-that.ele.offsetTop/2)*0.01;;
that.display();
}
//搜索框发生改变,生成搜索内容
this.searchBtn.onclick = function(){
var str = "";
that.url = "https://api.apiopen.top/searchMusic";
str = "?name="+that.text.value;
that.url += str;
that.load();
}
}
// ajax请求数据
load(){
var that = this;
ajax({
url:this.url,
success:function(res){
that.res = JSON.parse(res);
console.log(that.res.result);
that.display2();
}
})
}
//形成视差
display(){
this.box.style.transform = "rotateY(" + this.x + "deg) rotateX(" + this.y + "deg)";
}
//生成搜索内容
//请求完成,获得数据,将数据展示到搜索页面上
display2(){
this.displayPage.style.display = "block"
var str = "";
// 如果搜索为空,显示搜索内容为空
//否则,展示搜索内容
if(this.res.result.length == 0){
str = "<h3>搜索内容为空</h3>"
}else{
for(var i=0;i<this.res.result.length;i++){
str+=`<p>${this.res.result[i].title}-${this.res.result[i].author} <span>听这首</span></p>`
}
}
this.infoBox.innerHTML = str;
this.change();
this.disClose();
}
//点击选中的歌曲,改变播放器页面的信息
change(){
var that = this;
this.span = document.querySelectorAll("#infoBox p span");
console.log(this.span);
// 遍历所有的歌曲span,找到点击所在的索引,保存到num中
//同时:如果选中了歌曲,就关闭搜索展示页面
for(var i=0;i<this.span.length;i++){
this.span[i].index = i;
this.span[i].onclick = function (){
this.num = that.span[this.index].index
that.reMove();
that.display3(this.num);
// console.log(this.num);
}
}
}
// 关闭按钮方法 ===> 点击关闭按钮(disPageClose) 执行关闭搜索页面
disClose(){
var that = this;
this.disPageClose.onclick = function(){
that.reMove();
}
}
//移除方法 ====> 执行该函数,清空搜索界面所有内容,并隐藏
reMove(){
this.displayPage.style.display = "none";
this.infoBox.innerHTML = "";
}
//播放器音乐信息展示方法 ===> 将ajax请求到的数据,写入到播放内容区(bfBox)
display3(num){
var str = "";
for(var i=0;i<this.res.result.length;i++){
str = `<div class="imgBox">
<img src="${this.res.result[num].pic}" alt="">
</div>
<div class="info">
<p>${this.res.result[num].title}</p>
<p>${this.res.result[num].author}</p>
<audio src="${this.res.result[num].url}" controls></audio>
</div>`
}
this.bfBox.innerHTML = str;
}
}
//ajax封装函数
function ajax(options) {
var { type, url, success, error, data, timeout } = options;
type = type || "get";
data = data || {};
timeout = timeout || 2000;
var str = "";
for (var i in data) {
str += `${i}=${data[i]}&`;
}
if (type == "get") {
var d = new Date();
url = url + "?" + str + "__qft=" + d.getTime();
}
var xhr = new XMLHttpRequest();
xhr.open(type, url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
success && success(xhr.responseText);
error = null;
} else if (xhr.readyState == 4 && xhr.status != 200) {
error && error(xhr.status);
success = null;
error = null;
}
}
setTimeout(() => {
error && error("timeout");
success = null;
}, timeout);
if (type == "post") {
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(str)
} else {
xhr.send()
}
}
var body = document.body;
new Music(body);
</script>
</html>
