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: 时间变化时的回调函数
相关推荐
小兔崽子去哪了10 分钟前
微信小程序入门
前端·vue.js·微信小程序
独立开阀者_FwtCoder14 分钟前
# 白嫖千刀亲测可行——200刀拿下 Cursor、V0、Bolt和Perplexity 等等 1 年会员
前端·javascript·面试
AI程序员罗尼18 分钟前
React SSR 水合(Hydration)详解
react.js
不和乔治玩的佩奇20 分钟前
【 React 】useState (温故知新)
前端
那小孩儿21 分钟前
?? 、 || 、&&=、||=、??=这些运算符你用对了吗?
前端·javascript
七月十二24 分钟前
[微信小程序]对接sse接口
前端·微信小程序
AI程序员罗尼25 分钟前
React 服务端渲染 (SSR) 详解
react.js
AI程序员罗尼25 分钟前
React useEffect 在服务端渲染中的执行行为
react.js
小七_雪球25 分钟前
Permission denied"如何解决?详解GitHub SSH密钥认证流程
前端·github
野原猫之助27 分钟前
tailwind css在antd组件中使用不生效
前端