最近在做的一个功能需要一个音频播放控件,但是浏览器自带的感觉有点丑
一开始的想法是用<input type="range">来做进度条,但是做完的感觉还是丑,而且很难用css美化
所以就想着干脆自己写一个,下面是基础功能的实现
<!DOCTYPE html>
<html>
<head>
<title>TEST</title>
<meta charset="utf-8">
<style>
* {margin: 0; padding: 0}
/* 进度条容器样式 */
.progress-bar {
position: relative;
height: 16px;
width: 100%;
background-color: white;
border: 1px solid;
border-color: black;
}
/* 进度条填充样式,border的颜色和进度条的背景颜色保持相同 */
.progress-bar .progress-fill {
position: absolute;
width: 0;
height: 100%;
box-sizing: border-box;
border: 2px solid white;
background-color: dodgerblue;
}
/* 滑块基础样式 */
.progress-bar .progress-handle {
position: absolute;
height: 100%;
aspect-ratio: 1 / 1;
background-color: azure;
box-sizing: border-box;
border: 1px solid;
border-radius: 50%;
}
/* 滑块悬浮样式 */
.progress-bar .progress-handle:hover {
transform: scale(1.25);
}
/* 滑块滑动样式 */
.progress-bar .progress-handle.dragging {
transform: scale(1.25);
}
</style>
</head>
<body>
<div style="width: calc(100% - 40px); margin: 20px">
<!-- 进度条 -->
<div id="bar" class="progress-bar">
<div id="fill" class="progress-fill">
<div id="handle" class="progress-handle"></div>
</div>
</div>
</div>
</body>
<script>
// 获取dom元素
const bar = document.getElementById('bar');
const fill = document.getElementById('fill');
const handle = document.getElementById('handle');
// 基础属性
var progress = 0;
var min = 0;
var max = 100;
var dragging = false; // 拖拽状态
// 将一个数在两个范围之间映射
function mapValue(value, min1, max1, min2, max2) {
return (value - min1) / (max1 - min1) * (max2 - min2) + min2;
}
// 把x截取到a与b之间
function clamp (x, a, b) {
return Math.max(a, Math.min(b, x));
}
// 禁用默认拖拽事件
function disableDefaultDrag (element) {
element.addEventListener('dragstart', event => event.preventDefault());
element.addEventListener('dragover', event => event.preventDefault());
element.addEventListener('drop', event => event.preventDefault());
}
disableDefaultDrag(bar);
disableDefaultDrag(fill);
disableDefaultDrag(handle);
// 设置进度条
function setProgress (value) {
progress = clamp(value, min, max);
updateView ();
}
// 根据拖动的坐标设置进度
function updateProgress (x) {
progress = mapValue(x, 0, bar.offsetWidth, min, max);
progress = clamp(progress, min, max);
updateView ();
}
// 更新视图方法
function updateView () {
const bw = bar.offsetWidth;
const hw = handle.offsetWidth;
// 计算百分比进度
const pp = mapValue(progress, min, max, 0, 1);
// 设置填充的width用来表示进度
// 添加滑块宽度相关的偏移让进度条看起来更自然
fill.style.width = `${bw*pp + hw*(0.5-pp)}px`;
// 设置滑块偏移,减去滑块宽度防止滑块飞出去
handle.style.left = `${(bw-hw)*pp}px`;
}
// 检测到左键按下时设置进度条
bar.addEventListener('mousedown', (event) => {
if (event.button === 0) {
// 如果没有点到滑块则设置进度,点到滑块无事发生
if (event.target !== handle) updateProgress(event.offsetX);
// 为滑块添加拖拽class,控制样式
handle.classList.add('dragging');
// 设置拖拽状态
dragging = true;
console.log('dragging!')
}
});
// 在轨道上拖拽时设置进度条
bar.addEventListener('mousemove', (event) => {
// 如果没有按下左键则还原
if (event.buttons === 0 || event.button !== 0) {
handle.classList.remove('dragging');
dragging = false;
return;
}
if (dragging) {
// 如果鼠标指针在滑块上要重新计算坐标
if (event.target === handle) updateProgress(event.offsetX + handle.offsetLeft);
else updateProgress(event.offsetX);
}
});
window.addEventListener('load', () => {
updateView ();
});
</script>
</html>
最终效果:
简单封装一下就可以用了,样式随便调