之前写过一个音乐播放器项目,今天再给它完善一下,加一个歌词同步滚动。

先看效果图:

 

要做歌词同步滚动,我们首先需要的文件资源就是音乐文件和与之匹配的歌词文件。现在歌词文件不太好找,没关系,我们可以自己做。先来看本项目的歌词示例,文件格式是.lrc

[00:20.5]
[00:20.5]I walked through the door with you
[00:23.63]曾与你一起穿过那扇门
[00:23.63]The air was cold
[00:25.83]空气充满寒意
[00:25.83]But something 'bout it felt like home somehow and I
[00:31.41]不知为何却让我觉得温暖得像家一般
[00:31.41]Left my scarf there at your sister's house
[00:35.22]我把自己的围巾落在了你姐姐家

内容中除了要有歌词文本之外,还要有每一行歌词出现的时间点,大家按照这种格式创建就可以,就是一行一行写比较麻烦。

准备好之后把音乐和歌词放入rawfile文件夹中,播放音乐就不说了,我们今天只说歌词部分。

首先读取一下本地文件:

this.context .resourceManager.getRawFileContent('AllTooWell.lrc')  .then((value: Uint8Array) => {
let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true });
let stringData = textDecoder.decodeWithStream(value, { stream: false });
this.mLrcEntryList = parseLrcLyric(stringData);
})

然后后要把歌词和时间分离出来分别存储,方便进行滚动展示,这里使用正则表达式来提取时间部分:

const lrcLineRegex: RegExp = new RegExp('\\[\\d{2,}:\\d{2}((\\.|:)\\d{2,})\\]', 'g');
for (let i = 0; i < lyric.length; i++) {
let lineTime = lyric[i].match(lrcLineRegex);
let lineText = lyric[i].replace(lrcLineRegex, '');
if (lineTime && lineText) {
for (let j = 0; j < lineTime.length; j++) {
let min = Number(String(lineTime[j].match(lrcTimeRegex1)).slice(1));
let sec = Number.parseFloat(String(lineTime[j].match(lrcTimeRegex2)));
let timeInSeconds = (min * 60 + sec) * 1000;
lrc.push({ lineStartTime: timeInSeconds, lineDuration: 0, lineWords: lineText, words: [] });
}
}}

这样就得到了一个由时间和内容组成的数组。

接下来将歌词逐行展示,这里使用的是绘制画布的方式:

for (let i = 0; i < lyric.words.length; i++) {
let wordStartTime = lyric.lineStartTime + lyric.words[i].wordStartTime;
let wordEndTime = lyric.lineStartTime + lyric.words[i].wordStartTime + lyric.words[i].duration;
if (wordStartTime <= this.lyricMilliSecondsTime && wordEndTime >= this.lyricMilliSecondsTime) {
let wordProgress = (this.lyricMilliSecondsTime - wordStartTime) / lyric.words[i].duration / lyric.words.length; let wordPassedProgress = i / lyric.words.length; let progress = wordPassedProgress + wordProgress; this.context.fillStyle = this.progressGrad(startX, gradY, endX, gradY, progress);
this.context.font = this.fontWeight + ' ' + (this.mCurrentTextSize + this.TEXT_ADD_SIZE) + 'vp ' + this.fontFamily; this.context.fillText(lyric.words[i].text, wordX, this.lrcY + this.TEXT_ADD_SIZE / 2, this.lrcWidth);
} else { this.context.font = this.fontWeight + ' ' + this.mCurrentTextSize + 'vp ' + this.fontFamily;
this.context.fillText(lyric.words[i].text, wordX, this.lrcY, this.lrcWidth);
}
wordX += this.context.measureText(lyric.words[i].text).width;}

下面就是歌词同步滚动的问题,在播放音乐的时候肯定会有一个计时器,歌词滚动同样使用这个计时器,计时器计数改变时,找到对应的歌词,并计算该行歌词的行数和偏移量,进行画布滚动,就可以实现歌词的同步滚动了,我们还可以对歌词的透明度进行绘制,实现渐隐渐出的效果。

由于众所周知的原因,本项目就和大家分享一个大概的思路,有了思路这个功能就不是很难了,感谢您的阅读。

鸿蒙NEXT实战教程—实现音乐歌词同步滚动的更多相关文章

  1. Android VLC播放器二次开发3——音乐播放(歌曲列表+歌词同步滚动)

    今天讲一下对VLC播放器音频播放功能进行二次开发,讲解如何改造音乐播放相关功能.最近一直在忙着优化视频解码部分代码,因为我的视频播放器需要在一台主频比较低的机器上跑(800M主频),所以视频解码能力受 ...

  2. 我的Android进阶之旅------>Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能

    前言 一LRC歌词文件简介 1什么是LRC歌词文件 2LRC歌词文件的格式 LRC歌词文件的标签类型 1标识标签 2时间标签 二解析LRC歌词 1读取出歌词文件 2解析得到的歌词内容 1表示每行歌词内 ...

  3. 基于jplayer实现歌词同步的JS音乐播放器效果

    分享一款基于jplayer实现歌词同步的JS音乐播放器效果.这是一款基于jQuery实现的音乐播放器功能代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <textare ...

  4. 《ElasticSearch6.x实战教程》之实战ELK日志分析系统、多数据源同步

    第十章-实战:ELK日志分析系统 ElasticSearch.Logstash.Kibana简称ELK系统,主要用于日志的收集与分析. 一个完整的大型分布式系统,会有很多与业务不相关的系统,其中日志系 ...

  5. TextView实现歌词同步

    利用TextView实现歌词同步显示,这是一个简单的利用TextView实现滚动实时显示歌词的. 里面的内容都已经写上了详细的注释.里面播放音乐的时候歌词同步展示. 做媒体这块的朋友可以学习一下,练练 ...

  6. NDK-JNI实战教程(二) JNI官方中文资料

    声明 设计概述 JNI接口函数和指针 加载和链接本地方法 解析本地方法名 本地方法的参数 引用Java对象 全局和局部引用 实现局部引用 访问Java对象 访问基本类型数组 访问域和方法 报告编程错误 ...

  7. 用grunt搭建自动化的web前端开发环境实战教程(详细步骤)

    用grunt搭建自动化的web前端开发环境实战教程(详细步骤) jQuery在使用grunt,bootstrap在使用grunt,百度UEditor在使用grunt,你没有理由不学.不用!前端自动化, ...

  8. wpf 仿QQ音乐歌词卡拉OK

    最近用WPF做了个音乐播放器,读取歌词.歌词同步都已经实现了.卡拉OK逐字变色 也实现了,但是逐字变色时不能根据歌手唱的快慢来逐字显示.请问各位大神,这个如何解决,有何思路?(附上我做的界面) 感谢各 ...

  9. Node+Express+MongoDB + Socket.io搭建实时聊天应用实战教程(二)--node解析与环境搭建

    前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战.写教程一方面在自己写的过程中需要考虑更多的东西,另一方面希望能对node入门者有 ...

  10. Node+Express+MongoDB+Socket.io搭建实时聊天应用实战教程(一)--MongoDB入门

    前言 本文并不是网上流传的多少天学会MongoDB那种全面的教程,而意在总结这几天使用MongoDB的心得,给出一个完整的Node+Express+MongoDB+Socket.io搭建实时聊天应用实 ...

随机推荐

  1. FreeSql学习笔记——2.插入

    前言 由于还没有表结构,就先从新增开始,插入一些数据后才好做查询.修改.删除操作. 初始化 前面注入FreeSql时设置过自动同步表结构,那么就不用管数据库了,只需要在项目中定义实体,就会自动生成表结 ...

  2. 图解MySQL【日志】——Binlog

    Binlog(Binary Log,归档日志) 为什么需要 Binlog? Binlog 是 MySQL 中的二进制日志,用于记录数据库的所有写操作(INSERT.UPDATE.DELETE 等) 1 ...

  3. PD还是QC?快充协议全解析

    什么是快充协议 快充协议是一种通过提高充电效率来缩短设备充电时间的电池充电技术.它是通过在充电器和设备之间建立一种沟通机制,充电器能够根据设备的需求和状态,调整输出的电压和电流.这种沟通机制由快充协议 ...

  4. QT5笔记: 15. 其他显示组件的常用功能

    其他显示组件的常用功能 代码 #include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget ...

  5. 咨询公司:趁着AI人工智能的浪潮还能持续,好好享受吧……

    在人工生成式智能热潮的喧嚣与狂热之中,咨询行业正经历一场基本未被察觉却极具变革性的革命,这场变革将塑造它的未来. 传统咨询依赖于由高素质专业人士组成的团队,他们专注于研究.数据分析,并提供定制化建议. ...

  6. python实现批量自动访问站点URL并获取内容,自动模拟打开电脑端及移动端URL访问站点,打开URL页面获取页面内容

    问题描述:假设目前有多个网站URL,需要检查各站点keyword,description是否正常设置,如果人工逐个打开URL访问比较耗时,故采用python模拟电脑端和移动端自动打开网站URL访问,并 ...

  7. Caddy web服务器

    caddy 中文文档:https://caddy2.dengxiaolong.com/docs/ 常用命令 命令 描述 caddy run 启动Caddy服务器 caddy reload 重载Cadd ...

  8. css px em rem % vw vh vm 区别

    前言 在传统项目开发中,我们只会用到 px.%.em 这几个单位长度,它们可以适用大部分项目的开发,并且拥有较好的兼容性. 而从 css3 开始,浏览器对逻辑单位的支持又提升了新的境界,增加了 rem ...

  9. Flask快速入门3

    十一,Flask Cookies Cookie以文本文件的形式存储在客户端的计算机上.其目的是记住和跟踪与客户使用相关的数据,以获得更好的访问者体验和网站统计信息. Request对象包含Cookie ...

  10. 网站支持https之一:https原理和SSL证书类型

    1 https原理 https加密请求过程 Client和Server之间会进行一下几个步骤的交互: ① Client发送https请求: ② Client和Server通过tcp的三次握手建立连接, ...