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: 时间变化时的回调函数
相关推荐
kymjs张涛9 分钟前
零一开源|前沿技术周报 #8
前端·ios·kotlin
小小小小宇10 分钟前
React Hooks 的执行顺序
前端
Mintopia24 分钟前
🌌 构建宇宙不是梦:Three.js 中的多个子场景、LOD 切换与资源统一管理
前端·javascript·three.js
hxmmm25 分钟前
scss实现多主题样式切换
前端·css
腹黑天蝎座26 分钟前
怎么监听图片或者背景图片渲染完成
前端·图片资源
Mintopia29 分钟前
🌪️ 点云世界的炼金术:法线估计与泊松重建教程
前端·javascript·计算机图形学
邢同学爱折腾31 分钟前
grid布局实现旋转视差效果
前端·css
拾光拾趣录44 分钟前
前端主题色切换
前端·css
晴殇i1 小时前
浏览器原生模块化革命:Import Maps能否终结前端构建工具时代?
前端·javascript·面试
bug爱好者1 小时前
原生小程序如何使用css预处理器 + 定义全局样式
前端·javascript