一个简单的自定义可拖动进度条

最近在做的一个功能需要一个音频播放控件,但是浏览器自带的感觉有点丑

一开始的想法是用<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>

最终效果:

简单封装一下就可以用了,样式随便调

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值