Skip to content

音乐播放器-2

音乐播放器-2

image-20250419105957490

目录

[toc]

已下架

危险

因次功能特别影响首页加载,所以下架次功能。(且音乐播放器功能之前已经实现了的) --2025年4月21日

image-20250421064913905

背景

给你的 vitepress 添加音乐播放器,让你的网站更加有活力。

环境

2025.4.19(已解决)

警告

次配置适用于Teek@1.0.2-2025.4.10版本(其它版本可自行测试)。

提示

自己开源的 《vitepress-theme-teek-one-public》网站模板。

此模板是在《Young Kbt blog》大佬开源项目《vitepress-theme-teek 》基础上修改为自己的风格而成,感谢大佬开发得主题,大佬威武。❤️❤️

版权

提示

本文是在 博主程序员皮蛋鸽鸽 💻 网站:极客兔 - 笔记站》 看到的 nav 导航栏音乐播放器。在基础上增加了自己实践过程的一些细节,转载无需和我联系,但请注明文章来源。如果侵权之处,请联系博主进行删除,谢谢~

本次是在《Hyde Blog》大佬的《音乐播放器》文章基础上,增加了自己实践过程的一些细节,感谢大佬的文章。💖💖💖

1、安装依赖

警告

基于作者的 vue 组件用了 element-plus 组件提示,所以需要安装 element-plus 组件,不需要可以跳过。

sh
pnpm install element-plus
sh
npm install element-plus --save
sh
yarn add element-plus

2、新建组件

  • docs\.vitepress\theme\components\ 创建如下文件
详细信息
vue
<script setup lang="ts">
// import '../../lib/iconfont/iconfont';    // vitepress 基于 nodejs 的项目,无法引入需要window对象的模块

import { onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import 'element-plus/dist/index.css'
import PauseMusicController from './PauseMusicController.vue'
import PlayingMusicController from './PlayingMusicController.vue'
/**
 *
 * 音乐播放器
 */
const musics = [
  '成都-赵雷.mp3',
  '我愿意-王菲.mp3',
  '阴天快乐.mp3',
]
// 当前音乐
const currentMusic = ref('/music/成都-赵雷.mp3')
// 播放器元素
const audio = ref<HTMLAudioElement | null>()
// 是否播放音乐: 默认: false
const isPlayed = ref(false)
// 播放音乐的随机数字
let random = ref(0)
// 开一个定时器,什么时候需要销毁播放器可以直接清除该查询定时器
let music_palyer_timer = ref<ReturnType<typeof setInterval> | null>()
const playMusic = () => {
  /**
   * 浏览器为什么不能直接播放音乐参考博客:
   * https://blog.csdn.net/s18813688772/article/details/121103802
   */
  isPlayed.value = !isPlayed.value
  const musicName = currentMusic.value.split('/').pop()?.replace(/\.mp3$/, '') ?? '未知歌曲'
  ElMessage({
    message: isPlayed.value ? `正在播放: ${musicName}` : `已暂停: ${musicName}`,
    type: isPlayed.value ? 'success' : 'warning',
    duration: 2000
  })
  console.log('播放状态: ', isPlayed.value ? '播放' : '不播放')

  if (isPlayed.value) {
    // 如果是播放状态,则播放音乐
    audio.value?.play()
  } else {
    // 如果是暂停状态,则暂停音乐
    audio.value?.pause()

    const handleLoadError = () => {
      ElMessage.error('音乐加载失败,请重试')
    }
  }
}
const generateRandom = () => {
  /**
   * 生成一个与上次的数字不一样的数字
   */
  let tmp: number = Math.floor(Math.random() * musics.length)
  while (tmp === random.value) {
    tmp = Math.floor(Math.random() * musics.length)
  }
  return tmp
}
onMounted(() => {
  // 挂在完成后给一个随机音乐
  random.value = generateRandom()
  console.log(`%c第${random.value + 1}首音乐.`, 'color: green; font-weight: bolder;')
  currentMusic.value = `/music/${musics[random.value]}`

  // 提示用户可以播放音乐
  /* setTimeout(() => {
        confirm('点击右侧🎵可以播放音乐哦~');
    }, 100); */

  // 组件挂在完成即开启定时器监听音乐是否播放完成的状态
  music_palyer_timer.value = setInterval(function () {
    // 如果音频播放器获取到了,就监听是否结束的事件
    if (audio.value?.ended) {
      console.log('%c音乐结束, 下一曲~', 'color: oranger; font-weight: bold;')
      // 以播放结束的标志判断
      random.value = generateRandom()
      console.log(`%c第${random.value}首音乐.`, 'color: green; font-weight: bolder;')
      currentMusic.value = `/music/${musics[random.value]}`
      /*audio.value.onended = function () {
                // 以播放结束的事件监听形式控制
                let random: number = Math.floor(Math.random() * musics.length);
                currentMusic.value = `/music/${musics[random]}`;
                console.log('音乐结束, 下一曲~');
            }*/
    }
  }, 1000)
})

/**
 * 播放定时器的清除看情况 ...
 * Todo...
 */
</script>

<template>
  <div class="playMusic-wrapper">
    <button class="playMusic" @click="playMusic">
      <PlayingMusicController v-if="isPlayed" />
      <PauseMusicController v-else />
      <!-- <svg class="icon" aria-hidden="true">
                <use :xlink:href="`#icon-${isPlayed ? 'music' : 'play2'}`"></use>
            </svg> -->
    </button>
    <audio ref="audio" preload="auto" :autoplay="isPlayed" :src="currentMusic" style="display: none" controls></audio>
  </div>
</template>

<style scoped lang="scss">
$PlayControler-width: 20px;
$PlayControler-height: 20px;

.playMusic-wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 36px;
  // width: 400px; // 测试
  height: 36px;
  margin: 0 5px;

  .playMusic {
    width: $PlayControler-width;
    height: $PlayControler-width;
    border-radius: 20%;
    font-size: 1.3rem;
    line-height: 1.3rem;

    svg {
      margin: 0;
      padding: 0;
      width: $PlayControler-width;
      height: $PlayControler-width;
    }
  }
}
</style>
vue
<script setup lang="ts">
// import '../../lib/iconfont/iconfont';    // vitepress 基于 nodejs 的项目,无法引入需要window对象的模块

import { onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import 'element-plus/dist/index.css'
import PauseMusicController from './PauseMusicController.vue'
import PlayingMusicController from './PlayingMusicController.vue'
/**
 *
 * 音乐播放器
 */
const musics = [
  '周杰伦-搁浅.mp3',
  '林俊杰-不潮不用花钱.mp3',
  '林俊杰-可惜没如果.mp3',
  '林俊杰-美人鱼.mp3',
  '林俊杰-修炼爱情.mp3',
  '周杰伦-安静.mp3',
  '周杰伦-不能说的秘密.mp3',
  '周杰伦-彩虹.mp3',
  '周杰伦-断了的弦.mp3',
  '周杰伦-搁浅.mp3',
  '周杰伦-轨迹.mp3',
  '周杰伦-回到过去.mp3',
  '周杰伦-借口.mp3',
  '周杰伦-晴天.mp3',
  '周杰伦-退后.mp3',
]
// 当前音乐
const currentMusic = ref('/music/周杰伦-搁浅.mp3')
// 播放器元素
const audio = ref<HTMLAudioElement | null>()
// 是否播放音乐: 默认: false
const isPlayed = ref(false)
// 播放音乐的随机数字
let random = ref(0)
// 开一个定时器,什么时候需要销毁播放器可以直接清除该查询定时器
let music_palyer_timer = ref<ReturnType<typeof setInterval> | null>()
const playMusic = () => {
  /**
   * 浏览器为什么不能直接播放音乐参考博客:
   * https://blog.csdn.net/s18813688772/article/details/121103802
   */
  isPlayed.value = !isPlayed.value
  const musicName = currentMusic.value.split('/').pop()?.replace(/\.mp3$/, '') ?? '未知歌曲'
  ElMessage({
    message: isPlayed.value ? `正在播放: ${musicName}` : `已暂停: ${musicName}`,
    type: isPlayed.value ? 'success' : 'warning',
    duration: 2000
  })
  console.log('播放状态: ', isPlayed.value ? '播放' : '不播放')

  if (isPlayed.value) {
    // 如果是播放状态,则播放音乐
    audio.value?.play()
  } else {
    // 如果是暂停状态,则暂停音乐
    audio.value?.pause()

    const handleLoadError = () => {
      ElMessage.error('音乐加载失败,请重试')
    }
  }
}
const generateRandom = () => {
  /**
   * 生成一个与上次的数字不一样的数字
   */
  let tmp: number = Math.floor(Math.random() * musics.length)
  while (tmp === random.value) {
    tmp = Math.floor(Math.random() * musics.length)
  }
  return tmp
}
onMounted(() => {
  // 挂在完成后给一个随机音乐
  random.value = generateRandom()
  console.log(`%c第${random.value + 1}首音乐.`, 'color: green; font-weight: bolder;')
  currentMusic.value = `/music/${musics[random.value]}`

  // 提示用户可以播放音乐
  /* setTimeout(() => {
        confirm('点击右侧🎵可以播放音乐哦~');
    }, 100); */

  // 组件挂在完成即开启定时器监听音乐是否播放完成的状态
  music_palyer_timer.value = setInterval(function () {
    // 如果音频播放器获取到了,就监听是否结束的事件
    if (audio.value?.ended) {
      console.log('%c音乐结束, 下一曲~', 'color: oranger; font-weight: bold;')
      // 以播放结束的标志判断
      random.value = generateRandom()
      console.log(`%c第${random.value}首音乐.`, 'color: green; font-weight: bolder;')
      currentMusic.value = `/music/${musics[random.value]}`
      /*audio.value.onended = function () {
                // 以播放结束的事件监听形式控制
                let random: number = Math.floor(Math.random() * musics.length);
                currentMusic.value = `/music/${musics[random]}`;
                console.log('音乐结束, 下一曲~');
            }*/
    }
  }, 1000)
})

/**
 * 播放定时器的清除看情况 ...
 * Todo...
 */
</script>

<template>
  <div class="playMusic-wrapper">
    <button class="playMusic" @click="playMusic">
      <PlayingMusicController v-if="isPlayed" />
      <PauseMusicController v-else />
      <!-- <svg class="icon" aria-hidden="true">
                <use :xlink:href="`#icon-${isPlayed ? 'music' : 'play2'}`"></use>
            </svg> -->
    </button>
    <audio ref="audio" preload="auto" :autoplay="isPlayed" :src="currentMusic" style="display: none" controls></audio>
  </div>
</template>

<style scoped lang="scss">
$PlayControler-width: 20px;
$PlayControler-height: 20px;

.playMusic-wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 36px;
  // width: 400px; // 测试
  height: 36px;
  margin: 0 5px;

  .playMusic {
    width: $PlayControler-width;
    height: $PlayControler-width;
    border-radius: 20%;
    font-size: 1.3rem;
    line-height: 1.3rem;

    svg {
      margin: 0;
      padding: 0;
      width: $PlayControler-width;
      height: $PlayControler-width;
    }
  }
}
</style>
vue
// 播放时图标
<template>
  <svg
    t="1706712309791"
    class="icon"
    viewBox="0 0 1024 1024"
    version="1.1"
    xmlns="http://www.w3.org/2000/svg"
    p-id="1184"
    width="200"
    height="200"
  >
    <path
      d="M983.38 306.44l-442.628 81.458a17.454 17.454 0 0 0-14.302 17.162v375.822c-31.38-18.59-72.608-24.724-113.914-13.666-74.146 19.85-120.418 87.528-103.352 151.164s91.006 99.13 165.152 79.28c65.884-17.638 109.718-73.042 106.704-129.814h0.188V469.482l367.992-67.74v289.742c-31.38-18.59-72.61-24.724-113.914-13.666-74.146 19.85-120.418 87.528-103.352 151.164 17.066 63.636 91.006 99.13 165.152 79.28 64.794-17.348 108.296-71.218 106.852-126.998H1004l0.008-457.66c0.002-10.906-9.896-19.14-20.628-17.164z"
      fill="#707AFA"
      p-id="1185"
    ></path>
    <path
      d="M474.336 977.676c-74.146 19.85-148.086-15.644-165.152-79.28a103.588 103.588 0 0 1-3.038-16.91c-1.166 12.202-0.254 24.62 3.038 36.894 17.066 63.636 91.006 99.13 165.152 79.28 65.884-17.638 109.718-73.042 106.704-129.814h0.188v-19.984h-0.188c3.014 56.774-40.82 112.176-106.704 129.814zM1004 761.28h-0.042c1.444 55.78-42.058 109.652-106.852 126.998-74.146 19.85-148.086-15.644-165.152-79.28a103.588 103.588 0 0 1-3.038-16.91c-1.166 12.202-0.254 24.62 3.038 36.894 17.066 63.636 91.006 99.13 165.152 79.28 64.794-17.348 108.296-71.218 106.852-126.998H1004l0.008-457.66v-0.006L1004 761.28z"
      fill="#6770E6"
      p-id="1186"
    ></path>
    <path
      d="M487.55 167.318c-30.86-99.616-129.14-131.888-191.96-142.34-15.806-2.62-31.02-3.864-41.386-4.454a12.668 12.668 0 0 0-13.394 12.65v378.478c-31.38-18.59-72.61-24.726-113.916-13.668-74.146 19.85-120.418 87.528-103.352 151.164 17.066 63.636 91.006 99.13 165.152 79.28 65.884-17.638 109.716-73.042 106.704-129.814h0.192V150.392c35.96 2.658 84.72 19.644 130.94 79.312 67.62 87.246 154.7 51.036 154.7 51.036-39.7 0.002-75.24-53.872-93.68-113.422z"
      fill="#FF8354"
      p-id="1187"
    ></path>
    <path
      d="M188.692 607.944c-74.146 19.85-148.086-15.644-165.152-79.28a103.55 103.55 0 0 1-3.008-16.66c-1.204 12.282-0.306 24.79 3.008 37.146 17.066 63.636 91.006 99.13 165.152 79.28 65.884-17.638 109.716-73.042 106.704-129.814h0.192v-20.486h-0.192c3.014 56.774-40.82 112.176-106.704 129.814z"
      fill="#E0734A"
      p-id="1188"
    ></path>
    <path
      d="M126.894 437.954c41.306-11.058 82.536-4.924 113.916 13.668v-39.966c-31.38-18.59-72.61-24.726-113.916-13.668-71.43 19.122-116.964 82.634-104.95 144.156 9.03-47.094 49.064-89.228 104.95-104.19z"
      fill="#FFAC8C"
      p-id="1189"
    ></path>
    <path
      d="M540.752 427.864l442.628-81.456c10.728-1.974 20.62 6.252 20.628 17.148v-39.952c0-10.904-9.896-19.138-20.628-17.162l-442.628 81.456a17.456 17.456 0 0 0-14.302 17.162v39.966a17.454 17.454 0 0 1 14.302-17.162zM835.308 717.784c41.306-11.058 82.534-4.924 113.914 13.666v-39.966c-31.38-18.59-72.61-24.724-113.914-13.666-71.43 19.124-116.964 82.634-104.95 144.156 9.03-47.094 49.064-89.228 104.95-104.19zM412.536 807.182c41.304-11.058 82.532-4.924 113.914 13.666v-39.966c-31.38-18.59-72.608-24.724-113.914-13.666-71.43 19.124-116.964 82.634-104.948 144.156 9.03-47.092 49.064-89.228 104.948-104.19z"
      fill="#8F95E6"
      p-id="1190"
    ></path>
    <path
      d="M244.204 54.496c10.366 0.59 25.582 1.834 41.386 4.454 62.82 10.452 161.1 42.724 191.96 142.34 10.292 33.238 25.916 64.698 44.612 86.018 33.838 3.914 59.066-6.566 59.066-6.566-39.7 0-75.24-53.874-93.68-113.424-30.86-99.616-129.14-131.888-191.96-142.34-15.806-2.618-31.02-3.864-41.386-4.454a12.668 12.668 0 0 0-13.394 12.65v21.598a12.806 12.806 0 0 1 3.396-0.276z"
      fill="#FFAC8C"
      p-id="1191"
    ></path>
    <path
      d="M865.658 118.83c-9.74 0-17.636-7.89-17.636-17.62 0-9.378-7.082-17.566-16.448-18.172-10.264-0.664-18.796 7.456-18.796 17.568v0.604c0 9.732-7.896 17.62-17.636 17.62-9.386 0-17.58 7.076-18.186 16.434-0.664 10.254 7.462 18.78 17.582 18.78h0.604c9.74 0 17.636 7.89 17.636 17.62 0 9.378 7.082 17.566 16.448 18.172 10.264 0.664 18.796-7.456 18.796-17.568v-0.604c0-9.732 7.896-17.62 17.636-17.62h0.604c10.12 0 18.248-8.524 17.584-18.78-0.608-9.358-8.804-16.434-18.188-16.434zM188.01 811.662c-13.588 0-24.602-11.006-24.602-24.582 0-13.082-9.88-24.504-22.946-25.35-14.318-0.926-26.22 10.402-26.22 24.508v0.842c0 13.576-11.014 24.582-24.602 24.582-13.092 0-24.524 9.872-25.37 22.926-0.928 14.306 10.41 26.198 24.528 26.198h0.844c13.588 0 24.602 11.006 24.602 24.582 0 13.082 9.88 24.504 22.946 25.35 14.318 0.926 26.22-10.402 26.22-24.508v-0.842c0-13.576 11.014-24.582 24.602-24.582h0.844c14.118 0 25.456-11.892 24.53-26.198-0.85-13.054-12.282-22.926-25.376-22.926z"
      fill="#69EBFC"
      p-id="1192"
    ></path>
  </svg>
</template>

3、注册组件

  • docs\.vitepress\theme\components\TeekLayoutProvider.vue 中注册组件
VUE
<script setup lang="ts" name="TeekLayoutProvider">
import MusicPlayer from "./MusicPlayer.vue"; // 引入音乐播放器组件
</script>

<template>
  <Teek.Layout>
    <template #nav-bar-content-after>
      <div :class="ns.b('appearance')">
        ...
      </div>

      <MusicPlayer />
    </template>
  </Teek.Layout>
</template>

image-20250419104921158

4、提供音乐源文件

  • docs\public\music 目录下放置音乐文件

image-20250419105052615

  • 修改docs\.vitepress\theme\components\MusicPlayer.vue文件里音乐源文件名称(修改为你自己的音乐名称)

image-20250419105216506

5、验证

运行项目,验证效果:

image-20250419105529831

结束。

关于我

我的博客主旨:

  • 排版美观,语言精炼;
  • 文档即手册,步骤明细,拒绝埋坑,提供源码;
  • 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!

🍀 个人网站

image-20250109220325748

🍀 微信二维码

x2675263825 (舍得), qq:2675263825。

image-20230107215114763

🍀 微信公众号

《云原生架构师实战》

image-20230107215126971

🍀 csdn

https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

image-20230107215149885

🍀 知乎

https://www.zhihu.com/people/foryouone

image-20230107215203185

最后

如果你还有疑惑,可以去我的网站查看更多内容或者联系我帮忙查看。

如果你有更好的方式,评论区留言告诉我。谢谢!

好了,本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

最近更新