Heim  >  Artikel  >  Web-Frontend  >  Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit Vue3 einen Player schreiben

Bringen Sie Ihnen Schritt für Schritt bei, wie Sie mit Vue3 einen Player schreiben

藏色散人
藏色散人nach vorne
2023-03-03 15:53:432269Durchsuche

Dieser Artikel vermittelt Ihnen relevantes Wissen über Vue3. Es geht hauptsächlich darum, wie man einen Player mit Vue3 schreibt. Ich hoffe, dass er für alle hilfreich ist.

ps: Musik kann möglicherweise nicht abgespielt werden. Der Grund dafür ist, dass die Audioverbindung temporär ist und manuell ersetzt werden kann.

TODO

  • Start-/Endzeit und dynamische Änderungen der Bildlaufleiste entsprechend der Wiedergabedynamik realisieren;
  • Klicken Sie auf den Fortschritt Leiste Zur angegebenen Wiedergabeposition springen;
  • Klicken Sie auf den Punkt und ziehen Sie die Bildlaufleiste.
  • Das Seitenlayout und der css-Stil sind wie folgt
<template>
  <div class="song-item">
    <audio src="" />
    <!-- 进度条 -->
    <div class="audio-player">
      <span>00:00</span>
      <div class="progress-wrapper">
        <div class="progress-inner">
          <div class="progress-dot" />
        </div>
      </div>
      <span>00:00</span>
      <!-- 播放/暂停 -->
      <div style="margin-left: 10px; color: #409eff; cursor: pointer;" >
        播放      </div>
    </div>
  </div></template><style lang="scss">
  * { font-size: 14px; }  .song-item {    display: flex;    flex-direction: column;    justify-content: center;    height: 100px;    padding: 0 20px;    transition: all ease .2s;    border-bottom: 1px solid #ddd;    /* 进度条样式 */
    .audio-player {      display: flex;      height: 18px;      margin-top: 10px;      align-items: center;      font-size: 12px;      color: #666;      .progress-wrapper {        flex: 1;        height: 4px;        margin: 0 20px 0 20px;        border-radius: 2px;        background-color: #e9e9eb;        cursor: pointer;        .progress-inner {          position: relative;          width: 0%;          height: 100%;          border-radius: 2px;          background-color: #409eff;          .progress-dot {            position: absolute;            top: 50%;            right: 0;            z-index: 1;            width: 10px;            height: 10px;            border-radius: 50%;            background-color: #409eff;            transform: translateY(-50%);
          }
        }
      }
    }
  }</style>

Play/Pause implementieren

css 样式如下

const audioIsPlaying = ref(false); // 用于同步当前的播放状态const audioEle = ref<HTMLAudioElement | null>(null); // audio 元素/**
 * @description 播放/暂停音乐
 */const togglePlayer = () => {  if (audioEle.value) {    if (audioEle.value?.paused) {
      audioEle.value.play();
      audioIsPlaying.value = true;
    }    else {
      audioEle.value?.pause();
      audioIsPlaying.value = false;
    }
  }
};onMounted(() => {  // 页面点击的时候肯定是加载完成了,这里获取一下没毛病
  audioEle.value = document.querySelector('audio');
});

实现播放/暂停

思路:给 ”播放“ 注册点击事件,在点击事件中通过 audio 的属性及方法来判定当前歌曲是什么状态,是否播放或暂停,然后声明一个属性同步这个状态,在模板中做出判断当前应该显示 ”播放/暂停“。

关键性 api:

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

audio.play():播放

audio.pause():暂停

<div 
  style="margin-left: 10px; color: #409eff; cursor: pointer;"
  @click="togglePlayer"> 
  {{ audioIsPlaying ? '暂停' : '播放'}}</div>

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

import dayjs from 'dayjs';const audioCurrentPlayTime = ref('00:00'); // 当前播放时长const audioCurrentPlayCountTime = ref('00:00'); // 总时长const pgsInnerEle = ref<HTMLDivElement | null>(null);/**
 * @description 更新进度条与当前播放时间
 */const updateProgress = () => {  const currentProgress = audioEle.value!.currentTime / audioEle.value!.duration;

  pgsInnerEle.value!.style.width = `${currentProgress * 100}%`;  // 设置进度时长
  if (audioEle.value)
    audioCurrentPlayTime.value = dayjs(audioEle.value.currentTime * 1000).format('mm:ss');
};/**
 * @description 播放完成重置播放状态
 */const audioPlayEnded = () => {
  audioCurrentPlayTime.value = '00:00';
  pgsInnerEle.value!.style.width = '0%';
  audioIsPlaying.value = false;
};onMounted(() => {
  pgsInnerEle.value = document.querySelector('.progress-inner');  
  // 设置总时长
  if (audioEle.value)
    audioCurrentPlayCountTime.value = dayjs(audioEle.value.duration * 1000).format('mm:ss');    
  // 侦听播放中事件
  audioEle.value?.addEventListener('timeupdate', updateProgress, false);  // 播放结束 event
  audioEle.value?.addEventListener('ended', audioPlayEnded, false);
});

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

思路:获取当前已经播放的时间及总时长,然后再拿当前时长 / 总时长及得到歌曲播放的百分比即滚动条的百分比。通过侦听 audio 元素的 timeupdate 事件以做到每次当前时间改变时,同步把 DOM 也进行更新。最后播放完成后把状态初始化。

关键性api:

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

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

timeupdatecurrentTime 变更时会触发该事件。

/**
 * @description 点击滚动条同步更新音乐进度
 */const clickProgressSync = (event: MouseEvent) => {  if (audioEle.value) {    // 保证是正在播放或者已经播放的状态
    if (!audioEle.value.paused || audioEle.value.currentTime !== 0) {      const pgsWrapperWidth = pgsWrapperEle.value!.getBoundingClientRect().width;      const rate = event.offsetX / pgsWrapperWidth;      // 同步滚动条和播放进度
      audioEle.value.currentTime = audioEle.value.duration * rate;      updateProgress();
    }
  }
};onMounted({
  pgsWrapperEle.value = document.querySelector('.progress-wrapper');  // 点击进度条 event
  pgsWrapperEle.value?.addEventListener('mousedown', clickProgressSync, false);
});// 别忘记统一移除侦听onBeforeUnmount(() => {
  audioEle.value?.removeEventListener('timeupdate', updateProgress);
  audioEle.value?.removeEventListener('ended', audioPlayEnded);
  pgsWrapperEle.value?.removeEventListener('mousedown', clickProgressSync);
});

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

思路:给滚动条注册鼠标点击事件,每次点击的时候获取当前的 offsetX 以及滚动条的宽度,用宽度 / offsetX 最后用总时长 * 前面的商就得到了我们想要的进度,再次更新进度条即可。

关键性api:

event.offsetX:鼠标指针相较于触发事件对象的 x 坐标。

/**
 * @method useSongProgressDrag
 * @param audioEle
 * @param pgsWrapperEle
 * @param updateProgress 更新滚动条方法
 * @param startSongDragDot 是否开启拖拽滚动
 * @description 拖拽更新歌曲播放进度
 */const useSongProgressDrag = (
  audioEle: Ref<HTMLAudioElement | null>,
  pgsWrapperEle: Ref<HTMLDivElement | null>,
  updateProgress: () => void,
  startSongDragDot: Ref<boolean>) => {  const audioPlayer = ref<HTMLDivElement | null>(null);  const audioDotEle = ref<HTMLDivElement | null>(null);  const dragFlag = ref(false);  const position = ref({    startOffsetLeft: 0,    startX: 0,    maxLeft: 0,    maxRight: 0,
  });  /**
   * @description 鼠标点击 audioPlayer
   */
  const mousedownProgressHandle = (event: MouseEvent) => {    if (audioEle.value) {      if (!audioEle.value.paused || audioEle.value.currentTime !== 0) {
        dragFlag.value = true;

        position.value.startOffsetLeft = audioDotEle.value!.offsetLeft;
        position.value.startX = event.clientX;
        position.value.maxLeft = position.value.startOffsetLeft;
        position.value.maxRight = pgsWrapperEle.value!.offsetWidth - position.value.startOffsetLeft;
      }
    }
    event.preventDefault();
    event.stopPropagation();
  };  /**
   * @description 鼠标移动 audioPlayer
   */
  const mousemoveProgressHandle = (event: MouseEvent) => {    if (dragFlag.value) {      const clientX = event.clientX;      let x = clientX - position.value.startX;      if (x > position.value.maxRight)
        x = position.value.maxRight;      if (x < -position.value.maxLeft)
        x = -position.value.maxLeft;      const pgsWidth = pgsWrapperEle.value?.getBoundingClientRect().width;      const reat = (position.value.startOffsetLeft + x) / pgsWidth!;

      audioEle.value!.currentTime = audioEle.value!.duration * reat;      updateProgress();
    }
  };  /**
   * @description 鼠标取消点击
   */
  const mouseupProgressHandle = () => dragFlag.value = false;  onMounted(() => {    if (startSongDragDot.value) {
      audioPlayer.value = document.querySelector('.audio-player');
      audioDotEle.value = document.querySelector('.progress-dot');      // 在捕获中去触发点击 dot 事件. fix: 点击原点 offset 回到原点 bug
      audioDotEle.value?.addEventListener('mousedown', mousedownProgressHandle, true);
      audioPlayer.value?.addEventListener('mousemove', mousemoveProgressHandle, false);      document.addEventListener('mouseup', mouseupProgressHandle, false);
    }
  });  onBeforeUnmount(() => {    if (startSongDragDot.value) {
      audioPlayer.value?.removeEventListener('mousedown', mousedownProgressHandle);
      audioPlayer.value?.removeEventListener('mousemove', mousemoveProgressHandle);      document.removeEventListener('mouseup', mouseupProgressHandle);
    }
  });
};

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

思路:使用 hook 管理这个拖动的功能,侦听这个滚动条的 鼠标点击、鼠标移动、鼠标抬起事件。

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

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

抬起时:将 flag 重置。

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

最后调用这个 hookIdee: Registrieren Sie ein Klickereignis für „play“, verwenden Sie im Click-Ereignis die Attribute und Methoden von audio, um den Status des aktuellen Songs zu bestimmen, ob er abgespielt oder pausiert wird, deklarieren Sie dann ein Attribut, um diesen Status zu synchronisieren, und erstellen Sie ein Urteil in der Vorlage, dass das aktuelle Lied „Play/Pause“ anzeigen soll.

Schlüssel-API: audio.paused: Ob der aktuelle Player angehalten ist

audio.play(): Wiedergabe 🎜🎜audio.pause () : Pause🎜🎜rrreee🎜Zuletzt Attribute und Ereignisse auf die Vorlage anwenden. 🎜rrreee

Erkennen Sie die Start-/Endzeit, die Startzeit und die Bildlaufleiste, um der Wiedergabedynamik dynamisch zu folgen

🎜🎜Idee: Erhalten Sie die aktuelle Spielzeit und Gesamtdauer und Rufen Sie dann die aktuelle Dauer/Gesamtdauer ab und ermitteln Sie den Prozentsatz des gespielten Songs, der dem Prozentsatz der Bildlaufleiste entspricht. Durch das Abhören des timeupdate-Ereignisses des audio-Elements wird das DOM jedes Mal, wenn sich die aktuelle Zeit ändert, auch synchron aktualisiert. Nachdem die endgültige Wiedergabe abgeschlossen ist, wird der Status initialisiert. 🎜🎜Schlüssel-API: 🎜🎜audio.currentTime: aktuelle Wiedergabezeit(en) 🎜🎜audio.duration: Gesamtdauer der Audioeinheit(en) 🎜🎜 timeupdate: Dieses Ereignis wird ausgelöst, wenn sich currentTime ändert. 🎜🎜rrreee

Realisieren Sie das Klicken auf den Fortschrittsbalken, um zur angegebenen Wiedergabeposition zu springen

🎜🎜Idee: Registrieren Sie ein Mausklickereignis für die Bildlaufleiste und rufen Sie den aktuellen ab offsetX klicken, und die Breite der Bildlaufleiste, verwenden Sie width / offsetX und verwenden Sie schließlich die Gesamtdauer *, um den gewünschten Fortschritt zu erhalten, und aktualisieren Sie einfach den Fortschritt Bar wieder. 🎜🎜Schlüssel-API: 🎜🎜event.offsetX: Die x-Koordinate des Mauszeigers im Vergleich zum auslösenden Ereignisobjekt. 🎜🎜rrreee

Klicken Sie auf den Punkt, um die Bildlaufleiste zu ziehen.

🎜🎜Idee: Verwenden Sie hook, um diese Ziehfunktion zu verwalten und die Mausklick-, Mausbewegungs- und Maushubereignisse dieser Bildlaufleiste abzuhören. 🎜🎜Beim Klicken: Erhalten Sie die x-Koordinaten der Maus im Fenster, den Abstand zwischen dem Punkt und der linken des Fensters und die maximale Rechtsbewegungsentfernung (Bildlaufleiste). Breite – der Abstand zwischen dem Punkt und dem Fenster) links). Um zu verhindern, dass das Mobiltelefon versehentlich mit der Berechnung beginnt, können Sie zu Beginn einen Schalter flag setzen🎜🎜Beim Bewegen: Holen Sie sich die x-Koordinaten der Maus im Fenster Echtzeit abzüglich des Klicks. Die x-Koordinate von . Treffen Sie dann eine Beurteilung auf der Grundlage der maximalen Bewegungsdistanz und lassen Sie nicht zu, dass die Grenze überschritten wird. Schließlich: Gesamtdauer * (Punktabstand von links des Fensters + berechnetes x / Länge der Bildlaufleiste) Der Prozentsatz wird ermittelt, um die Bildlaufleiste und den Fortschritt beim Anheben zu aktualisieren :Flag zurücksetzen. 🎜🎜rrreee🎜Rufen Sie diesen Hook endlich auf 🎜rrreee🎜[Verwandte Empfehlung: 🎜vue.js Video-Tutorial🎜]🎜🎜

Das obige ist der detaillierte Inhalt vonBringen Sie Ihnen Schritt für Schritt bei, wie Sie mit Vue3 einen Player schreiben. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.im. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen