【生活美好】jay_music

今天我们一起来看用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的歌曲,看着功能的实现,觉得很幸福

原文地址:https://www.cnblogs.com/smart-girl/p/12935960.html