挑战用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;
相关推荐
剪刀石头布啊27 分钟前
设计一个多计时任务的任务管理功能
前端
ModyQyW1 小时前
vite-plugin-uni-manifest 更新了什么
前端·typescript·uni-app
Asize2 小时前
AI 协作开发新范式:我用 SDD 做了个排版 npm 包
前端·人工智能
kyriewen4 小时前
我把今年流传的前端 AI 面试题整理了一遍——4 类场景题+回答框架(附速查表)
前端·面试·程序员
计算机魔术师4 小时前
国产多模态模型正面硬刚Opus旗舰:差距从30%缩到3%
前端
风骏时光牛马4 小时前
程序员进阶:深度思考,解锁职场成长的底层逻辑
前端
IT_陈寒4 小时前
用了Proxy才发现以前的JavaScript白写了
前端·人工智能·后端
爱丶不疚5 小时前
Eval: Agent 说的 Eval 是什么?从单测、TDD 到 Sentry 聊起
前端·ai编程·vibecoding
求道於盲5 小时前
python中的类型标注
前端
计算机魔术师5 小时前
从硅谷测试到全球铺开,ChatGPT广告的10亿美元秘密
前端