Membangun a Custom Video Player dengan Claude Code
Learn about building a custom video player using Claude Code. Includes practical code examples.
カスタムvideoプレーヤー dengan Claude Code: 作る
デフォルト browservideoプレーヤー デザイン 統一 難しく、機能 juga 限定され.Claude Code 使えば、ブランド 合ったデザイン 、pemutaran速度変更・字幕・ピクチャーインピクチャー dll. 機能 備えたカスタムプレーヤー pembangunan bisa dilakukan.
要件 プレーヤー
> HTML5 Videoベース カスタムvideoプレーヤー buatkan.
> pemutaran/一時停止、シークバー、音量、pemutaran速度変更、
> フルスクリーン、ピクチャーインピクチャー、字幕tampilan support.
メインプレーヤーkomponen
// src/components/VideoPlayer.tsx
'use client';
import { useRef, useState, useEffect } from 'react';
interface VideoPlayerProps {
src: string;
poster?: string;
subtitles?: { src: string; label: string; lang: string }[];
}
export function VideoPlayer({ src, poster, subtitles }: VideoPlayerProps) {
const videoRef = useRef<HTMLVideoElement>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [volume, setVolume] = useState(1);
const [playbackRate, setPlaybackRate] = useState(1);
const [showControls, setShowControls] = useState(true);
const video = videoRef.current;
useEffect(() => {
if (!video) return;
const handleTimeUpdate = () => setCurrentTime(video.currentTime);
const handleLoadedMetadata = () => setDuration(video.duration);
video.addEventListener('timeupdate', handleTimeUpdate);
video.addEventListener('loadedmetadata', handleLoadedMetadata);
return () => {
video.removeEventListener('timeupdate', handleTimeUpdate);
video.removeEventListener('loadedmetadata', handleLoadedMetadata);
};
}, [video]);
const togglePlay = () => {
if (!video) return;
if (video.paused) {
video.play();
setIsPlaying(true);
} else {
video.pause();
setIsPlaying(false);
}
};
const handleSeek = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!video) return;
const time = Number(e.target.value);
video.currentTime = time;
setCurrentTime(time);
};
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!video) return;
const vol = Number(e.target.value);
video.volume = vol;
setVolume(vol);
};
const changePlaybackRate = (rate: number) => {
if (!video) return;
video.playbackRate = rate;
setPlaybackRate(rate);
};
const toggleFullscreen = () => {
const container = videoRef.current?.parentElement;
if (!container) return;
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
container.requestFullscreen();
}
};
const togglePiP = async () => {
if (!video) return;
if (document.pictureInPictureElement) {
await document.exitPictureInPicture();
} else {
await video.requestPictureInPicture();
}
};
const formatTime = (seconds: number) => {
const m = Math.floor(seconds / 60);
const s = Math.floor(seconds % 60);
return `${m}:${s.toString().padStart(2, '0')}`;
};
return (
<div
className="relative group bg-black rounded-xl overflow-hidden"
onMouseEnter={() => setShowControls(true)}
onMouseLeave={() => isPlaying && setShowControls(false)}
>
<video
ref={videoRef}
src={src}
poster={poster}
onClick={togglePlay}
className="w-full cursor-pointer"
>
{subtitles?.map((sub) => (
<track key={sub.lang} kind="subtitles" src={sub.src} label={sub.label} srcLang={sub.lang} />
))}
</video>
{/* コントロールバー */}
<div className={`absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4 transition-opacity ${showControls ? 'opacity-100' : 'opacity-0'}`}>
{/* シークバー */}
<input
type="range"
min={0}
max={duration}
value={currentTime}
onChange={handleSeek}
className="w-full h-1 mb-3 accent-blue-500"
/>
<div className="flex items-center justify-between text-white text-sm">
<div className="flex items-center gap-3">
<button onClick={togglePlay} className="text-xl">
{isPlaying ? '⏸' : '▶'}
</button>
<span>{formatTime(currentTime)} / {formatTime(duration)}</span>
<input
type="range"
min={0}
max={1}
step={0.1}
value={volume}
onChange={handleVolumeChange}
className="w-20 h-1 accent-white"
/>
</div>
<div className="flex items-center gap-3">
<select
value={playbackRate}
onChange={(e) => changePlaybackRate(Number(e.target.value))}
className="bg-transparent text-white text-sm"
>
{[0.5, 0.75, 1, 1.25, 1.5, 2].map((rate) => (
<option key={rate} value={rate} className="text-black">
{rate}x
</option>
))}
</select>
<button onClick={togglePiP} title="ピクチャーインピクチャー">🖼</button>
<button onClick={toggleFullscreen} title="フルスクリーン">⛶</button>
</div>
</div>
</div>
</div>
);
}
キーボードショートカット
// src/hooks/useVideoShortcuts.ts
import { useEffect } from 'react';
export function useVideoShortcuts(videoRef: React.RefObject<HTMLVideoElement>) {
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
const video = videoRef.current;
if (!video) return;
switch (e.key) {
case ' ':
e.preventDefault();
video.paused ? video.play() : video.pause();
break;
case 'ArrowRight':
video.currentTime = Math.min(video.currentTime + 10, video.duration);
break;
case 'ArrowLeft':
video.currentTime = Math.max(video.currentTime - 10, 0);
break;
case 'f':
document.fullscreenElement
? document.exitFullscreen()
: video.parentElement?.requestFullscreen();
break;
case 'm':
video.muted = !video.muted;
break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [videoRef]);
}
例 使い方
<VideoPlayer
src="/videos/demo.mp4"
poster="/images/poster.jpg"
subtitles={[
{ src: '/subtitles/ja.vtt', label: '日本語', lang: 'ja' },
{ src: '/subtitles/en.vtt', label: 'English', lang: 'en' },
]}
/>
Artikel Terkait
メディア関連 オーディオプレーヤー implementasi、アクセシビリティdukungan アクセシビリティimplementasipanduan juga ご覧.
Webvideo formatやエンコーディング mengenai MDN Web Docs(developer.mozilla.org) 詳しい.
PDF Gratis: Cheatsheet Claude Code dalam 5 Menit
Cukup masukkan emailmu dan kami akan langsung mengirim cheatsheet PDF A4 satu halaman.
Kami menjaga data pribadimu dengan aman dan tidak pernah mengirim spam.
Tentang Penulis
Masa
Engineer yang aktif menggunakan Claude Code. Mengelola claudecode-lab.com, media teknologi 10 bahasa dengan lebih dari 2.000 halaman.
Artikel Terkait
7 pemeriksaan sebelum menerbitkan artikel Claude Code multibahasa setiap hari
Checklist praktis agar artikel Claude Code multibahasa yang diterbitkan setiap hari tidak kehilangan locale, tidak merusak CTA, dan tidak meninggalkan halaman lama di production.
Apa itu Codex Automations? Membiarkan AI mengurus konten saat kamu tidur
Panduan praktis memakai Codex Automations untuk analytics, artikel, CTA, deploy, dan monetisasi.
Claude Code × GCP Cloud Functions Panduan Lengkap | Pengembangan Serverless Super Cepat
Optimalkan GCP Cloud Functions dengan Claude Code. Implementasikan trigger HTTP/Pub/Sub/Firestore, pengujian lokal, dan otomatisasi deployment dengan contoh kode nyata dari pengalaman Masa.