This project implements a custom video player using HTML5 canvas, TypeScript, and vanilla JavaScript. It supports:
The VideoPlayer
class encapsulates:
<canvas>
.playlist: PlaylistItem[]
: An array of objects containing video data (id
, title
, src
, and optional subtitleUrl
).currentIndex
: Which video in the playlist is currently playing.currentSubtitles
: An array of subtitle objects specifying start/end times and text.volumeLevel
: The current audio volume, a number from 0.0
to 1.0
.canvas
, ctx
: The main <canvas>
element and its 2D drawing context.videoElement
, previewVideoElement
: Hidden <video>
elements used for main playback and for generating preview frames.In the constructor:
<ul>
, the drag-and-drop zone, the file input, the effect <select>
, and the debug info <div>
.<video>
elements in memory (videoElement
and previewVideoElement
) and set their crossOrigin
property for potential same-origin issues.init()
MethodloadSettings()
: Attempts to read previously saved volume and playlist index from localStorage
.buildInitialPlaylist()
: Creates a default array of 4 sample videos with optional subtitle URLs.enforceCurrentIndex()
: Ensures the currentIndex is in valid range.setupVideo()
: Loads and begins playing the first video.registerDOMEvents()
: Hooks up event handlers for the canvas, file input, drag events, and ended
event on the <video>
.startRenderLoop()
: Initiates a requestAnimationFrame
loop to continually update the canvas.setInterval
updates the on-screen info every second.renderPlaylist()
: Clears the <ul>
and recreates it, adding an item for each video.createTitleElement()
: Makes a clickable <span>
for each video, which sets the currentIndex when clicked.createMoveUpButton()
, createMoveDownButton()
, createDeleteButton()
: Each returns a <button>
that shifts or removes an item from the playlist
.setupVideo()
: Sets the src
, resets the currentTime
, applies the saved volumeLevel
, and attempts to auto-play. If the item has a subtitle URL, it calls fetchSubtitles()
.fetchSubtitles()
: Fetches the JSON from a URL, returning an array of subtitle objects."click"
to detect user interactions with the controls, and "mousemove"
to handle hover states for preview frames."change"
to add new local video files to the playlist."dragover"
and "drop"
to allow drag-and-drop additions to the playlist.startRenderLoop()
: Uses requestAnimationFrame
to keep calling drawFrame()
.drawFrame()
: Draws the video to the canvas, then calls:applyVideoEffect()
- modifies the current frame (e.g., grayscale, invert, threshold).drawSubtitles()
- checks if any subtitle is active and draws text at the bottom of the canvas.drawControls()
- draws a semi-transparent overlay with control icons, volume bar, and progress bar.drawPreviewFrame()
: On hover over the progress bar, we compute a previewTime
, seek previewVideoElement.currentTime
, then draw a tiny thumbnail above the progress bar.drawThumbnailTime()
: Draws a small black rectangle with a timecode at the bottom.handleCanvasClick()
: Determines if the click position is over the previous/next buttons, play/pause, volume area, or progress bar, and calls the appropriate action (e.g., togglePlay()
, prevVideo()
, etc.).togglePlay()
: Resumes or pauses the main video.prevVideo()
, nextVideo()
: Moves backward or forward in the playlist.cycleVolume()
: Steps the volume by 0.2 until it loops back to 0.updateDebugInfo()
: Shows current volume, time, and total duration in a <div>
.formatTime()
: Converts seconds to a mm:ss
string.npm install
).npm run dev
(or your preferred command) to start the local development server, typically via Vite.X
).