Cet article vous apporte des connaissances pertinentes sur Vue3. Il explique principalement comment écrire un lecteur avec Vue3. Les amis intéressés peuvent jeter un œil ci-dessous. J'espère que cela sera utile à tout le monde.
ps : La lecture de la musique peut échouer. La raison en est que le lien audio est temporaire et peut être remplacé manuellement.
TODO
css
sont les suivants
<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>
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)
timeupdate
:currentTime
变更时会触发该事件。
/** * @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);
最后调用这个 hook
audio
pour déterminer l'état de la chanson en cours, qu'elle soit en lecture ou en pause, puis déclarez un attribut pour synchroniser cet état, et faites un jugement dans le modèle selon lequel la chanson actuelle doit afficher "Play/pause".
audio.paused
: indique si le lecteur actuel est en pause audio.play()
: lecture 🎜🎜audio.pause ()
: Pause🎜🎜rrreee🎜Enfin, appliquez les attributs et les événements au modèle. 🎜rrreeetimeupdate
de l'élément audio
, chaque fois que l'heure actuelle change, le DOM est également mis à jour de manière synchrone. Une fois la lecture finale terminée, l'état est initialisé. 🎜🎜API clé : 🎜🎜audio.currentTime
: durée de lecture actuelle ; unité(s) 🎜🎜audio.duration
: durée totale de l'unité(s) audio 🎜🎜 timeupdate
: cet événement est déclenché lorsque currentTime
change. 🎜🎜rrreeeoffsetX
et la largeur de la barre de défilement, utilisez width / offsetX
et enfin utilisez la durée totale * pour obtenir la progression souhaitée, et mettez simplement à jour la progression bar à nouveau. 🎜🎜API clé : 🎜🎜event.offsetX
: La coordonnée x du pointeur de la souris par rapport à l'objet événement déclencheur. 🎜🎜rrreeehook
pour gérer cette fonction de déplacement et écouter les événements de clic de souris, de mouvement de la souris et de soulèvement de la souris de cette barre de défilement. 🎜🎜Lorsque vous cliquez : Obtenez les coordonnées x
de la souris dans la fenêtre, la distance entre le point et la gauche
de la fenêtre et la distance maximale de déplacement à droite (barre de défilement largeur - la distance entre le point et la fenêtre) gauche
). Afin d'éviter que le mobile ne démarre le calcul par hasard, vous pouvez définir un commutateur drapeau
au début🎜🎜Lors du déplacement : Récupérez les coordonnées x
de la souris dans la fenêtre dans temps réel moins le clic. La coordonnée x
de . Ensuite, faites un jugement en fonction de la distance de déplacement maximale et ne la laissez pas sortir des limites. Enfin : Durée totale * (distance du point depuis gauche
de la fenêtre + x
calculé / longueur de la barre de défilement) Le pourcentage est obtenu pour mettre à jour la barre de défilement et progresser🎜🎜lorsqu'elle est levée :Réinitialiser le drapeau
. 🎜🎜rrreee🎜Enfin, appelez ce hook
🎜rrreee🎜[Recommandation associée : 🎜tutoriel vidéo vue.js🎜]🎜🎜Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!