zoukankan      html  css  js  c++  java
  • 【生活美好】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的歌曲,看着功能的实现,觉得很幸福

  • 相关阅读:
    装饰器
    内置函数
    文件操作
    函数
    数据结构[总结笔记]
    汉诺塔解题思路
    springboot事物
    mysql5.7.29 zip包安装教程
    mysql常用语句【转载】
    springboot+mysql+jpa+sharding-jdbc+druid读写分离
  • 原文地址:https://www.cnblogs.com/smart-girl/p/12935960.html
Copyright © 2011-2022 走看看