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 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄3 天前
JavaScript 隐式全局变量解析
javascript
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大3 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师3 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端