基于Vue3怎么编写一个简单的播放器

其他教程   发布日期:2023年07月27日   浏览次数:666

本文小编为大家详细介绍“基于Vue3怎么编写一个简单的播放器”,内容详细,步骤清晰,细节处理妥当,希望这篇“基于Vue3怎么编写一个简单的播放器”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

TODO

  • 实现播放/暂停;

  • 实现开始/结束时间及开始时间和滚动条动态跟随播放动态变化;

  • 实现点击进度条跳转指定播放位置;

  • 实现点击圆点拖拽滚动条。

页面布局及

  1. css
样式如下
  1. <template>
  2. <div class="song-item">
  3. <audio src="" />
  4. <!-- 进度条 -->
  5. <div class="audio-player">
  6. <span>00:00</span>
  7. <div class="progress-wrapper">
  8. <div class="progress-inner">
  9. <div class="progress-dot" />
  10. </div>
  11. </div>
  12. <span>00:00</span>
  13. <!-- 播放/暂停 -->
  14. <div >
  15. 播放
  16. </div>
  17. </div>
  18. </div>
  19. </template>
  20. <style lang="scss">
  21. * { font-size: 14px; }
  22. .song-item {
  23. display: flex;
  24. flex-direction: column;
  25. justify-content: center;
  26. height: 100px;
  27. padding: 0 20px;
  28. transition: all ease .2s;
  29. border-bottom: 1px solid #ddd;
  30. /* 进度条样式 */
  31. .audio-player {
  32. display: flex;
  33. height: 18px;
  34. margin-top: 10px;
  35. align-items: center;
  36. font-size: 12px;
  37. color: #666;
  38. .progress-wrapper {
  39. flex: 1;
  40. height: 4px;
  41. margin: 0 20px 0 20px;
  42. border-radius: 2px;
  43. background-color: #e9e9eb;
  44. cursor: pointer;
  45. .progress-inner {
  46. position: relative;
  47. width: 0%;
  48. height: 100%;
  49. border-radius: 2px;
  50. background-color: #409eff;
  51. .progress-dot {
  52. position: absolute;
  53. top: 50%;
  54. right: 0;
  55. z-index: 1;
  56. width: 10px;
  57. height: 10px;
  58. border-radius: 50%;
  59. background-color: #409eff;
  60. transform: translateY(-50%);
  61. }
  62. }
  63. }
  64. }
  65. }
  66. </style>

实现播放/暂停

思路:给 ”播放“ 注册点击事件,在点击事件中通过

  1. audio
的属性及方法来判定当前歌曲是什么状态,是否播放或暂停,然后声明一个属性同步这个状态,在模板中做出判断当前应该显示 ”播放/暂停“。

关键性 api:

  1. audio.paused
:当前播放器是否为暂停状态

  1. audio.play()
:播放

  1. audio.pause()
:暂停
  1. const audioIsPlaying = ref(false); // 用于同步当前的播放状态
  2. const audioEle = ref<HTMLAudioElement | null>(null); // audio 元素
  3. /**
  4. * @description 播放/暂停音乐
  5. */
  6. const togglePlayer = () => {
  7. if (audioEle.value) {
  8. if (audioEle.value?.paused) {
  9. audioEle.value.play();
  10. audioIsPlaying.value = true;
  11. }
  12. else {
  13. audioEle.value?.pause();
  14. audioIsPlaying.value = false;
  15. }
  16. }
  17. };
  18. onMounted(() => {
  19. // 页面点击的时候肯定是加载完成了,这里获取一下没毛病
  20. audioEle.value = document.querySelector('audio');
  21. });

最后把属性及事件应用到模板中去。

  1. <div
  2. @click="togglePlayer"
  3. >
  4. {{ audioIsPlaying ? '暂停' : '播放'}}
  5. </div>

实现开始/结束时间及开始时间和滚动条动态跟随播放动态变化

思路:获取当前已经播放的时间及总时长,然后再拿当前时长 / 总时长及得到歌曲播放的百分比即滚动条的百分比。通过侦听

  1. audio
元素的
  1. timeupdate
事件以做到每次当前时间改变时,同步把 DOM 也进行更新。最后播放完成后把状态初始化。

关键性api:

  1. audio.currentTime
:当前的播放时间;单位(s)

  1. audio.duration
:音频的总时长;单位(s)

  1. timeupdate
  1. currentTime
变更时会触发该事件。
  1. import dayjs from 'dayjs';
  2. const audioCurrentPlayTime = ref('00:00'); // 当前播放时长
  3. const audioCurrentPlayCountTime = ref('00:00'); // 总时长
  4. const pgsInnerEle = ref<HTMLDivElement | null>(null);
  5. /**
  6. * @description 更新进度条与当前播放时间
  7. */
  8. const updateProgress = () => {
  9. const currentProgress = audioEle.value!.currentTime / audioEle.value!.duration;
  10. pgsInnerEle.value!.style.width = `${currentProgress * 100}%`;
  11. // 设置进度时长
  12. if (audioEle.value)
  13. audioCurrentPlayTime.value = dayjs(audioEle.value.currentTime * 1000).format('mm:ss');
  14. };
  15. /**
  16. * @description 播放完成重置播放状态
  17. */
  18. const audioPlayEnded = () => {
  19. audioCurrentPlayTime.value = '00:00';
  20. pgsInnerEle.value!.style.width = '0%';
  21. audioIsPlaying.value = false;
  22. };
  23. onMounted(() => {
  24. pgsInnerEle.value = document.querySelector('.progress-inner');
  25. // 设置总时长
  26. if (audioEle.value)
  27. audioCurrentPlayCountTime.value = dayjs(audioEle.value.duration * 1000).format('mm:ss');
  28. // 侦听播放中事件
  29. audioEle.value?.addEventListener('timeupdate', updateProgress, false);
  30. // 播放结束 event
  31. audioEle.value?.addEventListener('ended', audioPlayEnded, false);
  32. });

实现点击进度条跳转指定播放位置

思路:给滚动条注册鼠标点击事件,每次点击的时候获取当前的

  1. offsetX
以及滚动条的宽度,用宽度 /
  1. offsetX
最后用总时长 * 前面的商就得到了我们想要的进度,再次更新进度条即可。

关键性api:

  1. event.offsetX
:鼠标指针相较于触发事件对象的 x 坐标。
  1. /**
  2. * @description 点击滚动条同步更新音乐进度
  3. */
  4. const clickProgressSync = (event: MouseEvent) => {
  5. if (audioEle.value) {
  6. // 保证是正在播放或者已经播放的状态
  7. if (!audioEle.value.paused || audioEle.value.currentTime !== 0) {
  8. const pgsWrapperWidth = pgsWrapperEle.value!.getBoundingClientRect().width;
  9. const rate = event.offsetX / pgsWrapperWidth;
  10. // 同步滚动条和播放进度
  11. audioEle.value.currentTime = audioEle.value.duration * rate;
  12. updateProgress();
  13. }
  14. }
  15. };
  16. onMounted({
  17. pgsWrapperEle.value = document.querySelector('.progress-wrapper');
  18. // 点击进度条 event
  19. pgsWrapperEle.value?.addEventListener('mousedown', clickProgressSync, false);
  20. });
  21. // 别忘记统一移除侦听
  22. onBeforeUnmount(() => {
  23. audioEle.value?.removeEventListener('timeupdate', updateProgress);
  24. audioEle.value?.removeEventListener('ended', audioPlayEnded);
  25. pgsWrapperEle.value?.removeEventListener('mousedown', clickProgressSync);
  26. });

实现点击圆点拖拽滚动条

思路:使用

  1. hook
管理这个拖动的功能,侦听这个滚动条的 鼠标点击、鼠标移动、鼠标抬起事件。

点击时: 获取鼠标在窗口的

  1. x
坐标,圆点距离窗口的
  1. left
距离及最大的右移距离(滚动条宽度 - 圆点距离窗口的
  1. left
)。为了让移动式不随便开始计算,在开始的时候可以弄一个开关
  1. flag

移动时: 实时获取鼠标在窗口的

  1. x
坐标减去 点击时获取的
  1. x
坐标。然后根据最大移动距离做出判断,不要让它越界。最后: 总时长 * (圆点距离窗口的
  1. left
+ 计算得出的
  1. x
/ 滚动条长度) 得出百分比更新滚动条及进度

抬起时:将

  1. flag
重置。
  1. /**
  2. * @method useSongProgressDrag
  3. * @param audioEle
  4. * @param pgsWrapperEle
  5. * @param updateProgress 更新滚动条方法
  6. * @param startSongDragDot 是否开启拖拽滚动
  7. * @description 拖拽更新歌曲播放进度
  8. */
  9. const useSongProgressDrag = (
  10. audioEle: Ref<HTMLAudioElement | null>,
  11. pgsWrapperEle: Ref<HTMLDivElement | null>,
  12. updateProgress: () => void,
  13. startSongDragDot: Ref<boolean>
  14. ) => {
  15. const audioPlayer = ref<HTMLDivElement | null>(null);
  16. const audioDotEle = ref<HTMLDivElement | null>(null);
  17. const dragFlag = ref(false);
  18. const position = ref({
  19. startOffsetLeft: 0,
  20. startX: 0,
  21. maxLeft: 0,
  22. maxRight: 0,
  23. });
  24. /**
  25. * @description 鼠标点击 audioPlayer
  26. */
  27. const mousedownProgressHandle = (event: MouseEvent) => {
  28. if (audioEle.value) {
  29. if (!audioEle.value.paused || audioEle.value.currentTime !== 0) {
  30. dragFlag.value = true;
  31. position.value.startOffsetLeft = audioDotEle.value!.offsetLeft;
  32. position.value.startX = event.clientX;
  33. position.value.maxLeft = position.value.startOffsetLeft;
  34. position.value.maxRight = pgsWrapperEle.value!.offsetWidth - position.value.startOffsetLeft;
  35. }
  36. }
  37. event.preventDefault();
  38. event.stopPropagation();
  39. };
  40. /**
  41. * @description 鼠标移动 audioPlayer
  42. */
  43. const mousemoveProgressHandle = (event: MouseEvent) => {
  44. if (dragFlag.value) {
  45. const clientX = event.clientX;
  46. let x = clientX - position.value.startX;
  47. if (x > position.value.maxRight)
  48. x = position.value.maxRight;
  49. if (x < -position.value.maxLeft)
  50. x = -position.value.maxLeft;
  51. const pgsWidth = pgsWrapperEle.value?.getBoundingClientRect().width;
  52. const reat = (position.value.startOffsetLeft + x) / pgsWidth!;
  53. audioEle.value!.currentTime = audioEle.value!.duration * reat;
  54. updateProgress();
  55. }
  56. };
  57. /**
  58. * @description 鼠标取消点击
  59. */
  60. const mouseupProgressHandle = () => dragFlag.value = false;
  61. onMounted(() => {
  62. if (startSongDragDot.value) {
  63. audioPlayer.value = document.querySelector('.audio-player');
  64. audioDotEle.value = document.querySelector('.progress-dot');
  65. // 在捕获中去触发点击 dot 事件. fix: 点击原点 offset 回到原点 bug
  66. audioDotEle.value?.addEventListener('mousedown', mousedownProgressHandle, true);
  67. audioPlayer.value?.addEventListener('mousemove', mousemoveProgressHandle, false);
  68. document.addEventListener('mouseup', mouseupProgressHandle, false);
  69. }
  70. });
  71. onBeforeUnmount(() => {
  72. if (startSongDragDot.value) {
  73. audioPlayer.value?.removeEventListener('mousedown', mousedownProgressHandle);
  74. audioPlayer.value?.removeEventListener('mousemove', mousemoveProgressHandle);
  75. document.removeEventListener('mouseup', mouseupProgressHandle);
  76. }
  77. });
  78. };

最后调用这个

  1. hook
  1. // 是否显示可拖拽 dot
  2. // 可以在原点元素上增加 v-if 用来判定是否需要拖动功能
  3. const startSongDragDot = ref(true);
  4. useSongProgressDrag(audioEle, pgsWrapperEle, updateProgress, startSongDragDot);

以上就是基于Vue3怎么编写一个简单的播放器的详细内容,更多关于基于Vue3怎么编写一个简单的播放器的资料请关注九品源码其它相关文章!