今天我们一起来看用vue编写的h5端周杰伦音乐播放器
我是听着jay的歌,看的这个项目哦
先放上作者大大的地址
https://github.com/osuuu/jay_music
接下来我们看看这个项目里面有哪些效果
接下来我们看看代码
首先是main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import './assets/js/remScale' //适配尺寸
import './assets/css/reset.css'
//引入Vant-ui
import Vant from 'vant'
import 'vant/lib/index.css'
Vue.use(Vant)
import axios from 'axios'
Vue.prototype.$axios = axios
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
接下来入口文件app.vue
<template>
<div id="app">
<router-view v-wechat-title="$route.meta.title" />
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>
router.js中定义了对应的组件
import Vue from 'vue'
import Router from 'vue-router'
import Index from '@/components/Index'
import Album from '@/components/Album'
import Seach from '@/components/Seach'
import VueWechatTitle from 'vue-wechat-title'
Vue.use(VueWechatTitle)
Vue.use(Router)
export default new Router({
// mode:'history',
routes: [
{
path: '/',
name: 'Index',
component: Index,
meta: {
title: '首页 - JAY粉丝俱乐部'
}
},
{
path: '/album',
name: 'Album',
component: Album,
meta: {
title: '专辑 - JAY粉丝俱乐部'
}
},
{
path: '/seach',
name: 'Seach',
component: Seach,
meta: {
title: '搜索 - JAY粉丝俱乐部'
}
},
{
path: '*',
redirect: '/'
}
]
})
// JavaScript Document
(function (doc, win) {
var docEl = doc.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function () {
var clientWidth = docEl.clientWidth; //获取设备尺寸
if (!clientWidth) return;
if(clientWidth>=750){ //设计稿宽度
docEl.style.fontSize = '100px';
}else{
docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
}
};
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false); //绑定事件
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
这个是对应的单位的转换的
接下来我们看首页
首页中有轮播,列表,还有一个搜索以及刷新的功能
比较干净的请求
<template>
<div>
<div class="car">
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
<van-swipe-item v-for="(item,index) in imgs" :key="index">
<a :href="item.url?item.url:'#'" >
<img :src="item.img" class="imgs" alt />
</a>
</van-swipe-item>
</van-swipe>
</div>
<div style="margin-bottom:60px">
<div class="contain">
<div class="title">
随机推荐
<van-icon
name="replay"
size="20"
style="float:right;margin-top:4px"
color="#777"
@click="replay"
/>
</div>
<div class="song" v-for="(item,index) in song" :key="index" @click="play(item.id)">
<img :src="item.images" class="song_img" alt />
<div class="song_name">{{item.name}}</div>
<div class="sq">{{item.sound}}</div>
<div class="song_detail">{{item.details}}</div>
<van-icon name="arrow" class="song_enter" size="26" color="#777" />
</div>
</div>
<div class="contain">
<div class="title">
热门专辑
<van-icon name="replay" size="20" style="float:right" color="#777" @click="reAlbum" />
</div>
<div class="album_list">
<div
class="album_one"
v-for="(item,index) in album"
:key="index"
@click="Album(item.id,item.images,item.album)"
>
<img :src="item.images" class="album_img" alt />
<div class="album_name">{{item.album}}</div>
</div>
</div>
</div>
</div>
<div class="nav1" @click="seach">
<van-icon name="search" size="40" color="#fff" />
</div>
<play :song="play_id"></play>
</div>
</template>
<script>
import { Toast } from "vant";
import play from "@/components/play";
export default {
data() {
return {
imgs: [],
song: [],
album: [],
play_id: ""
};
},
components: {
play
},
methods: {
//获取轮播图
_getImgs() {
this.$axios({
url: "https://api.h234.cn/music/swiper.php"
})
.then(res => {
this.imgs = res.data.data;
})
.catch(err => {});
},
//获取随机推荐
_getSong() {
this.$axios({
url: "https://api.h234.cn/music/jay.html"
})
.then(res => {
this.song = res.data.data;
})
.catch(err => {});
},
//获取专辑列表
_getAlbum() {
this.$axios({
url: "https://api.h234.cn/music/jay.html?list=all"
})
.then(res => {
var newAlbum = [];
for (let i = 0; i < res.data.data.length; ) {
let j = Math.random() * res.data.data.length; // 获取随机的下标值
j = ~~j; // 取整
let item = res.data.data.splice(j, 1)[0]; // 从原数组中剔除选中的元素
newAlbum.push(item);
}
this.album = newAlbum.splice(0, 6);
})
.catch(err => {});
},
//传值播放
play(id) {
this.play_id = id;
Toast.loading({
message: "加载中...",
forbidClick: true
});
},
//刷新列表
replay() {
Toast.loading({
message: "加载中...",
forbidClick: true
});
this._getSong();
},
//刷新专辑
reAlbum() {
Toast.loading({
message: "加载中...",
forbidClick: true
});
this._getAlbum();
},
//跳转专辑列表
Album(id, img, name) {
this.$router.push("/album?id=" + id + "&img=" + img + "&name=" + name);
},
//搜索效果
seach() {
this.$router.push("/seach");
}
},
mounted() {
this._getImgs();
this._getSong();
this._getAlbum();
}
};
</script>
<style scoped>
.my-swipe .van-swipe-item {
color: #fff;
font-size: 20px;
border-radius: 10px;
text-align: center;
}
.car {
height: 200px;
padding: 10px;
}
.imgs {
100%;
height: 200px;
border-radius: 10px;
}
.contain {
background: #fff;
padding: 20px 15px 10px 15px;
}
.title {
font-size: 20px;
font-weight: 600;
display: block;
margin-bottom: 10px;
}
.song {
margin: 10px 0;
position: relative;
}
.song_img {
60px;
height: 60px;
border-radius: 10px;
margin-right: 10px;
}
.song_name {
80%;
position: absolute;
top: 5px;
left: 70px;
font-size: 18px;
color: black;
white-space: nowrap; /* 一行 */
overflow: hidden; /* 溢出隐藏 */
text-overflow: ellipsis; /* 超出内容显示省略号 */
}
.sq {
position: absolute;
bottom: 10px;
left: 70px;
padding: 0 4px;
font-size: 11px;
border-radius: 4px;
border: 1px solid orange;
color: orange;
}
.song_detail {
position: absolute;
bottom: 10px;
left: 108px;
font-size: 12px;
color: #888;
}
.song_enter {
position: absolute;
top: 14px;
right: 0;
}
.album_list {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.album_one {
30%;
height: 130px;
margin-bottom: 20px;
}
.album_img {
100%;
height: 110px;
border-radius: 10px;
}
.album_name {
font-size: 18px;
color: #888;
white-space: nowrap; /* 一行 */
overflow: hidden; /* 溢出隐藏 */
text-overflow: ellipsis; /* 超出内容显示省略号 */
}
.nav1 {
position: fixed;
right: 30px;
bottom: 80px;
40px;
height: 40px;
border-radius: 50%;
text-align: center;
background: #5dade2;
box-shadow: 1px 1px 5px #888888;
padding: 5px;
}
.nav {
position: fixed;
right: 30px;
bottom: 80px;
50px;
height: 50px;
border-radius: 50%;
text-align: center;
background: #5dade2;
box-shadow: 1px 1px 5px #888888;
/* padding: 5px; */
}
.seach {
position: fixed;
bottom: 210px;
0;
margin: 0 5%;
background: #5dade2;
color: #fff;
border-radius: 30px;
box-shadow: 1px 1px 5px #888888;
transition: width 0.5s;
}
.shows {
90%;
}
.seach input {
/* 100%; */
height: 50px;
margin-left: 48px;
font-size: 17px;
}
.ico {
position: absolute;
top: 7px;
left: 10px;
}
.hide {
display: none;
}
</style>
里面还封装了一个播放组件
我们看看播放组件的内容
播放组件里面还有播放下一曲的功能
//play.vue
<template>
<div class="play">
<img :src="music.images" class="play_img" :class="plays?'animation':''" alt />
<div class="play_name">{{music.name}}</div>
<div class="play_detail">{{music.details}}</div>
<div class="play_ico" :class="plays?'playImg':'stopImg'" @click="stop"></div>
<div class="play_next" @click="next"></div>
<audio ref="audio">
<source :src="music.music" type="audio/mpeg" />
</audio>
</div>
</template>
<script>
import { Toast } from "vant";
export default {
props: ["song"],
data() {
return {
music: {
images: "http://q2.qlogo.cn/headimg_dl?dst_uin=1750754503&spec=100",
name: "听见好音乐",
details: "我偷看着这万物世态炎凉"
},
plays: false
};
},
methods: {
_getSong(id) {
this.$axios({
url: "https://api.h234.cn/music/jay.html?id=" + id
})
.then(res => {
this.music = res.data.data[0];
let audio = this.$refs.audio;
audio.load();
audio.play();
this.plays = true;
//监听播放结束 执行下一曲
audio.addEventListener(
"ended",
function() {
this.plays = false;
this.next()
},
false
);
})
.catch(err => {});
},
//播放与暂停按钮
stop() {
if (this.plays) {
let audio = this.$refs.audio;
audio.pause();
this.plays = false;
} else if (!this.music.music) {
Toast.fail("还没有选择歌曲哦");
} else {
let audio = this.$refs.audio;
audio.play();
this.plays = true;
}
},
//下一曲
next() {
let j = Math.random() * (191 - 30) + 30;
j = ~~j; // 取整
this._getSong(j);
}
},
watch: {
song(newVal) {
this._getSong(newVal);
}
}
};
</script>
<style scoped>
.play {
92%;
height: 60px;
padding: 0 4%;
position: fixed;
bottom: 0;
background: #fff;
box-shadow: 1px 1px 5px #888888;
}
.play_img {
46px;
height: 46px;
border-radius: 50%;
margin: 7px 0;
}
.animation {
-webkit-animation: circle 10s infinite linear;
animation: circle 10s infinite linear;
}
@-webkit-keyframes circle {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.play_name {
position: absolute;
top: 8px;
left: 80px;
font-size: 16px;
}
.play_detail {
position: absolute;
bottom: 8px;
left: 80px;
color: #777;
font-size: 14px;
}
.play_ico {
position: absolute;
right: 70px;
top: 15px;
30px;
height: 30px;
}
.playImg {
background: url(../assets/img/play.png);
background-size: 30px 30px;
}
.stopImg {
background: url(../assets/img/stop.png);
background-size: 30px 30px;
}
.play_next {
position: absolute;
right: 20px;
top: 13px;
34px;
height: 34px;
background: url(../assets/img/next.png);
background-size: 34px 34px;
}
</style>
里面还有Album组件,不过可能还没有做这个
//Alum
**<template>
<div>
<div style="margin-bottom:60px">
<div class="album_head">
<div class="bg">
<img :src="album_img" class="imgs" alt />
<div class="album_name">{{album_name}}</div>
<div class="album_auth">周杰伦</div>
<div class="album_details">未知信息 ></div>
</div>
<div class="mask"></div>
</div>
<div class="song_list" v-for="(item,index) in song" :key="index" @click="play(item.id)">
<div class="song_name">{{item.name}}</div>
<div class="song_sq">{{item.sound}}</div>
<div class="song_detail">{{item.details}}</div>
<van-icon name="arrow" class="song_enter" size="26" color="#777" />
</div>
</div>
<play :song="play_id"></play>
</div>
</template>
<script>
import { Toast } from "vant";
import play from "@/components/play";
export default {
data() {
return {
album_img: "",
album_name: "",
song: [],
play_id: ""
};
},
components: {
play
},
mounted() {
this.album_img = this.$route.query.img;
this.album_name = this.$route.query.name;
this._getSong(this.$route.query.id);
},
methods: {
//获取专辑音乐
_getSong(value) {
this.$axios({
url: "https://api.h234.cn/music/jay.html?album=" + value
})
.then(res => {
this.song = res.data.data;
})
.catch(err => {});
},
play(id) {
Toast.loading({
message: "加载中...",
forbidClick: true
});
this.play_id = id;
}
}
};
</script>
<style scoped>
.album_head {
position: relative;
height: 200px;
}
.bg {
position: absolute;
top: 0;
100%;
background: linear-gradient(#3b5870, #696568);
}
.imgs {
margin: 30px 30px 40px 30px;
120px;
height: 120px;
border-radius: 10px;
}
.album_name {
position: absolute;
top: 30px;
left: 180px;
color: #fff;
font-size: 20px;
font-weight: 600;
}
.album_details {
position: absolute;
top: 120px;
left: 180px;
color: #fff;
font-size: 14px;
}
.album_auth {
position: absolute;
top: 70px;
left: 180px;
color: #fff;
font-size: 14px;
}
.mask {
position: absolute;
top: 176px;
100%;
height: 40px;
background: #fff;
border-radius: 20px;
}
.song_list {
margin: 10px 15px;
position: relative;
}
.song_name {
80%;
font-size: 18px;
color: black;
white-space: nowrap; /* 一行 */
overflow: hidden; /* 溢出隐藏 */
text-overflow: ellipsis; /* 超出内容显示省略号 */
margin-bottom: 5px;
}
.song_sq {
display: inline-block;
padding: 0 4px;
font-size: 10px;
border-radius: 4px;
border: 1px solid orange;
color: orange;
}
.song_detail {
display: inline-block;
margin-left: 16px;
font-size: 14px;
color: #888;
}
.song_enter {
position: absolute;
top: 14px;
right: 0;
}
</style>
接下来是search页面的
接下来看代码
<template>
<div>
<div class="song_seach">
<van-icon name="search" class="ico" size="30" color="#777" />
<input
placeholder="歌曲名(只能周总的哦)"
v-model="seach"
@keyup.enter="startSeach"
/>
</div>
<div style="margin-bottom:80px">
<div class="song_list" v-for="(item,index) in song" :key="index" @click="play(item.id)">
<div class="song_name">{{item.name}}</div>
<div class="song_sq">{{item.sound}}</div>
<div class="song_detail">{{item.details}}</div>
<van-icon name="arrow" class="song_enter" size="26" color="#777" />
</div>
</div>
<play :song="play_id"></play>
</div>
</template>
<script>
import { Toast } from "vant";
import play from "@/components/play";
export default {
data() {
return {
seach: "",
play_id: "",
song: []
};
},
components: {
play
},
methods: {
_getSong(value) {
this.$axios({
url: "https://api.h234.cn/music/jay.html?query=" + this.seach
})
.then(res => {
this.song = res.data.data;
})
.catch(err => {});
},
//搜索音乐
startSeach() {
this._getSong(this.seach);
this.seach = ""
},
play(id) {
Toast.loading({
message: "加载中...",
forbidClick: true
});
this.play_id = id;
}
}
};
</script>
<style scoped>
.song_seach {
position: relative;
margin: 0 3%;
background: #f5f5f5;
border-radius: 36px;
margin-top: 10px;
}
.ico {
position: absolute;
top: 5px;
left: 10px;
}
.song_seach input {
50%;
height: 40px;
margin-left: 48px;
font-size: 17px;
color: #777;
background: #f5f5f5;
}
.song_list {
margin: 10px 15px;
position: relative;
}
.song_name {
80%;
font-size: 18px;
color: black;
white-space: nowrap; /* 一行 */
overflow: hidden; /* 溢出隐藏 */
text-overflow: ellipsis; /* 超出内容显示省略号 */
margin-bottom: 5px;
}
.song_sq {
display: inline-block;
padding: 0 4px;
font-size: 10px;
border-radius: 4px;
border: 1px solid orange;
color: orange;
}
.song_detail {
display: inline-block;
margin-left: 16px;
font-size: 14px;
color: #888;
}
.song_enter {
position: absolute;
top: 14px;
right: 0;
}
</style>
这个项目的功能虽然不太多,也很简单,不过很完整,没有多余的代码,感觉看着很优雅
也许是因为在单曲Jay的歌曲呢~~
后记:听着Jay的歌曲,看着功能的实现,觉得很幸福