前端一周一个小玩意儿(一):摇摇乐版音乐播放器

一. 前言

完整代码链接:https://github.com/CHANGsome/awesome-demos

最近看到这篇文章 Reddit | 最糟糕的音量控制设计大赛…
其中一个作品是这样的:
在这里插入图片描述
感觉这个摇摇乐挺有意思的,所以自己做了一个:
在这里插入图片描述
做完之后觉得应该用起来才完整,又做了个音乐播放器:
在这里插入图片描述
可以,面目全非,谁能想到这个摇杆是用来调音量的呢~

二. 摇摇乐实现思路

摇摇乐本身的实现并不复杂,主要有两个问题需要解决:

  1. 鼠标拖拽时让杆子动起来。
  2. 记住摇过的圈数。

1. 实现拖拽摇动杆子的动画

先解决第一个问题,怎么让杆子跟着鼠标动?
从图中可以看出来,摇杆在绕着底部旋转,所以只要知道旋转角度就好了。

transform: `rotate(${current_deg}deg)`

监听鼠标的mousemove事件,在鼠标每次移动的时候实时计算更新当前的旋转角度current_deg

当前旋转角度的正切 tan deg = det_x / det_y
det_x = 鼠标x坐标 - 坐标原点x坐标
det_y = 鼠标y坐标 - 坐标原点y坐标
其中,坐标值都根据离视口左上角的距离计算

在这里插入图片描述

2. 旋转圈数计数

实现用它来控制媒体音量的话,设定是:

  • 顺时针旋转一圈,圈数+1,音量+1
  • 逆时针旋转一圈,圈数-1,音量-1

我们让杆子每次经过 y 轴正半轴的时候算是一圈结束,此时的旋转角度 deg = 0
理论上来讲我们只要判断:

let circles = 0; // 旋转的圈数
handler.onmousemove=()=>{
	...
	// 如果是顺时针方向旋转
	if(current_deg === 0){
		circles ++;
	}
	...
}

但是这里存在一个问题:浏览器会限制事件的触发频率,onmousemove的触发是有间隔时间的。所以经常取不到旋转角度 current_deg 刚好为 0 的时候,所以这个时候需要委婉一点判断。

判断是否顺时针方向转了一圈的条件:

  1. 什么时候圈数+1:
    current_deg < last_deg,即旋转角度从 359 度变为 1 度的时候,在顺时针旋转的时候因为角度一直时递增的(0 度增到趋近于 360 度),所以 current_deg 小于 last_deg 的时候就是从第四象限转到第一象限的时候。
  2. 判断顺时针方向旋转:
    (1)当前鼠标的 x 坐标大于上一次的 x 坐标:不是充分必要条件,因为 onmousemove 绑定 在 document 上,有时候拖拽鼠标会往回移动。
    (2)所以加一个条件一起判断是否顺时针:current_deg < 10,只在离 y 正半轴10度的地方判断。

判断是否逆时针方向转了一圈的条件:

  1. 什么时候圈数-1:current_deg > last_deg,即从第一象限转到第四象限的时候。
  2. 判断逆时针方向旋转:
    (1)当前鼠标的 x 坐标小于上一次的 x 坐标
    (2)360 - current_deg < 10

至此,最难的部分解决了,剩下的就是怎么把它放在一个音乐播放器上了。

三. 自制一个音乐播放器

HTML 本身有 audio 标签可以用来播放音乐,但是直接使用的话有自定义样式,为了将上面的音量控制旋转杆用上去,这里重新封装 audio 标签。
具体实现:

  1. 新建一个 audio 元素,不带任何自定义控件,此时在页面上什么都不显示。
  <audio
        src={audioSrc}
        preload="true"
      >
        您的浏览器不支持 audio 标签。
      </audio>
  1. 需要用到的 audio 的属性:

duration 音频总长度
currentTime 音频当前播放时间
volume 当前音频音量

  1. 需要用到的 audio 的几个事件:

canplay 当浏览器可以播放音频/视频时
pause 当音频/视频已暂停时
play 当音频/视频已开始或不再暂停时
timeupdate 当目前的播放位置已更改时
其他就是很简单的逻辑,点击某个按钮触发相应的事件。

四. 总结

使用该音量控制组件的时候有一个问题:当摇动杆子时监听 document 的 mousemove 事件,此时会触发其他区域的文字和内容的选中和 hover 效果,所以需要禁止掉。

解决方案:设置一个变量 isMouseMove 来记录mousemove事件是否被触发,当被触发的时候设置以下 css 样式:

user-select: none;	// 禁止内容选中
pointerEvents: 'none';	// 禁止鼠标其他事件

五. 参考链接

  1. 灵感来源:https://zhuanlan.zhihu.com/p/27373842
  2. 设计参考:https://codepen.io/himalayasingh/pen/QZKqOX
  3. Audio 组件封装参考:
    https://blog.csdn.net/qq_40610760/article/details/98503537
    https://www.html.cn/qa/react/20626.html
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值