React + JavaScript 实现可拖拽进度条

React + JavaScript 实现可拖拽进度条

下面是一个使用 React (JavaScript) 实现的可拖拽时间进度条组件。

代码
js 复制代码
import React, { useState, useRef, useEffect } from 'react';
import './TimeProgressBar.css';

const TimeProgressBar = ({
  totalTime = 600, // 默认10分钟 (600秒)
  initialTime = 0,
  onChange,
}) => {
  const [currentTime, setCurrentTime] = useState(initialTime);
  const [isDragging, setIsDragging] = useState(false);
  const progressBarRef = useRef(null);

  // 格式化时间 (秒 → HH:MM:SS)
  const formatTime = (seconds) => {
    const hrs = Math.floor(seconds / 3600);
    const mins = Math.floor((seconds % 3600) / 60);
    const secs = Math.floor(seconds % 60);
    
    return [
      hrs.toString().padStart(2, '0'),
      mins.toString().padStart(2, '0'),
      secs.toString().padStart(2, '0')
    ].join(':');
  };

  // 计算进度百分比
  const getPercentage = () => {
    return Math.min((currentTime / totalTime) * 100, 100);
  };

  // 更新进度
  const updateProgress = (time) => {
    const newTime = Math.max(0, Math.min(time, totalTime));
    setCurrentTime(newTime);
    if (onChange) onChange(newTime);
  };

  // 处理进度条点击
  const handleProgressBarClick = (e) => {
    if (progressBarRef.current) {
      const rect = progressBarRef.current.getBoundingClientRect();
      const pos = (e.clientX - rect.left) / rect.width;
      updateProgress(pos * totalTime);
    }
  };

  // 处理拖拽开始
  const handleDragStart = (e) => {
    setIsDragging(true);
    e.preventDefault();
  };

  // 处理拖拽移动
  const handleDragMove = (e) => {
    if (!isDragging || !progressBarRef.current) return;
    
    const rect = progressBarRef.current.getBoundingClientRect();
    const pos = (e.clientX - rect.left) / rect.width;
    updateProgress(pos * totalTime);
  };

  // 处理拖拽结束
  const handleDragEnd = () => {
    setIsDragging(false);
  };

  // 添加/移除事件监听器
  useEffect(() => {
    document.addEventListener('mousemove', handleDragMove);
    document.addEventListener('mouseup', handleDragEnd);
    document.addEventListener('touchmove', handleDragMove);
    document.addEventListener('touchend', handleDragEnd);

    return () => {
      document.removeEventListener('mousemove', handleDragMove);
      document.removeEventListener('mouseup', handleDragEnd);
      document.removeEventListener('touchmove', handleDragMove);
      document.removeEventListener('touchend', handleDragEnd);
    };
  }, [isDragging]);

  return (
    <div className="time-progress-container">
      <div 
        className="progress-bar" 
        ref={progressBarRef}
        onClick={handleProgressBarClick}
      >
        <div 
          className="progress-fill" 
          style={{ width: `${getPercentage()}%` }}
        />
        <div 
          className="progress-thumb" 
          style={{ left: `${getPercentage()}%` }}
          onMouseDown={handleDragStart}
          onTouchStart={handleDragStart}
        />
      </div>
      <div className="time-display">
        <span className="current-time">{formatTime(currentTime)}</span>
        <span className="total-time">{formatTime(totalTime)}</span>
      </div>
    </div>
  );
};

export default TimeProgressBar;
CSS 样式
css 复制代码
.time-progress-container {
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
  padding: 10px 0;
}

.progress-bar {
  position: relative;
  height: 8px;
  background-color: #e0e0e0;
  border-radius: 4px;
  cursor: pointer;
  margin-bottom: 10px;
}

.progress-fill {
  position: absolute;
  height: 100%;
  background-color: #4285f4;
  border-radius: 4px;
  transition: width 0.1s ease;
}

.progress-thumb {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 16px;
  height: 16px;
  background-color: #4285f4;
  border-radius: 50%;
  cursor: grab;
  z-index: 2;
  transition: left 0.1s ease;
}

.progress-thumb:active {
  cursor: grabbing;
}

.time-display {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  color: #666;
}
使用
js 复制代码
import React from 'react';
import TimeProgressBar from './TimeProgressBar';

function App() {
  const handleTimeChange = (time) => {
    console.log('当前时间:', time);
  };

  return (
    <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
      <h2>视频播放器</h2>
      <TimeProgressBar 
        totalTime={720} // 12分钟
        initialTime={120} // 从2分钟开始
        onChange={handleTimeChange}
      />
    </div>
  );
}

export default App;
功能特点
  1. 完全可拖拽:可以通过鼠标或触摸拖动滑块
  2. 点击跳转:点击进度条任意位置可跳转到对应时间点
  3. 响应式设计:适应不同屏幕尺寸
  4. 平滑过渡:添加了CSS过渡效果
  5. 触摸支持:支持移动设备触摸操作
  6. 时间显示:显示当前时间和总时间
  7. 回调函数:提供onChange回调,当时间变化时触发
自定义选项

你可以通过以下props自定义组件:

  • totalTime: 设置总时间(秒)
  • initialTime: 设置初始时间(秒)
  • onChange: 时间变化时的回调函数
相关推荐
非专业程序员3 分钟前
HarfBuzz 实战:五大核心API 实例详解【附iOS/Swift实战示例】
前端·程序员
DreamMachine9 分钟前
Flutter 开发的极简风格音乐播放器
前端·flutter
前端老宋Running16 分钟前
前端防抖与节流一篇讲清楚
前端·面试
ejinxian19 分钟前
Rust UI 框架GPUI 与 Electron 的对比
前端·javascript·electron
小马哥learn21 分钟前
Vue3 + Electron + Node.js 桌面项目完整开发指南
前端·javascript·electron
znhy@12331 分钟前
CSS3属性(三)
前端·css·css3
凌泽34 分钟前
「让规范驱动代码」——我如何用 Cursor + Spec Kit 在5小时内完成一个智能学习分析平台的
前端
omnibots37 分钟前
瑞萨SDK编译linux时,make menuconfig报错
linux·服务器·前端·嵌入式硬件
魔云连洲40 分钟前
前端树形结构过滤算法
前端·算法
涔溪42 分钟前
在 Electron 框架中连接 OPC UA 服务器并读取 PLC 数据
服务器·javascript·electron