20230621----重返学习-仿QQ音乐播放器-静态页面的免费部署-vue2

day-096-ninety-six-20230621-仿QQ音乐播放器-静态页面的免费部署-vue2

仿QQ音乐播放器

audio音频标签

  • audio标签

    <audio src="images/myDream.m4a" id="audioInp" controls autoplay loop preload="metadata" />
    
  • 标签的属性

    • controls:显示浏览器自带的播放组件「表现形式不一样」
    • autoplay:自动播放「当代浏览器默认是禁用了自动播放的功能的」
    • loop:循环播放
    • muted:静音
    • preload:资源加载的方式
      • none:只有开始播放的时候,才会去加载音频资源(页面加载的时候不会去获取音频资源)
      • metadata:页面加载的时候,首先获取音频的一些基本信息
      • auto:页面加载的时候,同时加载音频资源
    • src:音频的地址
      • 各浏览器对不用音频格式的支持是不一样的

      • 可以在audio标签用内部使用source来指定可以使用的音频源。

        <audio src="images/myDream.m4a" id="audioInp" controls autoplay loop preload="metadata" />
        
        <audio controls>
          <source src="images/myDream.mp3" type="audio/mpeg" />
          <source src="images/myDream.ogg" type="audio/ogg" />
          <source src="images/myDream.wav" type="audio/wav" />
          您的浏览器不支持Audio!
        </audio>
        
  • audio标签对应的DOM元素对象的常见属性

    let audioInp = document.querySelector("#audioInp");
    console.dir(audioInp)
    
    • currentTime:存储了当前播放的时间「单位秒」
    • duration:存储了总的时间
    • ended:true/false 是否播放完毕
    • paused:true/false 当前是否为暂停的
    • volume:0~1 控制音量的 1最大音量 0静音
    • play方法:控制音频播放
    • pause方法:控制播放暂停
    • canplay事件:音频可以播放了
    • canplaythrough事件:也是音频可以播放了(加载很多资源后,才会触发,保证音频播放中不卡顿)
    • pause事件
    • play事件
    • playing事件
    • volumechange事件
<!DOCTYPE html>
<html>
  <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>audio音频处理</title>
  </head>

  <body>
    <!-- 
        标签的属性
          controls:显示浏览器自带的播放组件「表现形式不一样」
          autoplay:自动播放「当代浏览器默认是禁用了自动播放的功能的」
          loop:循环播放
          muted:静音
          preload:资源加载的方式
            none:只有开始播放的时候,才会去加载音频资源(页面加载的时候不会去获取音频资源)
            metadata:页面加载的时候,首先获取音频的一些基本信息
            auto:页面加载的时候,同时加载音频资源
          src:音频的地址 
            各浏览器对不用音频格式的支持是不一样的
     -->
    <audio
      src="https://zxt_team.gitee.io/opensource/qqmusic/mydream.m4a"
      id="audioInp"
      controls
      autoplay
      loop
      preload="metadata"
    />
    <!-- <audio controls>
      <source src="images/myDream.mp3" type="audio/mpeg" />
      <source src="images/myDream.ogg" type="audio/ogg" />
      <source src="images/myDream.wav" type="audio/wav" />
      您的浏览器不支持Audio!
    </audio> -->

    <script>
      let audioInp = document.querySelector("#audioInp");
      /*
         currentTime:存储了当前播放的时间「单位秒」
         duration:存储了总的时间
         ended:true/false 是否播放完毕
         paused:true/false 当前是否为暂停的
         volume:0~1 控制音量的  1最大音量 0静音
         play方法:控制音频播放
         pause方法:控制播放暂停
         canplay事件:音频可以播放了
         canplaythrough事件:也是音频可以播放了(加载很多资源后,才会触发,保证音频播放中不卡顿)
         pause事件
         play事件
         playing事件
         volumechange事件
        */
    </script>
  </body>
</html>

延期信ajax取数据

const obj = '要返回的数据';

const API = {
  queryLyric() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(obj);
      }, Math.round(Math.random() * (2000 - 500) + 500));
    });
  },
};
const obj = {
  code: 0,
  message: "ok",
  data: {
    title: "我的梦 - 华为手机主题曲",
    author: "张靓颖",
    duration: "03:39",
    pic: "https://zxt_team.gitee.io/opensource/qqmusic/mydream.jpg",
    audio: "https://zxt_team.gitee.io/opensource/qqmusic/mydream.m4a",
    lyric:
      "[ti&#58;《我的梦》]&#10;[ar&#58;张靓颖]&#10;[al&#58;]&#10;[by&#58;]&#10;[offset&#58;0]&#10;[00&#58;01&#46;36]我的梦&#32;&#40;华为手机主题曲&#41;&#32;&#45;&#32;张靓颖&#10;[00&#58;02&#46;11]词:王海涛/张靓颖&#10;[00&#58;02&#46;64]曲:Andy&#32;Love&#10;[00&#58;03&#46;48]编曲:崔迪&#10;[00&#58;04&#46;49]&#10;[00&#58;08&#46;73]一直地一直地往前走&#10;[00&#58;11&#46;65]&#10;[00&#58;13&#46;02]疯狂的世界&#10;[00&#58;14&#46;58]&#10;[00&#58;16&#46;68]迎着痛把眼中所有梦&#10;[00&#58;20&#46;52]&#10;[00&#58;21&#46;03]都交给时间&#10;[00&#58;22&#46;71]&#10;[00&#58;24&#46;24]想飞就用心地去飞&#10;[00&#58;26&#46;98]谁不经历狼狈&#10;[00&#58;30&#46;68]&#10;[00&#58;31&#46;60]我想我会忽略失望的灰&#10;[00&#58;34&#46;99]拥抱遗憾的美&#10;[00&#58;39&#46;05]我的梦说别停留等待&#10;[00&#58;43&#46;94]就让光芒折射泪湿的瞳孔&#10;[00&#58;47&#46;74]映出心中最想拥有的彩虹&#10;[00&#58;51&#46;78]带我奔向那片有你的天空&#10;[00&#58;55&#46;74]因为你是我的梦&#10;[01&#58;01&#46;06]&#10;[01&#58;07&#46;19]我的梦&#10;[01&#58;08&#46;72]&#10;[01&#58;16&#46;75]执着地勇敢地不回头&#10;[01&#58;20&#46;29]&#10;[01&#58;21&#46;05]穿过了黑夜踏过了边界&#10;[01&#58;24&#46;87]路过雨路过风往前冲&#10;[01&#58;28&#46;39]&#10;[01&#58;28&#46;96]总会有一天站在你身边&#10;[01&#58;32&#46;52]泪就让它往下坠&#10;[01&#58;35&#46;00]溅起伤口的美&#10;[01&#58;38&#46;60]&#10;[01&#58;39&#46;55]哦别以为失去的最宝贵&#10;[01&#58;43&#46;00]才让今天浪费&#10;[01&#58;47&#46;04]我的梦说别停留等待&#10;[01&#58;51&#46;93]就让光芒折射泪湿的瞳孔&#10;[01&#58;55&#46;66]映出心中最想拥有的彩虹&#10;[01&#58;59&#46;75]带我奔向那片有你的天空&#10;[02&#58;03&#46;67]因为你是我的梦&#10;[02&#58;09&#46;14]&#10;[02&#58;11&#46;72]我的梦&#10;[02&#58;13&#46;09]&#10;[02&#58;15&#46;13]我的梦&#10;[02&#58;16&#46;64]&#10;[02&#58;19&#46;60]我的梦&#10;[02&#58;21&#46;39]&#10;[02&#58;24&#46;27]世界会怎么变化&#10;[02&#58;26&#46;58]都不是意外&#10;[02&#58;28&#46;33]记得用心去回答&#10;[02&#58;30&#46;52]命运的精彩&#10;[02&#58;32&#46;34]世界会怎么变化&#10;[02&#58;34&#46;51]都离不开爱&#10;[02&#58;36&#46;25]记得成长的对话&#10;[02&#58;38&#46;28]&#10;[02&#58;39&#46;11]勇敢地说我不再等待&#10;[02&#58;45&#46;63]就让光芒折射泪湿的瞳孔&#10;[02&#58;49&#46;75]映出心中最想拥有的彩虹&#10;[02&#58;53&#46;74]带我奔向那片有你的天空&#10;[02&#58;57&#46;73]因为你是我的梦&#10;[03&#58;02&#46;71]&#10;[03&#58;05&#46;51]我的梦&#10;[03&#58;07&#46;32]&#10;[03&#58;09&#46;20]我的梦&#10;[03&#58;14&#46;12]因为你是我的梦",
  },
};

const API = {
  queryLyric() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(obj);
      }, Math.round(Math.random() * (2000 - 500) + 500));
    });
  },
};

对歌词进行处理

let lyric = "[ti&#58;《我的梦》]&#10;[ar&#58;张靓颖]&#10;[al&#58;]&#10;[by&#58;]&#10;[offset&#58;0]&#10;[00&#58;01&#46;36]我的梦&#32;&#40;华为手机主题曲&#41;&#32;&#45;&#32;张靓颖&#10;[00&#58;02&#46;11]词:王海涛/张靓颖&#10;[00&#58;02&#46;64]曲:Andy&#32;Love&#10;[00&#58;03&#46;48]编曲:崔迪&#10;[00&#58;04&#46;49]&#10;[00&#58;08&#46;73]一直地一直地往前走&#10;[00&#58;11&#46;65]&#10;[00&#58;13&#46;02]疯狂的世界&#10;[00&#58;14&#46;58]&#10;[00&#58;16&#46;68]迎着痛把眼中所有梦&#10;[00&#58;20&#46;52]&#10;[00&#58;21&#46;03]都交给时间&#10;[00&#58;22&#46;71]&#10;[00&#58;24&#46;24]想飞就用心地去飞&#10;[00&#58;26&#46;98]谁不经历狼狈&#10;[00&#58;30&#46;68]&#10;[00&#58;31&#46;60]我想我会忽略失望的灰&#10;[00&#58;34&#46;99]拥抱遗憾的美&#10;[00&#58;39&#46;05]我的梦说别停留等待&#10;[00&#58;43&#46;94]就让光芒折射泪湿的瞳孔&#10;[00&#58;47&#46;74]映出心中最想拥有的彩虹&#10;[00&#58;51&#46;78]带我奔向那片有你的天空&#10;[00&#58;55&#46;74]因为你是我的梦&#10;[01&#58;01&#46;06]&#10;[01&#58;07&#46;19]我的梦&#10;[01&#58;08&#46;72]&#10;[01&#58;16&#46;75]执着地勇敢地不回头&#10;[01&#58;20&#46;29]&#10;[01&#58;21&#46;05]穿过了黑夜踏过了边界&#10;[01&#58;24&#46;87]路过雨路过风往前冲&#10;[01&#58;28&#46;39]&#10;[01&#58;28&#46;96]总会有一天站在你身边&#10;[01&#58;32&#46;52]泪就让它往下坠&#10;[01&#58;35&#46;00]溅起伤口的美&#10;[01&#58;38&#46;60]&#10;[01&#58;39&#46;55]哦别以为失去的最宝贵&#10;[01&#58;43&#46;00]才让今天浪费&#10;[01&#58;47&#46;04]我的梦说别停留等待&#10;[01&#58;51&#46;93]就让光芒折射泪湿的瞳孔&#10;[01&#58;55&#46;66]映出心中最想拥有的彩虹&#10;[01&#58;59&#46;75]带我奔向那片有你的天空&#10;[02&#58;03&#46;67]因为你是我的梦&#10;[02&#58;09&#46;14]&#10;[02&#58;11&#46;72]我的梦&#10;[02&#58;13&#46;09]&#10;[02&#58;15&#46;13]我的梦&#10;[02&#58;16&#46;64]&#10;[02&#58;19&#46;60]我的梦&#10;[02&#58;21&#46;39]&#10;[02&#58;24&#46;27]世界会怎么变化&#10;[02&#58;26&#46;58]都不是意外&#10;[02&#58;28&#46;33]记得用心去回答&#10;[02&#58;30&#46;52]命运的精彩&#10;[02&#58;32&#46;34]世界会怎么变化&#10;[02&#58;34&#46;51]都离不开爱&#10;[02&#58;36&#46;25]记得成长的对话&#10;[02&#58;38&#46;28]&#10;[02&#58;39&#46;11]勇敢地说我不再等待&#10;[02&#58;45&#46;63]就让光芒折射泪湿的瞳孔&#10;[02&#58;49&#46;75]映出心中最想拥有的彩虹&#10;[02&#58;53&#46;74]带我奔向那片有你的天空&#10;[02&#58;57&#46;73]因为你是我的梦&#10;[03&#58;02&#46;71]&#10;[03&#58;05&#46;51]我的梦&#10;[03&#58;07&#46;32]&#10;[03&#58;09&#46;20]我的梦&#10;[03&#58;14&#46;12]因为你是我的梦"

// 1. 歌词解析:
// [ti&#58;《我的梦》]&#10;
// [ar&#58;张靓颖]&#10;
// [al&#58;]&#10;
// [by&#58;]&#10;
// [offset&#58;0]&#10;
// [00&#58;01&#46;36]我的梦&#32;&#40;华为手机主题曲&#41;&#32;&#45;&#32;张靓颖&#10;
// [00&#58;02&#46;11]词:王海涛/张靓颖&#10;
// [00&#58;02&#46;64]曲:Andy&#32;Love&#10;
// [00&#58;03&#46;48]编曲:崔迪&#10;
// [00&#58;04&#46;49]&#10;
// [00&#58;08&#46;73]一直地一直地往前走&#10;
// ...
// [03&#58;14&#46;12]因为你是我的梦

// 2. 歌词内容推测:
// &#32; ->空格
// &#40; ->(
// &#41; ->)
// &#45; ->-
// &#10; ->换行符
// &#58; ->前面的数字是分钟
// &#46; ->前面的数字是秒

// 3. 得到歌词的内容格式。
// 一行歌词:[分钟数&#58;秒数&#46;毫秒数]内部有特殊字符的歌词内容&#10;
// 内部有特殊字符的歌词内容:`&#32; 为 空格` `&#40; 为 (` `&#41; 为 )` `&#45; 为 -` 。
/* lyric.replace(
  /\[(\d+)&#58;(\d+)&#46;(?:\d+)\]([^&#?]+)(?:&#10;)?/g,
  (_, $1, $2, $3) => {
    console.log($1, $2, $3);
  }
); */
    // 处理歌词部分的特殊符号
    lyric = lyric.replace(/&#(\d+);/g, (value, $1) => {
      let instead = value;
      switch (+$1) {
        case 32:
          instead = " ";
          break;
        case 40:
          instead = "(";
          break;
        case 41:
          instead = ")";
          break;
        case 45:
          instead = "-";
          break;
        default:
      }
      return instead;
    });
    // 解析歌词信息
    let arr = [];
    lyric.replace(
      /\[(\d+)&#58;(\d+)&#46;(?:\d+)\]([^&#?]+)(?:&#10;)?/g,
      (_, $1, $2, $3) => {
        arr.push({
          minutes: $1,
          seconds: $2,
          text: $3,
        });
      }
    );
    console.log(arr,lyric)

源码示例

  • JS高级进阶/day0619_QQMusic/index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <meta
          name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
        />
        <title>QQ音乐播放器</title>
        <!-- IMPORT CSS -->
        <!-- <link rel="stylesheet" href="css/reset.min.css" /> -->
        <link rel="stylesheet" href="css/index.css" />
        <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js" async></script>
      </head>
    
      <body>
        <div id="app">
          <!-- 头部 -->
          <header class="header-box">
            <div class="base">
              <!-- <div class="cover">
                <img src="" alt="" />
              </div>
              <div class="info">
                <h2 class="title">我的梦 - 华为手机主题曲</h2>
                <h3 class="author">张靓颖</h3>
              </div> -->
            </div>
            <a href="javascript:;" class="player-button"></a>
          </header>
    
          <!-- 歌词 -->
          <main class="main-box">
            <div class="wrapper">
              <!-- <p class="active">我的梦 (华为手机主题曲) - 张靓颖</p>
              <p>词:王海涛/张靓颖</p>
              <p>曲:Andy Love</p> -->
            </div>
          </main>
    
          <!-- 尾部 -->
          <footer class="footer-box">
            <div class="bar">
              <span class="time current">00:00</span>
              <div class="progress">
                <div class="already"></div>
              </div>
              <span class="time duration">00:00</span>
            </div>
            <a href="#" class="download">下载这首音乐</a>
          </footer>
    
          <!-- 其它 -->
          <div class="mark-image"></div>
          <div class="mark-overlay"></div>
          <div class="loading-box">
            <div class="content">
              <img src="images/loading.gif" alt="" />
              <span>奴家正在努力加载中...</span>
            </div>
          </div>
          <audio src="" id="audioBox" preload="metadata"></audio>
        </div>
    
        <!-- IMPORT JS -->
        <script>
          (function () {
            const app = document.querySelector("#app"),
              HTML = document.documentElement;
            const computed = function computed() {
              let orien = Math.abs(window.orientation);
              if (orien === 90) {
                // 切换到横屏
                alert("横屏体验不佳,请您切换到竖屏");
                app.style.display = "none";
                return;
              }
              // 当前是竖屏
              app.style.display = "block";
    
              // 计算REM的换算比例
              let deviceW = HTML.clientWidth,
                designW = 375,
                initialRatio = 100;
              if (deviceW > 540) deviceW = 540;
              let currentRatio = (deviceW / designW) * initialRatio;
              HTML.style.fontSize = currentRatio + "px";
            };
            computed();
            window.addEventListener("resize", computed);
            // 基于 resize 代替 orientationchange 事件:这样可以适配模拟器中,设备的切换场景
          })();
        </script>
        <script src="js/api.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js"></script>
        <script src="js/index.js"></script>
      </body>
    </html>
    
  • JS高级进阶/day0619_QQMusic/css/index.css

    • 由less文件编译而来

      html {
        font-size: 100px;
      }
      html,
      body,
      #app {
        height: 100%;
        overflow: hidden;
      }
      #app {
        position: relative;
        margin: 0 auto;
        max-width: 540px;
        font-size: 0.14rem;
      }
      .header-box,
      .footer-box,
      .main-box {
        box-sizing: border-box;
        height: 1rem;
        overflow: hidden;
      }
      .main-box {
        height: calc(100vh - 2rem);
      }
      /* 通用样式 */
      .text-clip {
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: hidden;
      }
      /* Loading层 */
      .loading-box {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 9999;
        box-sizing: border-box;
        width: 100vw;
        height: 100vh;
        background: #555;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      .loading-box .content img,
      .loading-box .content span {
        display: block;
      }
      .loading-box .content img {
        margin: 0 auto;
        width: 0.5rem;
        height: 0.5rem;
      }
      .loading-box .content span {
        margin-top: 0.1rem;
        color: #1989fa;
      }
      /* 背景层 */
      .mark-overlay,
      .mark-image {
        position: absolute;
        top: -10%;
        left: -10%;
        width: 120%;
        height: 120%;
      }
      .mark-image {
        z-index: -2;
        background-repeat: no-repeat;
        background-size: cover;
        filter: blur(6px);
      }
      .mark-overlay {
        z-index: -1;
        background: rgba(0, 0, 0, 0.5);
      }
      /* 头部区域样式 */
      .header-box {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0.15rem;
      }
      .header-box .player-button {
        margin-left: 0.05rem;
        width: 0.35rem;
        height: 0.35rem;
        background: url('../images/music.svg') no-repeat;
        background-size: 100% 100%;
      }
      .header-box .player-button.move {
        animation: musicMove 1s linear 0s infinite both;
      }
      .header-box .base {
        flex-grow: 1;
        display: flex;
      }
      .header-box .base .cover {
        width: 0.7rem;
        height: 0.7rem;
        background: #AAA;
      }
      .header-box .base .cover img {
        display: block;
        width: 100%;
        height: 100%;
      }
      .header-box .base .cover img[src=""] {
        display: none;
      }
      .header-box .base .info {
        flex-grow: 1;
        margin-left: 0.05rem;
        max-width: 2.3rem;
      }
      .header-box .base .info .title,
      .header-box .base .info .author {
        line-height: 0.35rem;
        color: #fff;
        font-size: 0.17rem;
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: hidden;
      }
      .header-box .base .info .author {
        font-size: 0.15rem;
      }
      @keyframes musicMove {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
      /* 歌词区域样式 */
      .main-box .wrapper {
        transform: translateY(0);
        transition: transform 0.3s;
      }
      .main-box .wrapper p {
        height: 0.5rem;
        line-height: 0.5rem;
        text-align: center;
        font-size: 0.15rem;
        color: rgba(255, 255, 255, 0.5);
      }
      .main-box .wrapper p.active {
        color: #31C27C;
        transition: color 0.3s;
      }
      /* 尾部区域样式 */
      .footer-box {
        padding: 0 0.1rem;
      }
      .footer-box .download {
        display: block;
        margin: 0 auto;
        width: 2.13rem;
        height: 0.5rem;
        line-height: 0.5rem;
        text-align: center;
        font-size: 0.18rem;
        color: #fff;
        text-indent: 0.2rem;
        border-radius: 0.25rem;
        background: url('../images/sprite_play.png') no-repeat #31C27C;
        background-size: 0.4rem 3.5rem;
        background-position: 0.1rem -2.915rem;
      }
      .footer-box .bar {
        display: flex;
        align-items: center;
      }
      .footer-box .bar .time {
        width: 0.4rem;
        line-height: 0.46rem;
        text-align: center;
        font-size: 0.12rem;
        color: rgba(255, 255, 255, 0.5);
      }
      .footer-box .bar .progress {
        position: relative;
        flex-grow: 1;
        height: 0.02rem;
        background: rgba(255, 255, 255, 0.5);
      }
      .footer-box .bar .progress .already {
        position: absolute;
        top: 0;
        left: 0;
        width: 0;
        height: 100%;
        background: #31C27C;
      }
      /* 音频 */
      #audioBox {
        display: none;
      }
      
      
  • JS高级进阶/day0619_QQMusic/css/index.less

    html {
        font-size: 100px; //1REM=100PX「在375px的设计稿中」
    }
    
    html,
    body,
    #app {
        height: 100%;
        overflow: hidden;
    }
    
    #app {
        position: relative;
        margin: 0 auto;
        max-width: 540px;
        font-size: .14rem;
    }
    
    .header-box,
    .footer-box,
    .main-box {
        box-sizing: border-box;
        height: 1rem;
        overflow: hidden;
    }
    
    .main-box {
        height: calc(100vh - 2rem);
    }
    
    /* 通用样式 */
    @com-green: #31C27C;
    @com-gray: rgba(255, 255, 255, .5);
    
    .text-clip {
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: hidden;
    }
    
    /* Loading层 */
    .loading-box {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 9999;
        box-sizing: border-box;
        width: 100vw;
        height: 100vh;
        background: #555;
    
        display: flex;
        justify-content: center;
        align-items: center;
    
        .content {
    
            img,
            span {
                display: block;
            }
    
            img {
                margin: 0 auto;
                width: .5rem;
                height: .5rem;
            }
    
            span {
                margin-top: .1rem;
                color: rgb(25, 137, 250);
            }
        }
    }
    
    /* 背景层 */
    .mark-overlay,
    .mark-image {
        position: absolute;
        top: -10%;
        left: -10%;
        width: 120%;
        height: 120%;
    }
    
    .mark-image {
        z-index: -2;
        background-repeat: no-repeat;
        background-size: cover;
        filter: blur(6px); // 设置模糊度
    }
    
    .mark-overlay {
        z-index: -1;
        background: rgba(0, 0, 0, .5);
    }
    
    /* 头部区域样式 */
    .header-box {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: .15rem;
    
        .player-button {
            margin-left: .05rem;
            width: .35rem;
            height: .35rem;
            background: url('../images/music.svg') no-repeat;
            background-size: 100% 100%;
    
            &.move {
                animation: musicMove 1s linear 0s infinite both;
            }
        }
    
        .base {
            flex-grow: 1;
            display: flex;
    
            .cover {
                width: .7rem;
                height: .7rem;
                background: #AAA;
    
                img {
                    display: block;
                    width: 100%;
                    height: 100%;
                }
    
                img[src=""] {
                    display: none;
                }
            }
    
            .info {
                flex-grow: 1;
                margin-left: .05rem;
                max-width: 2.3rem;
    
                .title,
                .author {
                    line-height: .35rem;
                    color: #fff;
                    font-size: .17rem;
                    .text-clip;
                }
    
                .author {
                    font-size: .15rem;
                }
            }
        }
    }
    
    @keyframes musicMove {
        0% {
            transform: rotate(0deg);
        }
    
        100% {
            transform: rotate(360deg);
        }
    }
    
    /* 歌词区域样式 */
    .main-box {
        .wrapper {
            transform: translateY(0);
            transition: transform .3s;
    
            p {
                height: .5rem;
                line-height: .5rem;
                text-align: center;
                font-size: .15rem;
                color: @com-gray;
    
                &.active {
                    color: @com-green;
                    transition: color .3s;
                }
            }
        }
    }
    
    /* 尾部区域样式 */
    .footer-box {
        padding: 0 .1rem;
    
        .download {
            display: block;
            margin: 0 auto;
            width: 2.13rem;
            height: .5rem;
            line-height: .5rem;
            text-align: center;
            font-size: .18rem;
            color: #fff;
            text-indent: .2rem;
            border-radius: .25rem;
            background: url('../images/sprite_play.png') no-repeat @com-green;
            background-size: .4rem 3.5rem;
            background-position: .1rem -2.915rem;
        }
    
        .bar {
            display: flex;
            align-items: center;
    
            .time {
                width: .4rem;
                line-height: .46rem;
                text-align: center;
                font-size: .12rem;
                color: @com-gray;
            }
    
            .progress {
                position: relative;
                flex-grow: 1;
                height: .02rem;
                background: @com-gray;
    
                .already {
                    position: absolute;
                    top: 0;
                    left: 0;
                    width: 0;
                    height: 100%;
                    background: @com-green;
                }
            }
        }
    }
    
    /* 音频 */
    #audioBox {
        display: none;
    }
    
  • JS高级进阶/day0619_QQMusic/js/api.js

    const obj = {
      code: 0,
      message: "ok",
      data: {
        title: "我的梦 - 华为手机主题曲",
        author: "张靓颖",
        duration: "03:39",
        pic: "https://zxt_team.gitee.io/opensource/qqmusic/mydream.jpg",
        audio: "https://zxt_team.gitee.io/opensource/qqmusic/mydream.m4a",
        lyric:
          "[ti&#58;《我的梦》]&#10;[ar&#58;张靓颖]&#10;[al&#58;]&#10;[by&#58;]&#10;[offset&#58;0]&#10;[00&#58;01&#46;36]我的梦&#32;&#40;华为手机主题曲&#41;&#32;&#45;&#32;张靓颖&#10;[00&#58;02&#46;11]词:王海涛/张靓颖&#10;[00&#58;02&#46;64]曲:Andy&#32;Love&#10;[00&#58;03&#46;48]编曲:崔迪&#10;[00&#58;04&#46;49]&#10;[00&#58;08&#46;73]一直地一直地往前走&#10;[00&#58;11&#46;65]&#10;[00&#58;13&#46;02]疯狂的世界&#10;[00&#58;14&#46;58]&#10;[00&#58;16&#46;68]迎着痛把眼中所有梦&#10;[00&#58;20&#46;52]&#10;[00&#58;21&#46;03]都交给时间&#10;[00&#58;22&#46;71]&#10;[00&#58;24&#46;24]想飞就用心地去飞&#10;[00&#58;26&#46;98]谁不经历狼狈&#10;[00&#58;30&#46;68]&#10;[00&#58;31&#46;60]我想我会忽略失望的灰&#10;[00&#58;34&#46;99]拥抱遗憾的美&#10;[00&#58;39&#46;05]我的梦说别停留等待&#10;[00&#58;43&#46;94]就让光芒折射泪湿的瞳孔&#10;[00&#58;47&#46;74]映出心中最想拥有的彩虹&#10;[00&#58;51&#46;78]带我奔向那片有你的天空&#10;[00&#58;55&#46;74]因为你是我的梦&#10;[01&#58;01&#46;06]&#10;[01&#58;07&#46;19]我的梦&#10;[01&#58;08&#46;72]&#10;[01&#58;16&#46;75]执着地勇敢地不回头&#10;[01&#58;20&#46;29]&#10;[01&#58;21&#46;05]穿过了黑夜踏过了边界&#10;[01&#58;24&#46;87]路过雨路过风往前冲&#10;[01&#58;28&#46;39]&#10;[01&#58;28&#46;96]总会有一天站在你身边&#10;[01&#58;32&#46;52]泪就让它往下坠&#10;[01&#58;35&#46;00]溅起伤口的美&#10;[01&#58;38&#46;60]&#10;[01&#58;39&#46;55]哦别以为失去的最宝贵&#10;[01&#58;43&#46;00]才让今天浪费&#10;[01&#58;47&#46;04]我的梦说别停留等待&#10;[01&#58;51&#46;93]就让光芒折射泪湿的瞳孔&#10;[01&#58;55&#46;66]映出心中最想拥有的彩虹&#10;[01&#58;59&#46;75]带我奔向那片有你的天空&#10;[02&#58;03&#46;67]因为你是我的梦&#10;[02&#58;09&#46;14]&#10;[02&#58;11&#46;72]我的梦&#10;[02&#58;13&#46;09]&#10;[02&#58;15&#46;13]我的梦&#10;[02&#58;16&#46;64]&#10;[02&#58;19&#46;60]我的梦&#10;[02&#58;21&#46;39]&#10;[02&#58;24&#46;27]世界会怎么变化&#10;[02&#58;26&#46;58]都不是意外&#10;[02&#58;28&#46;33]记得用心去回答&#10;[02&#58;30&#46;52]命运的精彩&#10;[02&#58;32&#46;34]世界会怎么变化&#10;[02&#58;34&#46;51]都离不开爱&#10;[02&#58;36&#46;25]记得成长的对话&#10;[02&#58;38&#46;28]&#10;[02&#58;39&#46;11]勇敢地说我不再等待&#10;[02&#58;45&#46;63]就让光芒折射泪湿的瞳孔&#10;[02&#58;49&#46;75]映出心中最想拥有的彩虹&#10;[02&#58;53&#46;74]带我奔向那片有你的天空&#10;[02&#58;57&#46;73]因为你是我的梦&#10;[03&#58;02&#46;71]&#10;[03&#58;05&#46;51]我的梦&#10;[03&#58;07&#46;32]&#10;[03&#58;09&#46;20]我的梦&#10;[03&#58;14&#46;12]因为你是我的梦",
      },
    };
    
    
    
    const API = {
      queryLyric() {
        return new Promise((resolve) => {
          setTimeout(() => {
            resolve(obj);
          }, Math.round(Math.random() * (2000 - 500) + 500));
        });
      },
    };
    
  • JS高级进阶/day0619_QQMusic/js/index.js

    // 解决 click 事件的300ms延迟问题
    FastClick.attach(document.body);
    console.log(`cdn-->`);
    
    (async function () {
      const baseBox = document.querySelector(".header-box .base"),
        playerButton = document.querySelector(".player-button"),
        wrapperBox = document.querySelector(".wrapper"),
        footerBox = document.querySelector(".footer-box"),
        currentBox = footerBox.querySelector(".current"),
        durationBox = footerBox.querySelector(".duration"),
        alreadyBox = footerBox.querySelector(".already"),
        markImageBox = document.querySelector(".mark-image"),
        loadingBox = document.querySelector(".loading-box"),
        audioBox = document.querySelector("#audioBox");
      let wrapperList = [],
        timer = null,
        matchNum = 0; //记录历史匹配的数量
    
      /* 音乐控制 */
      const format = function format(time) {
        let minutes = Math.floor(time / 60),
          seconds = Math.round(time - minutes * 60);
        minutes = minutes < 10 ? "0" + minutes : "" + minutes;
        seconds = seconds < 10 ? "0" + seconds : "" + seconds;
        return {
          minutes,
          seconds,
        };
      };
      const playend = function playend() {
        clearInterval(timer);
        timer = null;
        currentBox.innerHTML = "00:00";
        alreadyBox.style.width = "0%";
        wrapperBox.style.transform = "translateY(0)";
        wrapperList.forEach((item) => (item.className = ""));
        matchNum = 0;
        playerButton.className = "player-button";
      };
      const handle = function handle() {
        let pH = wrapperList[0].offsetHeight;
        let { currentTime, duration } = audioBox;
        if (isNaN(currentTime) || isNaN(duration)) return;
        // 播放结束
        if (currentTime >= duration) {
          playend();
          return;
        }
    
        // 控制进度条
        let { minutes: currentTimeMinutes, seconds: currentTimeSeconds } =
            format(currentTime),
          { minutes: durationMinutes, seconds: durationSeconds } = format(duration),
          ratio = Math.round((currentTime / duration) * 100);
        currentBox.innerHTML = `${currentTimeMinutes}:${currentTimeSeconds}`;
        durationBox.innerHTML = `${durationMinutes}:${durationSeconds}`;
        alreadyBox.style.width = `${ratio}%`;
    
        // 控制歌词:查找和当前播放时间匹配的歌词段落
        let matchs = wrapperList.filter((item) => {
          let minutes = item.getAttribute("minutes"),
            seconds = item.getAttribute("seconds");
          return minutes === currentTimeMinutes && seconds === currentTimeSeconds;
        });
        if (matchs.length > 0) {
          // 让匹配的段落有选中样式,而其余的移除选中样式
          wrapperList.forEach((item) => (item.className = ""));
          matchs.forEach((item) => (item.className = "active"));
          // 控制移动
          matchNum += matchs.length;
          if (matchNum > 3) {
            let offset = (matchNum - 3) * pH;
            wrapperBox.style.transform = `translateY(${-offset}px)`;
          }
        }
      };
      playerButton.addEventListener("click", function () {
        if (audioBox.paused) {
          // 当前是暂停的:我们让其播放
          audioBox.play();
          playerButton.className = "player-button move";
          handle();
          if (!timer) timer = setInterval(handle, 1000);
          return;
        }
        // 当前是播放的:我们让其暂停
        audioBox.pause();
        playerButton.className = "player-button";
        clearInterval(timer);
        timer = null;
      });
    
      /* 绑定数据 */
      const bindLyric = function bindLyric(lyric) {
        // 处理歌词部分的特殊符号
        lyric = lyric.replace(/&#(\d+);/g, (value, $1) => {
          let instead = value;
          switch (+$1) {
            case 32:
              instead = " ";
              break;
            case 40:
              instead = "(";
              break;
            case 41:
              instead = ")";
              break;
            case 45:
              instead = "-";
              break;
            default:
          }
          return instead;
        });
        // 解析歌词信息
        let arr = [];
        lyric.replace(
          /\[(\d+)&#58;(\d+)&#46;(?:\d+)\]([^&#?]+)(?:&#10;)?/g,
          (_, $1, $2, $3) => {
            arr.push({
              minutes: $1,
              seconds: $2,
              text: $3,
            });
          }
        );
        // 歌词绑定
        let str = ``;
        arr.forEach(({ minutes, seconds, text }) => {
          str += `<p minutes="${minutes}" seconds="${seconds}">
                    ${text}
                </p>`;
        });
        wrapperBox.innerHTML = str;
        // 获取所有的P标签
        wrapperList = Array.from(wrapperBox.querySelectorAll("p"));
      };
      const binding = function binding(data) {
        let { title, author, duration, pic, audio, lyric } = data;
        // @1 绑定头部基本信息
        baseBox.innerHTML = `
            <div class="cover">
                <img src="${pic}" alt="">
            </div>
            <div class="info">
                <h2 class="title">${title}</h2>
                <h3 class="author">${author}</h3>
            </div>
        `;
        // @2 杂七杂八的信息
        durationBox.innerHTML = duration;
        markImageBox.style.backgroundImage = `url(${pic})`;
        audioBox.src = audio;
        // @3 绑定歌词信息
        bindLyric(lyric);
        // @4 关闭Loading效果
        loadingBox.style.display = "none";
      };
    
      /* 向服务器发送请求,从服务器获取相关的数据 */
      try {
        let { code, data } = await API.queryLyric();
        if (+code === 0) {
          // 请求成功:网络层和业务层都成功
          binding(data);
          return;
        }
      } catch (_) {}
      // 请求失败
      alert("网络繁忙,请刷新页面");
    })();
    

静态页面的免费部署

gitee部署项目

  • 部署项目:gitee

github部署项目

  • 第二种方案:基于github部署

vue2

  • 组件化开发:

    1. 一个页面比较大,我们把页面按照功能板块拆分为一个个的组件,最后把组件合并在一起
    2. 在SPA单页面应用开发中,真正的页面只有一个,我们把所谓的页面都设计为一个个的组件,最后基于路由管控机制,控制哪些组件渲染,哪些组件销毁
  • 脚手架:把webpack相关的配置规则都处理好了,把一个工程化组件化的项目架子都搭建完毕了

    • Vue框架:
      1. 以webpack为核心的脚手架 @vue/cli
        • 创建vue2的项目
        • 创建vue3的项目
      2. 新的脚手架 vite
        • 创建vue3的项目

进阶参考

  1. FastClick-cdn
  2. prefixfree-cdn
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值