Skip to content

Instantly share code, notes, and snippets.

@nwaughachukwuma
Last active October 2, 2023 02:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nwaughachukwuma/1a2079d0845b17dd45c0c6c5a995bbcb to your computer and use it in GitHub Desktop.
Save nwaughachukwuma/1a2079d0845b17dd45c0c6c5a995bbcb to your computer and use it in GitHub Desktop.
Get video frame preview (snapshot) from video element
type EventDef<D> = Event & { detail?: D }
type VideoRef = string | HTMLVideoElement | null
const eventName = 'image-updated'
export default function getVideoSnapshot(videoRef: VideoRef) {
if (typeof videoRef === 'string') {
videoRef = document.getElementById(videoRef) as HTMLVideoElement | null
}
if (!videoRef) {
throw new Error('Video element not found')
}
const video = videoRef
const canvas = document.createElement('canvas')
canvas.width = 1920
canvas.height = 1080
const eventTarget = document.appendChild(
document.createComment('imageSnapshot'),
)
function getImageURL() {
let image = ''
const ctx = canvas.getContext('2d')
if (ctx) {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
image = canvas.toDataURL('image/jpeg')
}
return image
}
function eventListener() {
eventTarget.dispatchEvent(
new CustomEvent(eventName, { detail: getImageURL() }),
)
}
video.addEventListener('seeked', eventListener)
return {
seekToSnap(time: number) {
video.currentTime = time
return getImageURL()
},
addSeekListener(listener: (evt: EventDef<string>) => void) {
eventTarget.addEventListener(eventName, listener)
},
removeSeekListener(listener: (evt: EventDef<string>) => void) {
eventTarget.removeEventListener(eventName, listener)
},
destroy() {
eventTarget.remove()
video.removeEventListener('seeked', getImageURL)
},
}
}
@nwaughachukwuma
Copy link
Author

nwaughachukwuma commented Jan 14, 2023

Example

let imageURL = ''
let videoSnapshot: ReturnType<typeof getVideoSnapshot> | null = null

function onVideoReady() {
  videoSnapshot = getVideoSnapshot('video')
  // get start snapshot
  imageURL = videoSnapshot.seekToSnap(0.25)
  videoSnapshot.addSeekListener(({ detail }) => {
    imageURL = detail || ''
  })
}
function onDestroy() {
  if (videoSnapshot) {
    videoSnapshot.destroy()
  }
}

See StackBlitz

@nwaughachukwuma
Copy link
Author

frame_preview.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment