当前位置: 首页 > news >正文

自造简易版音频进度条

最近在做音乐播放器页面, 积累了很多有趣的经验, 今天先分享播放进度条的开发过程.

效果

话不多说,先看效果

支持点击修改进度,拖拽修改进度,当然大家肯定都知道ui库里面有现成的,为何要自己造一个

首先著名的ui库中确实都要这样的滑动输入条,比如antd-mobile中的slider

官网:https://mobile.ant.design/zh/components/slider/

效果很多

比我自己造的肯定功能丰富的多,但是亲自试过之后,发现效果不太友好,下面可以看看使用antd-mobile中的slider效果如下:

对比

这是antd-mobile的效果

代码如下:

其实把value属性去掉,这个组件就会丝滑很多,但是音乐播放器,需要随着音频播放,更改进度条,这是必须要的功能,不能去掉。

<div className={styles.process}><div className={styles.processTime}>{currentTime ? formatTime(currentTime) : '00:00'}</div><SliderclassName={styles.songSlider}defaultValue={0}onAfterChange={changeProgressValue}value={currentTime && duration ? currentTime / duration * 100 : 0}icon={<div className={styles.sliderDot} />}/>{/* <MusicSliderclassName={styles.songSlider}defaultValue={currentTime && duration ? currentTime / duration * 100 : 0}onAfterChange={change}value={currentTime && duration ? currentTime / duration * 100 : 0}/> */}<div className={styles.processTime}>{duration ? formatTime(duration) : '00:00'}</div>
</div>

changeProgressValue事件就是修改音频的currentTime

除此之外,我也试用了其他的依赖库,比如react-slider

https://github.com/zillow/react-slider

但是效果依旧不好,就是因为有这种两三点的滑动,所以导致逻辑复杂,滑动效果就不太丝滑

所以,我就觉得自己造一个,slider组件

点击修改进度

点击事件比较简单,就需要给这个区域绑定点击事件

灰色区域是父元素,红色元素是子元素,红色元素的宽度就是歌曲当前播放进度比分比 * 父元素宽度

首先,需要理清楚几个坐标,如何确定点击这里是音频进度所占比分比

点击当前点的坐标,点击的时候,能够拿到;父元素的宽度,通过getBoundingClientRect().width也能拿到

父元素左边距离最左边的距离,也能拿到getBoundingClientRect().left,也就是下面这段距离。

所以最终的点击函数如下:

// 点击事件const barClick = (e: React.MouseEvent) => {// @ts-ignoreconst rect = mmProgress.current.getBoundingClientRect()const activeWidthVal = Math.min(rect.width, Math.max(0, e.clientX - rect.left))// @ts-ignoreconst progress = Math.floor(activeWidthVal / mmProgress.current.clientWidth * 100)setActiveWidth(progress)if (onAfterChange) {onAfterChange(progress)}}

拖拽修改进度

在电脑上,需要监听的是鼠标的mouseup和mouseMove事件

在移动端,需要监听的是touchend和touchmove事件

鼠标移动/触屏移动:需要更新进度条的百分比

鼠标弹起/触屏结束:需要更新歌曲的进度

开始事件能够直接绑定在进度小圆点上

开始时,需要获取到开始的坐标,并且存起来,方便移动事件计算

mouseDown

// 触摸开始事件const barDown = (e: React.TouchEvent) => {startX.current = e.touches[0].pageX// @ts-ignoreleftVal.current = mmProgressInner.current.clientWidthisDrag.current = true}
// 鼠标开始移动const barDown1 = (e: React.MouseEvent) => {startX.current = e.clientX// @ts-ignoreleftVal.current = mmProgressInner.current.clientWidthisDrag.current = true}// tsx
<div className={styles.sliderDot}onMouseDown={barDown1}onTouchStart={barDown}></div>

由于我直接绑定在了tsx元素上,为了防止ts报错,我就写了两个函数,因为两者的类型不同

类型错误如下:

mouseMove

鼠标移动,需要及时更新进度条的样式,也就是红色条的宽度

所以需要计算当前点击的坐标,和上面函数保持的开始移动坐标

然后就是计算百分比,更新样式

// 鼠标/触摸移动事件const barMove = (e: React.TouchEvent & React.MouseEvent) => {if (isDrag.current) {const endX = e.clientX || e.touches[0].pageXconst dist = endX - startX.current// @ts-ignoreconst activeWidthVal = Math.min(mmProgress.current.clientWidth, Math.max(0, leftVal.current + dist))// @ts-ignoreconst progress = Math.floor(activeWidthVal / mmProgress.current.clientWidth * 100)setActiveWidth(progress)dynamicState.current = progress}}

mouseUp

鼠标抬起,这个函数需要说一下,首先需要判断一下是否已经在鼠标抬起时完成了鼠标放下事件mouseDown

为什么呢?防止这两种情况

这两种情况,也会触发mouseMove和mouseUp事件,但是这两种情况都不可以修改进度

所以需要一个变量来判断是否是在小圆点处发生了mouseDown事件

 // 鼠标/触摸释放事件const barUp = () => {// 避免打开Playing组件时触发if (isDrag.current && onAfterChange) {// @ts-ignoreonAfterChange(dynamicState.current)}}

销毁事件

到这里已经接近尾声了,但是注意挂载了事件,需要销毁

useMount(() => {bindEvents()
})useUnmount(()=> {unbindEvents()})// 添加绑定事件const bindEvents = () => {// @ts-ignoremmProgress.current.addEventListener('mousemove', barMove)// @ts-ignoremmProgress.current.addEventListener('mouseup', barUp)// @ts-ignoremmProgress.current.addEventListener('touchmove', barMove)// @ts-ignoremmProgress.current.addEventListener('touchend', barUp)}// 移除绑定事件const unbindEvents = () => {if (mmProgress.current) {// @ts-ignoremmProgress.current.removeEventListener('mousemove', barMove)// @ts-ignoremmProgress.current.removeEventListener('mouseup', barUp)// @ts-ignoremmProgress.current.removeEventListener('touchmove', barMove)// @ts-ignoremmProgress.current.removeEventListener('touchend', barUp)}}

最后全部代码如下:


import classNames from 'classnames'
import { useEffect, useRef, useState } from 'react'
import styles from './index.module.scss'
import { useMount, useUnmount  } from 'ahooks';export default function MusicSlider(props: any) {const { className, defaultValue, onAfterChange, value } = propsconst [activeWidth, setActiveWidth] = useState(defaultValue)const dynamicState = useRef(0)const startX = useRef(0) // 记录最开始点击的x坐标const leftVal = useRef(0) // 记录当前已经移动的距离const isDrag = useRef(false) // 是否可以拖拽const mmProgress = useRef(null)const mmProgressInner = useRef(null)useMount(() => {bindEvents()})useEffect(() => {const progress = Math.floor(value)// @ts-ignoresetActiveWidth(progress)}, [value])useUnmount(()=> {unbindEvents()})// 添加绑定事件const bindEvents = () => {// @ts-ignoremmProgress.current.addEventListener('mousemove', barMove)// @ts-ignoremmProgress.current.addEventListener('mouseup', barUp)// @ts-ignoremmProgress.current.addEventListener('touchmove', barMove)// @ts-ignoremmProgress.current.addEventListener('touchend', barUp)}// 移除绑定事件const unbindEvents = () => {if (mmProgress.current) {// @ts-ignoremmProgress.current.removeEventListener('mousemove', barMove)// @ts-ignoremmProgress.current.removeEventListener('mouseup', barUp)// @ts-ignoremmProgress.current.removeEventListener('touchmove', barMove)// @ts-ignoremmProgress.current.removeEventListener('touchend', barUp)}}// 点击事件const barClick = (e: React.MouseEvent) => {// @ts-ignoreconst rect = mmProgress.current.getBoundingClientRect()const activeWidthVal = Math.min(rect.width, Math.max(0, e.clientX - rect.left))// @ts-ignoreconst progress = Math.floor(activeWidthVal / mmProgress.current.clientWidth * 100)setActiveWidth(progress)if (onAfterChange) {onAfterChange(progress)}}// 触摸开始事件const barDown = (e: React.TouchEvent) => {startX.current = e.touches[0].pageX// @ts-ignoreleftVal.current = mmProgressInner.current.clientWidthisDrag.current = true}
// 鼠标开始移动const barDown1 = (e: React.MouseEvent) => {startX.current = e.clientX// @ts-ignoreleftVal.current = mmProgressInner.current.clientWidthisDrag.current = true}// 鼠标/触摸移动事件const barMove = (e: React.TouchEvent & React.MouseEvent) => {if (isDrag.current) {const endX = e.clientX || e.touches[0].pageXconst dist = endX - startX.current// @ts-ignoreconst activeWidthVal = Math.min(mmProgress.current.clientWidth, Math.max(0, leftVal.current + dist))// @ts-ignoreconst progress = Math.floor(activeWidthVal / mmProgress.current.clientWidth * 100)setActiveWidth(progress)dynamicState.current = progress}}// 鼠标/触摸释放事件const barUp = () => {// 避免打开Playing组件时触发if (isDrag.current && onAfterChange) {// @ts-ignoreonAfterChange(dynamicState.current)}}return (<div className={classNames(className, styles.progress)} ref={mmProgress} onClick={barClick}><div className={styles.bar}></div><div className={styles.outer}></div><div className={styles.inner} ref={mmProgressInner} style={{width: `${activeWidth}%`}}><div className={styles.sliderDot}onMouseDown={barDown1}onTouchStart={barDown}></div></div></div>)
}

相关文章:

自造简易版音频进度条

最近在做音乐播放器页面, 积累了很多有趣的经验, 今天先分享播放进度条的开发过程. 效果 话不多说&#xff0c;先看效果 支持点击修改进度&#xff0c;拖拽修改进度&#xff0c;当然大家肯定都知道ui库里面有现成的&#xff0c;为何要自己造一个 首先著名的ui库中确实都要这…...

433MHz芯片在遥控应用市场中的优点

当涉及到简单的无线射频通信&#xff0c;433MHz芯片成为一种经济实惠且广泛应用的选择。以下是关于433MHz芯片的重点信息&#xff1a; 工作原理&#xff1a;433MHz芯片的工作原理是将数字信号转化为射频信号&#xff0c;并通过无线信道进行传输。在接收端&#xff0c;射频信号再…...

基于Bert+Attention+LSTM智能校园知识图谱问答推荐系统——NLP自然语言处理算法应用(含Python全部工程源码及训练模型)+数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境服务器环境 模块实现1. 构造数据集2. 识别网络3. 命名实体纠错4. 检索问题类别5. 查询结果 系统测试1. 命名实体识别网络测试2. 知识图谱问答系统整体测试 工程源代码下载其它资料下载 前言 这个项目充分利用了…...

慕尼黑主题活动!亚马逊云科技生成式AI全新解决方案,引领未来移动出行领域

IAA作为世界五大车展之一&#xff0c;一直对全球汽车产业的发展起着关键作用&#xff01;2023年9月5日在慕尼黑开幕的IAA MOBILITY 2023以“体验联动智慧出行”为主题&#xff0c;紧跟移动出行领域的前沿变化&#xff0c;将汇集整车企业、开发者、供应商、科技公司、服务提供商…...

android 离线语言合成(文字转语音)

1、基于开源MaryTTS https://github.com/AndroidMaryTTS/AndroidMaryTTS 目前查到的资料&#xff0c;不支持中文&#xff0c;只针对西方语种。 2、基于TensorFlowTTS 官方个地址&#xff1a;为 Android 构建 TensorFlow Lite 库 (google.cn) 所依赖包下载地址:Maven Centr…...

使用Fastchat部署vicuna大模型

FastChat是一个用于训练、提供服务和评估基于大型语言模型的聊天机器人的开放平台。其核心特点包括&#xff1a; 最先进模型&#xff08;例如 Vicuna&#xff09;的权重、训练代码和评估代码。一个分布式的多模型提供服务系统&#xff0c;配备 Web 用户界面和与 OpenAI 兼容的…...

【2023高教社杯】C题 蔬菜类商品的自动定价与补货决策 问题分析、数学模型及python代码实现

【2023高教社杯】C题 蔬菜类商品的自动定价与补货决策 1 题目 C题蔬菜类商品的自动定价与补货决策 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&…...

华为云云耀云服务器L实例评测|华为云云耀云服务器L实例评测使用

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…...

【DS思想+堆贪心】CF595div3 D2

Problem - D2 - Codeforces 题意&#xff1a; 思路&#xff1a; 大家都说这是典&#xff0c;但是我不懂怎么个典法&#xff0c;可能堆贪心都是这样做的吗&#xff0c;不懂 首先肯定要贪心&#xff0c;对于一个坏点&#xff0c;优先删除覆盖别的点多的 考虑nlogn做法&#x…...

2023-09-08 LeetCode每日一题(计算列车到站时间)

2023-09-08每日一题 一、题目编号 2651. 计算列车到站时间二、题目链接 点击跳转到题目位置 三、题目描述 给你一个正整数 arrivalTime 表示列车正点到站的时间&#xff08;单位&#xff1a;小时&#xff09;&#xff0c;另给你一个正整数 delayedTime 表示列车延误的小时…...

软考-高级-信息系统项目管理第四版(完整24章全笔记)

《信息系统项目管理师教程》&#xff08;第4版&#xff09;是由全国计算机专业技术资格考试办公室组织编写的考试用书&#xff0c;根据2022年审定通过的《信息系统项目管理师考试大纲》编写&#xff0c;对信息系统项目管理师岗位所要求的主要知识及应用技术进行了阐述。 《信息…...

华为Mate 60和iPhone 15选哪个?

最近也有很多朋友问我这个问题来着&#xff0c;首先两款手机定位都是高端机&#xff0c;性能和体验各有千秋&#xff0c;各自有自己的铁杆粉。 但是让人意想不到的是华为mate60近日在海外越来越受欢迎和追捧&#xff0c;甚至是引起了不少人的抢购&#xff0c;外观设计和…...

嵌入式Linux驱动开发(同步与互斥专题)(二)

一、自旋锁spinlock的实现 自旋锁&#xff0c;顾名思义&#xff1a;自己在原地打转&#xff0c;等待资源可用&#xff0c;一旦可用就上锁霸占它。 ① 原地打转的是CPU x&#xff0c;以后CPU y会解锁&#xff1a;这涉及多个CPU&#xff0c;适用于SMP系统&#xff1b; ② 对于单…...

Docker安装部署Nexus3作为内网镜像代理缓存容器镜像

Docker安装部署Nexus3作为内网镜像代理 一、背景描述 基础镜像比较小&#xff0c;仓库使用阿里云或者腾讯云拉取速度挺快&#xff0c;但是时光飞逝几年时间过去&#xff0c;再加上AI加持的情况下&#xff0c;有些镜像的大小已经接近20G&#xff01; 这种情况下不管是测试环境…...

SpringBoot工具库:解决SpringBoot2.*版本跨域问题

1.解决问题&#xff1a;When allowCredentials is true, xxxxxxx , using “allowedOriginPatterns“ instead 2.3版本跨域配置如下 /*** 跨域问题解决*/ Configuration public class CorsConfig implements WebMvcConfigurer {Overridepublic void addCorsMappings(CorsRegi…...

docker安装开发常用软件MySQL,Redis,rabbitMQ

Docker安装 docker官网&#xff1a;Docker: Accelerated Container Application Development docker镜像仓库&#xff1a;https://hub.docker.com/search?qnginx 官网的安装教程&#xff1a;Install Docker Engine on CentOS | Docker Docs 安装步骤 1、卸载以前安装的doc…...

C# Unity FSM 状态机

C# Unity FSM 状态机 使用状态机可以降低代码耦合性&#xff0c;并且可以优化代码可读性&#xff0c;方便团队协作等。 对于游戏开发内容来讲游戏开发的流程控制玩家动画都可以使用FSM有限状态机来实现。 1.FsmState 每个状态的基类&#xff0c;泛型参数表示所拥有者 publi…...

pytorch搭建squeezenet网络的整套工程,及其转tensorrt进行cuda加速

本来&#xff0c;前辈们用caffe搭建了一个squeezenet的工程&#xff0c;用起来也还行&#xff0c;但考虑到caffe的停更后续转trt应用在工程上时可能会有版本的问题所以搭建了一个pytorch版本的。 以下的环境搭建不再细说&#xff0c;主要就是pyorch&#xff0c;其余的需要什么p…...

【精读Uboot】SPL阶段的board_init_r详细分析

对于i.MX平台上的SPL来说&#xff0c;其不会直接跳转到Uboot&#xff0c;而是在SPL阶段借助BOOTROM跳转到ATF&#xff0c;然后再通过ATF跳转到Uboot。 board_init_f会初始化设备相关的硬件&#xff0c;最后进入board_init_r为镜像跳转做准备。下面是board_init_r调用的核心函数…...

canvas绘制渐变色三角形金字塔

项目需求:需要绘制渐变色三角形金字塔,并用折线添加标识 (其实所有直接用图片放上去也行,但是ui没切图,我也懒得找她要,正好也没啥事,直接自己用代码绘制算了,总结一句就是闲的) 最终效果如下图: (以上没用任何图片,都是代码绘制的) 在网上找了,有用canvas绘…...

CameraLink三种模式(Base/Medium/Full)信号传输差异对比与选型建议

CameraLink三种工作模式深度解析与工业选型实战指南 在工业视觉检测线上&#xff0c;一台高速运行的贴片机正以每分钟800次的速度捕捉元件位置。当工程师将相机从200万像素升级到800万像素时&#xff0c;原本稳定的图像突然出现随机噪点——这往往是CameraLink模式选择不当导致…...

从炸管到稳定运行:我的MOSFET应用避坑实录(附热设计、驱动电路实测数据)

从炸管到稳定运行&#xff1a;我的MOSFET应用避坑实录 去年夏天&#xff0c;当我设计的48V转12V DC-DC模块第三次在高温测试中炸毁时&#xff0c;实验室里弥漫的焦糊味终于让我意识到&#xff1a;MOSFET的应用远不是选个低Rds(on)就万事大吉。作为从业十年的电源工程师&#x…...

智慧树网课助手:智能化学习效率提升解决方案

智慧树网课助手&#xff1a;智能化学习效率提升解决方案 【免费下载链接】zhihuishu 智慧树刷课插件&#xff0c;自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 一、问题诊断&#xff1a;在线学习的效率困境与技术破局 1.1 …...

Legacy-iOS-Kit:让旧设备重获新生的开源解决方案

Legacy-iOS-Kit&#xff1a;让旧设备重获新生的开源解决方案 【免费下载链接】Legacy-iOS-Kit An all-in-one tool to restore/downgrade, save SHSH blobs, jailbreak legacy iOS devices, and more 项目地址: https://gitcode.com/gh_mirrors/le/Legacy-iOS-Kit 当你的…...

NAssistant上位机实战:从TOFSense数据解析到固件升级全流程

1. NAssistant上位机初识&#xff1a;连接TOFSense的起点 第一次打开NAssistant上位机时&#xff0c;那个简洁的灰色界面可能会让你觉得无从下手。别担心&#xff0c;我刚开始用的时候也是这样。这个由Nooploop开发的工具其实设计得非常直观&#xff0c;只是需要一点时间来熟悉…...

Qwen3-0.6B-FP8数据库智能查询:用自然语言生成SQL语句

Qwen3-0.6B-FP8数据库智能查询&#xff1a;用自然语言生成SQL语句 你有没有过这样的经历&#xff1f;面对一个数据库&#xff0c;明明知道数据就在里面&#xff0c;却因为不懂SQL而束手无策。想查“上个月哪个产品卖得最好”&#xff0c;或者“找出最近三个月复购率最高的客户…...

解锁Windows全版本安装自由:MediaCreationTool.bat实战指南

解锁Windows全版本安装自由&#xff1a;MediaCreationTool.bat实战指南 【免费下载链接】MediaCreationTool.bat Universal MCT wrapper script for all Windows 10/11 versions from 1507 to 21H2! 项目地址: https://gitcode.com/gh_mirrors/me/MediaCreationTool.bat …...

Tao-8k处理长文本技术详解:突破上下文窗口限制

Tao-8k处理长文本技术详解&#xff1a;突破上下文窗口限制 你是不是也遇到过这样的烦恼&#xff1f;想把一篇几十页的行业报告丢给AI&#xff0c;让它帮你总结要点&#xff0c;结果它告诉你“文本太长了&#xff0c;我处理不了”。或者&#xff0c;你希望AI能帮你分析一个完整…...

Java互联网大厂求职面试实录:Spring Boot、微服务与全栈技术深度解析

Java互联网大厂求职面试实录&#xff1a;Spring Boot、微服务与全栈技术深度解析 面试场景介绍 本文以互联网大厂面试为背景&#xff0c;通过严肃的面试官与搞笑的水货程序员“谢飞机”的对话&#xff0c;深入探讨Java求职者面试中常见的技术问题。涵盖Java SE、Jakarta EE、Sp…...

OpenClaw二次开发指南:Qwen3.5-9B模型适配与API扩展

OpenClaw二次开发指南&#xff1a;Qwen3.5-9B模型适配与API扩展 1. 为什么需要二次开发OpenClaw&#xff1f; 去年冬天&#xff0c;当我第一次尝试用OpenClaw对接本地部署的Qwen3.5-9B模型时&#xff0c;遇到了几个棘手问题&#xff1a;模型返回的JSON格式与框架预期不符、长…...