挑战用React封装100个组件【008】

项目地址
https://github.com/hismeyy/react-component-100

组件描述

这次的组件有点简单,这个卡片是可以控制视频播放的,用于展示一些比较小的视频动画。

样式展示

代码展示

VideoCard.tsx
js 复制代码
import { useRef, useState } from 'react'
import './VideoCard.css'

interface VideoCardProps {
    videoSrc: string;            
    title: string;                
    onMoreClick?: () => void;      
}

const VideoCard = ({ 
    videoSrc, 
    title, 
    onMoreClick
}: VideoCardProps) => {
    const videoRef = useRef<HTMLVideoElement>(null)
    const [isPlaying, setIsPlaying] = useState(false)

    const togglePlay = () => {
        if (videoRef.current) {
            if (isPlaying) {
                videoRef.current.pause()
            } else {
                videoRef.current.play()
            }
            setIsPlaying(!isPlaying)
        }
    }

    const handleVideoEnded = () => {
        setIsPlaying(false)
    }

    return (
        <div className="video-card">
            <div className="video">
                <video 
                    ref={videoRef}
                    src={videoSrc}
                    onEnded={handleVideoEnded}
                ></video>
            </div>
            <div className='line'></div>
            <div className='info'>
                <div className='video-info'>
                    <div className="title">{title}</div>
                </div>
                <div className='video-functions'>
                    <button className='play' onClick={togglePlay}>
                        {isPlaying ? '暂停' : '播放'}
                    </button>
                    <button className='more' onClick={onMoreClick}>
                        更多
                    </button>
                </div>
            </div>
        </div>
    )
}

export default VideoCard
VideoCard.css
js 复制代码
.video-card{
    width: 240px;
    height: 95px;
    box-sizing: border-box;
    background-color: #FEECBA;
    border-radius: 20px;
    display: flex;
    justify-content: left;
    align-items: center;
    padding: 5px 5px 5px 15px;
    box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1)
}

.video-card .video{
    width: 70px;
    height: 70px;
    border-radius: 50%;
    background-color: pink;
    overflow: hidden;
    display: flex;
    justify-content: center;
    align-items: center
}

.video-card .video video{
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.video-card .line{
    width: 2px;
    height: 60px;
    background-color: rgb(211, 211, 211);
    margin-left: 20px;
    margin-right: 20px;
    border-radius: 2px;
}

.video-card .info{
    display: flex;
    flex-direction: column;
    gap: 15px;
}

.video-card .info .video-info{
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 16px;
    font-weight: bold;
    color: #4d4d4d;
}

.video-card .info .video-functions{
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 10px;
}

.video-card .info .video-functions button{
    all: unset;
    padding: 5px 10px;
    border-radius: 20px;
    cursor: pointer;
    background-color: #4d4d4d;
    color: #ffffff;
    font-size: 12px;
    font-weight: bold;
    transition: all 0.3s ease;
    position: relative;
    overflow: hidden;
}

.video-card .info .video-functions button:hover {
    transform: translateY(-2px);
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}

.video-card .info .video-functions button:active {
    transform: translateY(0);
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}

.video-card .info .video-functions button::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100%;
    height: 100%;
    background: rgba(255, 255, 255, 0.2);
    transform: translate(-50%, -50%) scale(0);
    border-radius: inherit;
    transition: transform 0.3s ease;
}

.video-card .info .video-functions button:active::after {
    transform: translate(-50%, -50%) scale(2);
    opacity: 0;
}

.video-card .info .video-functions button:first-child{
    background-color: #b57bf8;
}

.video-card .info .video-functions button:last-child{
    background-color: #f08a5d;
}

使用

App.tsx
js 复制代码
import VideoCard from './components/card/videoCard01/VideoCard'
import './App.css'

function App() {
  const handleMoreClick = () => {
    console.log('查看更多信息');
  };

  return (
    <div className="App">
      <VideoCard
        videoSrc="https://www.w3schools.com/html/mov_bbb.mp4"
        title="萌宠日常"
        onMoreClick={handleMoreClick}
      />
    </div>
  );
}

export default App;
相关推荐
五点六六六5 小时前
基于 AST 与 Proxy沙箱 的局部代码热验证
前端·设计模式·架构
发现一只大呆瓜7 小时前
SSO单点登录:从同域到跨域实战
前端·javascript·面试
发现一只大呆瓜7 小时前
告别登录中断:前端双 Token无感刷新
前端·javascript·面试
Cg136269159748 小时前
JS-对象-Dom案例
开发语言·前端·javascript
无限大69 小时前
《AI观,观AI》:善用AI赋能|让AI成为你深耕核心、推进重心的“最强助手”
前端·后端
烛阴9 小时前
Claude Code Skill 从入门到自定义完整教程(Windows 版)
前端·ai编程·claude
lxh01139 小时前
数据流的中位数
开发语言·前端·javascript
神仙别闹9 小时前
基于NodeJS+Vue+MySQL实现一个在线编程笔试平台
前端·vue.js·mysql
zadyd10 小时前
Workflow or ReAct ?
前端·react.js·前端框架
北寻北爱12 小时前
vue2和vue3使用less和scss
前端·less·scss