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>
    
    <audio
      src="https://zxt_team.gitee.io/opensource/qqmusic/mydream.m4a"
      id="audioInp"
      controls
      autoplay
      loop
      preload="metadata"
    />
    

    <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:《我的梦》]
[ar:张靓颖]
[al:]
[by:]
[offset:0]
[00:01.36]我的梦 (华为手机主题曲) - 张靓颖
[00:02.11]词:王海涛/张靓颖
[00:02.64]曲:Andy Love
[00:03.48]编曲:崔迪
[00:04.49]
[00:08.73]一直地一直地往前走
[00:11.65]
[00:13.02]疯狂的世界
[00:14.58]
[00:16.68]迎着痛把眼中所有梦
[00:20.52]
[00:21.03]都交给时间
[00:22.71]
[00:24.24]想飞就用心地去飞
[00:26.98]谁不经历狼狈
[00:30.68]
[00:31.60]我想我会忽略失望的灰
[00:34.99]拥抱遗憾的美
[00:39.05]我的梦说别停留等待
[00:43.94]就让光芒折射泪湿的瞳孔
[00:47.74]映出心中最想拥有的彩虹
[00:51.78]带我奔向那片有你的天空
[00:55.74]因为你是我的梦
[01:01.06]
[01:07.19]我的梦
[01:08.72]
[01:16.75]执着地勇敢地不回头
[01:20.29]
[01:21.05]穿过了黑夜踏过了边界
[01:24.87]路过雨路过风往前冲
[01:28.39]
[01:28.96]总会有一天站在你身边
[01:32.52]泪就让它往下坠
[01:35.00]溅起伤口的美
[01:38.60]
[01:39.55]哦别以为失去的最宝贵
[01:43.00]才让今天浪费
[01:47.04]我的梦说别停留等待
[01:51.93]就让光芒折射泪湿的瞳孔
[01:55.66]映出心中最想拥有的彩虹
[01:59.75]带我奔向那片有你的天空
[02:03.67]因为你是我的梦
[02:09.14]
[02:11.72]我的梦
[02:13.09]
[02:15.13]我的梦
[02:16.64]
[02:19.60]我的梦
[02:21.39]
[02:24.27]世界会怎么变化
[02:26.58]都不是意外
[02:28.33]记得用心去回答
[02:30.52]命运的精彩
[02:32.34]世界会怎么变化
[02:34.51]都离不开爱
[02:36.25]记得成长的对话
[02:38.28]
[02:39.11]勇敢地说我不再等待
[02:45.63]就让光芒折射泪湿的瞳孔
[02:49.75]映出心中最想拥有的彩虹
[02:53.74]带我奔向那片有你的天空
[02:57.73]因为你是我的梦
[03:02.71]
[03:05.51]我的梦
[03:07.32]
[03:09.20]我的梦
[03:14.12]因为你是我的梦",
  },
};

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

对歌词进行处理

let lyric = "[ti:《我的梦》]
[ar:张靓颖]
[al:]
[by:]
[offset:0]
[00:01.36]我的梦 (华为手机主题曲) - 张靓颖
[00:02.11]词:王海涛/张靓颖
[00:02.64]曲:Andy Love
[00:03.48]编曲:崔迪
[00:04.49]
[00:08.73]一直地一直地往前走
[00:11.65]
[00:13.02]疯狂的世界
[00:14.58]
[00:16.68]迎着痛把眼中所有梦
[00:20.52]
[00:21.03]都交给时间
[00:22.71]
[00:24.24]想飞就用心地去飞
[00:26.98]谁不经历狼狈
[00:30.68]
[00:31.60]我想我会忽略失望的灰
[00:34.99]拥抱遗憾的美
[00:39.05]我的梦说别停留等待
[00:43.94]就让光芒折射泪湿的瞳孔
[00:47.74]映出心中最想拥有的彩虹
[00:51.78]带我奔向那片有你的天空
[00:55.74]因为你是我的梦
[01:01.06]
[01:07.19]我的梦
[01:08.72]
[01:16.75]执着地勇敢地不回头
[01:20.29]
[01:21.05]穿过了黑夜踏过了边界
[01:24.87]路过雨路过风往前冲
[01:28.39]
[01:28.96]总会有一天站在你身边
[01:32.52]泪就让它往下坠
[01:35.00]溅起伤口的美
[01:38.60]
[01:39.55]哦别以为失去的最宝贵
[01:43.00]才让今天浪费
[01:47.04]我的梦说别停留等待
[01:51.93]就让光芒折射泪湿的瞳孔
[01:55.66]映出心中最想拥有的彩虹
[01:59.75]带我奔向那片有你的天空
[02:03.67]因为你是我的梦
[02:09.14]
[02:11.72]我的梦
[02:13.09]
[02:15.13]我的梦
[02:16.64]
[02:19.60]我的梦
[02:21.39]
[02:24.27]世界会怎么变化
[02:26.58]都不是意外
[02:28.33]记得用心去回答
[02:30.52]命运的精彩
[02:32.34]世界会怎么变化
[02:34.51]都离不开爱
[02:36.25]记得成长的对话
[02:38.28]
[02:39.11]勇敢地说我不再等待
[02:45.63]就让光芒折射泪湿的瞳孔
[02:49.75]映出心中最想拥有的彩虹
[02:53.74]带我奔向那片有你的天空
[02:57.73]因为你是我的梦
[03:02.71]
[03:05.51]我的梦
[03:07.32]
[03:09.20]我的梦
[03:14.12]因为你是我的梦"

// 1. 歌词解析:
// [ti:《我的梦》]

// [ar:张靓颖]

// [al:]

// [by:]

// [offset:0]

// [00:01.36]我的梦 (华为手机主题曲) - 张靓颖

// [00:02.11]词:王海涛/张靓颖

// [00:02.64]曲:Andy Love

// [00:03.48]编曲:崔迪

// [00:04.49]

// [00:08.73]一直地一直地往前走

// ...
// [03:14.12]因为你是我的梦

// 2. 歌词内容推测:
//   ->空格
// ( ->(
// ) ->)
// - ->-
// 
 ->换行符
// : ->前面的数字是分钟
// . ->前面的数字是秒

// 3. 得到歌词的内容格式。
// 一行歌词:[分钟数:秒数.毫秒数]内部有特殊字符的歌词内容

// 内部有特殊字符的歌词内容:`  为 空格` `( 为 (` `) 为 )` `- 为 -` 。
/* lyric.replace(
  /\[(\d+):(\d+).(?:\d+)\]([^&#?]+)(?:
)?/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+):(\d+).(?:\d+)\]([^&#?]+)(?:
)?/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>
        
        
        <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>
            <a href="javascript:;" class="player-button">a>
          header>
    
          
          <main class="main-box">
            <div class="wrapper">
              
            div>
          main>
    
          
          <footer class="footer-box">
            <div class="bar">
              <span class="time current">00:00span>
              <div class="progress">
                <div class="already">div>
              div>
              <span class="time duration">00:00span>
            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>
    
        
        <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/[email protected]/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:《我的梦》]
    [ar:张靓颖]
    [al:]
    [by:]
    [offset:0]
    [00:01.36]我的梦 (华为手机主题曲) - 张靓颖
    [00:02.11]词:王海涛/张靓颖
    [00:02.64]曲:Andy Love
    [00:03.48]编曲:崔迪
    [00:04.49]
    [00:08.73]一直地一直地往前走
    [00:11.65]
    [00:13.02]疯狂的世界
    [00:14.58]
    [00:16.68]迎着痛把眼中所有梦
    [00:20.52]
    [00:21.03]都交给时间
    [00:22.71]
    [00:24.24]想飞就用心地去飞
    [00:26.98]谁不经历狼狈
    [00:30.68]
    [00:31.60]我想我会忽略失望的灰
    [00:34.99]拥抱遗憾的美
    [00:39.05]我的梦说别停留等待
    [00:43.94]就让光芒折射泪湿的瞳孔
    [00:47.74]映出心中最想拥有的彩虹
    [00:51.78]带我奔向那片有你的天空
    [00:55.74]因为你是我的梦
    [01:01.06]
    [01:07.19]我的梦
    [01:08.72]
    [01:16.75]执着地勇敢地不回头
    [01:20.29]
    [01:21.05]穿过了黑夜踏过了边界
    [01:24.87]路过雨路过风往前冲
    [01:28.39]
    [01:28.96]总会有一天站在你身边
    [01:32.52]泪就让它往下坠
    [01:35.00]溅起伤口的美
    [01:38.60]
    [01:39.55]哦别以为失去的最宝贵
    [01:43.00]才让今天浪费
    [01:47.04]我的梦说别停留等待
    [01:51.93]就让光芒折射泪湿的瞳孔
    [01:55.66]映出心中最想拥有的彩虹
    [01:59.75]带我奔向那片有你的天空
    [02:03.67]因为你是我的梦
    [02:09.14]
    [02:11.72]我的梦
    [02:13.09]
    [02:15.13]我的梦
    [02:16.64]
    [02:19.60]我的梦
    [02:21.39]
    [02:24.27]世界会怎么变化
    [02:26.58]都不是意外
    [02:28.33]记得用心去回答
    [02:30.52]命运的精彩
    [02:32.34]世界会怎么变化
    [02:34.51]都离不开爱
    [02:36.25]记得成长的对话
    [02:38.28]
    [02:39.11]勇敢地说我不再等待
    [02:45.63]就让光芒折射泪湿的瞳孔
    [02:49.75]映出心中最想拥有的彩虹
    [02:53.74]带我奔向那片有你的天空
    [02:57.73]因为你是我的梦
    [03:02.71]
    [03:05.51]我的梦
    [03:07.32]
    [03:09.20]我的梦
    [03:14.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+):(\d+).(?:\d+)\]([^&#?]+)(?:
    )?/g,
          (_, $1, $2, $3) => {
            arr.push({
              minutes: $1,
              seconds: $2,
              text: $3,
            });
          }
        );
        // 歌词绑定
        let str = ``;
        arr.forEach(({ minutes, seconds, text }) => {
          str += `

    ${minutes}" seconds="${seconds}"> ${text}

    `
    ; }); 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 = `
    ${pic}" alt="">

    ${title}

    ${author}

    `
    ; // @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

你可能感兴趣的:(重返学习,原生js学习,vue2.0,学习,javascript,前端)