演示


需要注意:引入vue.js与lyric-parser.js
lyric-parser.js地址:https://github.com/ustbhuangyi/lyric-parser/blob/master/src/index.js
因为是网易云的歌词,与这个库解析的歌词不适配,有个地方改一下就行了

代码

<!DOCTYPE html>
<html lang="en"><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>Document</title>
</head><body>
<div id="app"><audio ref="audioRef" autoplay @canplay='canplay' @timeupdate='update'></audio><button @click="play">播放</button><button @click="pause">暂停</button><div class="progress-wrapper"><span class="time time-l">{{formatTime(currentTime)}}</span><div class="progress-bar-wrapper"><cpn :progress=progress@progress-changing="onProgressChanging"@progress-changed='progressChanged'></cpn></div><span class="time time-l">{{formatTime(duration)}}</span></div><div class="lyric-container" ref="lyricContainer"><div class="cotnent" ref="lyricListRef"><p class="text" v-for="(line, index) in currentLyric.lines" :class="{'current': currentLineNum ===index}">{{line.txt}}</p></div></div>
<!--    <button @click="startScroll">点我开始</button>--></div><!-- 子组件 -->
<template id="myCpn"><div class="progress-bar"><!-- 后面黑色的一条 --><div class="bar-inner" @click="clickProgress"><!-- 已经播放的区域 --><div class="progress" :style='progressStyle' ref="progress"></div><!-- btn --><div class="progress-btn-wrapper" :style='btnStyle' @touchstart.preventDefault='onTouchStart'@touchmove.preventDefault='onTouchMove' @touchend.preventDefault='onTouchEnd'><div class="progress-btn"></div></div></div></div>
</template><script src="../../js/vue.js"></script>
<script type="module">import Lyric from "./lyricParser.js"let audioEl;let lyricEl;let lyricListEl = nullaudioEl = nulllyricEl = nullconst progressBtnWidth = 16// 子组件const cpn = {template: "#myCpn",props: {progress: {type: Number,default: 0}},data() {return {offset: 0}},mounted() {},created() {this.touch = {}},computed: {progressStyle() {return `width: ${this.offset}px`},btnStyle() {// console.log('fds');return `transform: translate3d(${this.offset}px,0,0)`},},watch: {progress(newProgress) {// 进度条宽度const barWidth = this.$el.clientWidth - progressBtnWidththis.offset = barWidth * newProgress},},methods: {onTouchStart(e) {// console.log(e);this.touch.x1 = e.changedTouches[0].clientX// 黄色进度条初始宽度this.touch.beginWidth = this.$refs.progress.clientWidthconsole.log(this.touch);},onTouchMove(e) {// console.log(e);// x偏移量const delta = e.changedTouches[0].clientX - this.touch.x1// 之前的width+这次拖动增加的偏移量=应有的黄条长度const tempWidth = this.touch.beginWidth + delta// 再拿到barWidthconst barWidth = this.$el.clientWidth - progressBtnWidth// 黄条长度/barwidth = progress 现在应该有的进度const progress = tempWidth / barWidththis.offset = barWidth * progressthis.$emit('progress-changing', progress)},onTouchEnd(e) {// console.log(e);const barWidth = this.$el.clientWidth - progressBtnWidthconst progress = this.$refs.progress.clientWidth / barWidththis.$emit('progress-changed', progress)},// 点击进度条clickProgress(e) {console.log('getBoundingClientRect', this.$el.getBoundingClientRect());const rect = this.$el.getBoundingClientRect()// 黄条应有的宽度const offsetWidth = e.pageX - rect.xconst barWidth = this.$el.clientWidth - progressBtnWidth// const progress = offsetWidth/barWidthconst progress = Math.min(1, Math.max(offsetWidth / barWidth, 0))this.$emit('progress-changed', progress)// console.log(offsetWidth)}},}const app = new Vue({el: "#app",data: {content: 'fdasdf',// 天外来物src: 'https://music.163.com/song/media/outer/url?id=1463165983.mp3',currentTime: 0,duration: 0,isplay: false,progressChanging: false,lyric: "[00:00.000] 作词 : 薛之谦\n[00:01.000] 作曲 : 罗小黑\n[00:02.000] 编曲 : 周以力\n[00:03.000] 制作人 : 周以力/郑伟\n[00:18.042]你降落的 太突然了\n[00:24.240]我刚好呢 又路过了\n[00:32.325]机会难得 又主观觉得\n[00:38.725]想明抢 又碰不得\n[00:46.157]你带来了 我的快乐\n[00:53.157]让这世界 有点颜色\n[01:00.444]我好想指责 你太随意了\n[01:06.991]宝物该有人捧着  你是不是我的\n[01:17.333]你像 天外来物一样 求之不得\n[01:24.269]你在世俗里的名字 不重要了\n[01:31.516]正好 我隐藏的人格是锲而不舍\n[01:38.299]直到蜂拥而至的人都透明了\n[01:45.943]我在 不近又不远处\n[01:49.925]用明天换你 靠近我\n[02:07.554]你占领了 我的快乐\n[02:14.406]和这世界 已没有瓜葛\n[02:21.639]任事物干渴  都褪去颜色\n[02:28.340]只有你是天蓝色  我开始找你了\n[02:40.569]会像 天外来物一样 失而复得\n[02:47.769]你在世俗里的名字  被人用了\n[02:54.662]反正 我隐藏的人格是锲而不舍\n[03:01.730]直到蜂拥而至的人都透明了\n[03:08.793]我在 不近又不远处\n[03:12.651]用明天换你 靠近我\n[03:18.765]你就像 天外来物一样 求之不得\n[03:26.717]我在世俗里的描写被取笑了\n[03:33.681]反正我隐藏的人格是非你不可\n[03:40.352]直到别有用心的人都透明了\n[03:47.895]我在 不近又不远处\n[03:52.044]用明天换你 靠近我\n[03:54.560] 吉他 : 张淞\n[03:57.076] 大提琴 : 郎莹\n[03:59.592] 鼓 : 褚伟明\n[04:02.108] 贝斯 : 努而德柯\n[04:04.624] 人声录音 : 郑伟 夏之炜 吴身宝\n[04:07.140] 人声编辑 : 郑伟\n[04:09.656] 人声录音室 : 上海广播大厦200studio\n[04:12.172] 乐器录音棚 : soundhub studio\n[04:14.688] 混音 : 全相彦 @OK master studio\n[04:17.204] 母带 : 全相彦 @OK master studio\n",currentLyric: '',// 当前播放行数  用来设置classcurrentLineNum: 0},components: {cpn},mounted() {// 解析歌词this.currentLyric = new Lyric(this.lyric, this.handleLyric)this.$nextTick(() => {audioEl = this.$refs.audioRefaudioEl.src = this.srcaudioEl.volume = 0.1console.log(this.$refs.audioRef)// 默认暂停audioEl.pause()lyricEl = this.$refs.lyricContainerlyricListEl = this.$refs.lyricListRefconsole.log("lyricListEl", lyricListEl)// this.currentLyric = new Lyric(this.lyric, this.handleLyric)})// console.log(window)},computed: {progress() {return this.currentTime / this.durationconsole.log("progress", this.currentTime / this.duration);},},methods: {play() {audioEl.play()this.isplay = truethis.playLyric()},pause() {console.log("pause")audioEl.pause()this.isplay = false// console.log();this.stopLyric()},canplay(e) {// console.log(123456);// console.log(e);console.log("canplay")this.duration = e.target.duration},update(e) {if (!this.progressChanging) {this.currentTime = e.target.currentTime}// console.log(this.currentTime)},onProgressChanging(e) {// console.log("onProgressChanging", e);this.progressChanging = true// 实时修改currentTime值this.currentTime = this.duration * econsole.log(this.currentTime)this.stopLyric()},progressChanged(e) {this.stopLyric()// console.log(e);this.progressChanging = falseaudioEl.currentTime = this.currentTime = this.duration * eif (!this.isplay) {console.log("------");audioEl.play()}// this.currentLyric.play(0)// this.currentLyric.stop()this.playLyric()},// -----------歌词有关-------------// 歌词滚动条handleLyric({lineNum, txt}) {// console.log(this.currentLyric)// console.log(lineNum, txt)this.currentLineNum = lineNumif (!lyricListEl) {return}if (lineNum > 3) {const lineEl = lyricListEl.children[0]// 拿到行的高度const lineHeight= lineEl.clientHeight// 滚动到哪里呢  到一行高度*当前的行-3行lyricEl.scrollTo({top: lineHeight*(lineNum-3),left: 0,behavior: 'smooth'});}},// 测试滚动功能// startScroll() {//   // console.log(1)//   setInterval(() => {//     lyricEl.scrollTop += 42//   }, 1000)// },// 播放歌词playLyric() {// clearTimeout()console.log(this.currentLyric.lines)console.log("播放了")console.log(this.currentLyric)this.currentLyric.seek(this.currentTime * 1000)},stopLyric() {console.log("暂停了")if (this.currentLyric) {this.currentLyric.stop()}},formatTime(interval) {// interval 向下取整interval = interval | 0// 不足两位的话就向前填充一个0let minute = ((interval / 60 | 0) + '')let second = ((interval % 60 | 0) + '')let len = minute.lengthfor (; len < 2; len++) {minute = '0' + minute}len = second.lengthfor (; len < 2; len++) {second = '0' + second}return `${minute}:${second}`}},})
</script>
</body>
<style>#app {width: 100%;}.progress-wrapper {display: flex;width: 80%;padding: 10px 0;align-items: center;margin: 0 auto;}.time {width: 40px;flex: 0 0 40px;font-size: 8px;margin: 0 auto;padding: 0 8px;}.time-l {text-align: left;}.time-l {text-align: right;}.progress-bar-wrapper {flex: 1;}/* 子组件样式 */.progress-bar {height: 30px;}.bar-inner {position: relative;top: 11px;height: 8px;background-color: rgba(87, 82, 82, 0.062);border-radius: 5px;}.progress {position: absolute;height: 100%;background-color: rgb(238, 238, 136);}.progress-btn-wrapper {position: absolute;left: -8px;top: -11px;width: 30px;height: 30px;}.progress-btn {position: relative;top: 7px;left: 7px;box-sizing: border-box;width: 16px;height: 16px;border: 3px solid rgb(189, 189, 218);border-radius: 50%;background: rgb(123, 192, 212);}.lyric-container {padding: 0px;width: 300px;height: 300px;background-color: antiquewhite;overflow: scroll;}::-webkit-scrollbar {width: 0px;height: 0px;}p {display: block;margin: 0;padding: 10px;text-align: center;box-sizing: border-box;}.current {color: white;}
</style></html>

解说

来看一下

在音乐加载完之后(不应该在这里实例化,后面有改)

handleLyric 给currentLine赋值 用来高亮

点击暂停和播放

播放音乐中

playLyric

页面

但是会发现有时候高亮会有延迟,这是因为lyric-parser的问题

修改方法

https://github.com/ustbhuangyi/lyric-parser/issues/11

这样就ok了

但是发现个bug

它为什么会跳到前面

试了一下,不拖动的话是没有问题的,但是一旦拖动或者点击再点暂停,暂停功能就会失效

为什么呢 因为如果拖动的话canplay是会触发的,所以可能创建了很多个实例,开了很多定时器(可以看lyric-parse的源码),上一个还没被销毁就又创建了一个,导致上一个还没有关掉 ,所以不应该在canplay里面实例化,还有这里是只有一首歌,如果有多首歌进行切换的时候记得先stop一下,要不然还是会有这个问题

再来整理一下
实例化

playLyric与stopLyric

用到playLyric和stopLyric的地方

这样高亮功能就完成了,接下来完成自动滚动

希望的效果:前5行,不动,接下来第六行:当前减去前五行保证一直在中间的位置

获取外层滚动的盒子el,和滚动盒子里面内容的el

获取外层滚动盒子的el是为了能够滚动,获取里面的内容是为了拿到里面的子元素,获取一行的dom,计算出它的高度

我又改了一下样式,再来整理一下这个功能,因为p标签它自动会加上margin ,上下的margin还是重叠的,获取height的时候还不会加上margin的值,很麻烦,所以吧margin去掉了,换成了padding

web-api scrollTo的用法

https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo

vue歌词随着歌曲播放滚动demo 使用lyric-parser库解析歌词相关推荐

  1. 仿网易云网页版音乐播放器,实现歌词随歌曲进行滚动高亮

    引言 前几天在使用网易云网页版听歌时,看着那个页面的歌词随歌曲进行高亮,突然也想自己手动地去实现一下,于是呢,就仿照了网易云音乐的网页自己也写了个页面.效果图如下: 当然了,此处不做css的样式介绍, ...

  2. vue项目:歌词随歌曲同步滚动

    封装成对象,暴露出去共享使用 1.新建js文件 2.因为需要用到元素运动来实现歌词自上而下滚动的效果,又不想引用jquery增大js的加载,所有在对象内部封装了一个运动函数. 3.主要的函数是添加定时 ...

  3. 编写一个歌词随音乐播放滚动的播放器

    提问:你平常都是如何听歌和下载歌曲的?官网?软件? 简介:作为计算机的爱好者,对计算的使用已经是非常熟悉了解的了,当然对编程语言也是有些许了解.听周围人说,无法听取某些歌曲或下载某些歌曲,身为脚本小子 ...

  4. vue实现歌词随音乐同步滚动

    请求回来的歌词是以下格式 第一步先处理歌词 if (this.lyricList.lyric) { //判断有没有歌词,因为有些音乐没有歌词arr = this.lyricList.lyric.spl ...

  5. js 实现音乐播放器中歌词与歌曲同步的原理

    首先知道歌词文件的后缀名是 .lrc (lyric歌词的缩写): 接着知道.lrc 文件的一般格式是 [** : ** . ***] 歌词内容(时间的tag标签组成): =============== ...

  6. Vue+Vuex+Element-ui实现歌曲播放控件、播放列表功能

    写在前面: 根据思路写代码和写文章的区别还是挺大的,后者更多需要的是一个总结表述的能力,以及需要判断文章中呈现的东西是否有价值.做笔记和写博客同样是在记录,而博客还承担了一个供别人浏览的职能,所以如何 ...

  7. Android 实现歌曲播放时歌词同步显示

    我们需要读取以上歌词文件的每一行转换成成一个个歌词实体: public class LyricObject { public int begintime; // 开始时间 public int end ...

  8. JavaScript实时监听歌曲播放进度显示对应歌词

    前言 在之前我就想试试在线的音乐播放器的制作,昨晚动手实现了播放音乐的歌词实时对应显示的组件,下面就来看看其中的解析原理. 正文 这里我以李玉刚的<刚好遇见你>为例,首先我们需要获取到音频 ...

  9. 网易云歌词解析(配合audio标签实现本地歌曲播放,歌词同步)

    先看下效果 github上做的一个音乐播放器: https://github.com/SorrowX/electron-music 中文歌曲 英文歌曲(如果有翻译的中文给回返回出去) 韩文歌曲 来看下 ...

最新文章

  1. 完美解决网站PNG图标在IE6下的透明显示
  2. PHP 缓存插件之 Zend Opcache ( 取代 APC )
  3. 计算机用户要以ADSL,2012年计算机一级MsOffice第三十三套练习题及答案解析
  4. golang中strings.ToUpper
  5. ajax 入参为list_ajax传递给后台数组参数方式
  6. [JS-DOM]核心DOM模型(Document,Element,Node)
  7. ccf命令行选项只能用c实现_CCF-201403-3-命令行选项
  8. 直击WinRoute
  9. redis实现分布式锁代码片段
  10. python中函数的返回值
  11. 基于R软件的网状meta分析
  12. 一看就明白的超标量超流水线超线程简介
  13. 电机瞬态过程分析的MATLAB建模与仿,电机瞬态过程分析的MATLAB建模与仿真
  14. word中取消链接上一节在哪_在WORD中怎样取消与上一节相同
  15. sql是什么mysql是什么意思_sql是什么意思
  16. 【图像处理】记一次粗心:未加载opencv_world300d.dll
  17. NE5532DR IC OPAMP GP 2 CIRCUIT 8SOIC
  18. root用户执行sourc /etc/profile命令提示权限不够
  19. QQ信任登录(PC端 )申请
  20. 本地存储Cookie、Storage、indexDB、ServiceWork离线访问网站

热门文章

  1. 微商引流海报怎么设置?什么样的海报更容易传播
  2. 分布式全文搜索引擎ElasticSearch
  3. 两台外网计算机远程桌面访问(内网穿透)
  4. 认识选购计算机配件的主要内容,认识和选购计算机配件键鼠.ppt
  5. jQuery动态生成的元素如何绑定事件
  6. 信息加密(古典密码学)
  7. 怎样求解逻辑回归算法的损失函数?
  8. 逻辑回归损失函数为啥不用最小二乘法
  9. 【数学建模笔记】2.整数规划
  10. java substring 异常_String.subString引发的StringIndexOutOfBoundsException