鸿蒙小案例-动态歌词

embedded/2024/9/25 10:33:13/

之前有个播放器要显示歌词,但没找到鸿蒙中现成的组件,只能摸索着自己写一个
先看下效果

原理其实很简单
首先布局什么的就不多说了,歌词显示这块肯定是要全部显示的,主要操作难点在于怎么根据播放时长动态跳转到歌词位置
在这里我们用个List组件包裹,并且绑定scroller滚动条,循环显示所有歌词,当播放到指定位置时,通过scrollToIndex方法跳到歌词的这一行由此实现动态歌词
拿到歌词后转换成两个长度一致的数组,分别为时间数组,歌词数组,他们的下标应该是一一对应的,
播放时长随时变动,一变动就去时间数组寻找最接近的那个值,拿到下标,然后用这个下标去用scroller的scrollToIndex方法跳到歌词的这一行
全局常量

lrcScroller: Scroller = new Scroller() //歌词滚动条
@State lrcKeys: number[] = [] //歌词时间数据
@State lrcValues: string[] = [] //歌词具体数据
@Watch('upPlayStatus')
@State playStartTime: number = 0 //当前播放起始时间
@State upPlayScrollerIndex: number = 0 //歌词滚动索引
@State upPlayIndex: number = 0 //歌词显示索引

显示代码

List({ initialIndex: 1, scroller: this.lrcScroller }) {ForEach(this.lrcValues, (item: string, index: number) => {ListItem() {Column() {Row() {Text(item).fontColor(this.upPlayIndex === index ? '#00AE68' : '#000000').fontSize(15).maxLines(3).textOverflow({ overflow: TextOverflow.Ellipsis }).padding(5).fontWeight(this.upPlayIndex === index ? FontWeight.Bold : FontWeight.Normal).textAlign(TextAlign.Center)}.borderRadius(5).backgroundColor('#F3F3FA').margin({ bottom: 5 })}.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center).width('100%')}})}.animation({ duration: 500 })

其中
this.lrcValues=歌词数组,结构为string
item = 具体的每一行的歌词,结构为string
index = 歌词数组的下标,结构为number
this.upPlayIndex = 当前播放的时间在歌词数组中的下标

歌词+时间获取代码

//获取歌词async queryLrcById() {let lrcMap: Map<string, string[] | number[]> = await CommUtils.queryLrcById(this.PlayState.id)this.lrcKeys = lrcMap.get('key') as number[]this.lrcValues = lrcMap.get('value') as string[]}

this.PlayState.id = 当前播放歌曲id
CommUtils.queryLrcById = 根据id获取歌词数据,返回的是两个数组,Map结构
this.lrcKeys = 时间数组
this.lrcValues = 歌词数组
这个方法应该是在你点击一个按钮,然后要显示歌词时调用

动态调整歌词

//显示歌词附带的滚动效果upPlayStatus() {if (this.lrcStatus && this.lrcKeys.length > 0) {const indexStr = CommUtils.findClosestNumber(this.lrcKeys, this.playStartTime)setTimeout(() => {this.upPlayIndex = this.lrcKeys.indexOf(indexStr)const index = this.upPlayIndex - 4 < 0 ? 0 : this.upPlayIndex - 4if (index > this.upPlayScrollerIndex) {this.lrcScroller.scrollToIndex(index)this.upPlayScrollerIndex = index}}, 1000)}}

this.playStartTime = 当前播放的歌曲的已播放时长
index = scroller需要滚动到的行数/下标
this.upPlayScrollerIndex = 上一个已经跳动的下标
通过@Watch装饰器来实现调用
为什么会有下面这块代码呢

if (index > this.upPlayScrollerIndex) {.......  }

因为歌词数据并不是连续的,它是跳着来的,有时候歌曲的伴奏,哼唱这些都是没有歌词的,但是我们的获取最相近下标那个方法是没做区分的,会有很多重复的index出现,所以我们在需要跳动前记录下跳动的坐标,在下一次跳动前加个判断,只有大于上一个下标的时候,我再去跳,小于等于的话就不去跳,节省下消耗
为什么会有定时器呢
因为获取最相近的下标并不太精准,我们延时一下再跳
为什么会有下标 减 4 呢?
因为list的scroller的scrollToIndex方法会直接置顶显示,我们减4 它就始终处于屏幕正中央(这里说明一下,要根据外围包裹组件的具体高度来调整,一屏能显示几行歌词,这里就减去 总行数除以2,否则它不会显示在正中间)
findClosestNumber方法如下

//获取一个数组中跟传入值最相近的数值static findClosestNumber(arr: number[], target: number) {return arr.reduce((p, c) => (Math.abs(target - c) < Math.abs(target - p) ? c : p))}

arr = 时间数组
target = 当前播放时间

到这里就基本上实现动态歌词的功能,当然还有一些细节需要自行去优化下
比如:
切歌后,这个index的值要等于0,滚动条要滚动到0,数组要等于0等。。。

//切歌时重置歌词相关组件clearLrcCom() {this.upPlayScrollerIndex = 0this.upPlayIndex = 0this.lrcValues = []this.lrcKeys = []this.lrcScroller.scrollToIndex(0)}

这个方法是要在切歌时,隐藏歌词时 调用


http://www.ppmy.cn/embedded/15906.html

相关文章

前端vue+xgVIdeo集成rstp流播放

注意&#xff1a;rstp流需要对应的西瓜视频插件 项目&#xff1a; petition-manager 代码概览&#xff1a; 1. video-player 子 组件 <template><div id"video-player" class"video-player"></div> </template> <script&g…

第十四届蓝桥杯ABD题

A、阶乘求和&#xff1a; 【问题描述】 令 S 1! 2! 3! ... 202320232023! &#xff0c;求 S 的末尾 9 位数字。 提示&#xff1a;答案首位不为 0 。 【答案提交】 这是一道结果填空的题&#xff0c;你只需要算出结果后提交即可。本题的结果为一 个整数&#xff0c;在…

Golang | Leetcode Golang题解之第44题通配符匹配

题目&#xff1a; 题解&#xff1a; func isMatch(s string, p string) bool {for len(s) > 0 && len(p) > 0 && p[len(p)-1] ! * {if charMatch(s[len(s)-1], p[len(p)-1]) {s s[:len(s)-1]p p[:len(p)-1]} else {return false}}if len(p) 0 {retur…

typecho博客的相对地址实现

typecho其中的博客地址,必须写上绝对地址,否则在迁移网址的时候会出现问题,例如页面记载异常 修改其中的 typecho\var\Widget\Options\General.php 中的165行左右, /** 站点地址 */if (!defined(__TYPECHO_SITE_URL__)) {$siteUrl new Form\Element\Text(siteUrl,null,$this-…

网络协议深度解析:SSL、 TLS、HTTP和 DNS(C/C++代码实现)

在数字化时代&#xff0c;网络协议构成了互联网通信的基石。SSL、TLS、HTTP和DNS是其中最关键的几种&#xff0c;它们确保了我们的数据安全传输、网页的正确显示以及域名的正常解析。 要理解这些协议&#xff0c;首先需要了解网络分层模型。SSL和TLS位于传输层之上&#xff0c…

华纳云:怎么防止租用服务器的数据丢失?

要防止租用服务器上的数据丢失&#xff0c;可以采取以下一些措施&#xff1a; 定期备份数据&#xff1a;建立定期备份数据的机制&#xff0c;将重要数据备份到安全的地方&#xff0c;例如云存储服务、外部硬盘或者另一个服务器上。备份频率可以根据数据的重要性和变动频率来确定…

代码随想录算法训练营day34

题目&#xff1a;860.柠檬水找零、406.根据身高重建队列、452. 用最少数量的箭引爆气球 参考链接&#xff1a;代码随想录 860.柠檬水找零 思路&#xff1a;本题思路比较容易想&#xff0c;主要对于5,10,20三种情况分别讨论&#xff0c;维护三个变量分别记录目前手中三种钞票…

Spring Boot 加载本地 JAR 包的技术实践

随着微服务架构的兴起&#xff0c;Spring Boot 因其快速构建、易于部署的特性&#xff0c;成为了众多开发者的首选框架。在开发过程中&#xff0c;我们有时需要引入一些不在公共 Maven 仓库中的 JAR 包作为依赖&#xff0c;这时候就需要加载本地的 JAR 包。本文将详细介绍在 Sp…