-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
<AnimatedImage>
component
#4175
Comments
hey hey hey… good weekend project and learning experience if you wanna assign! |
Hey @JonnyBurger would love to take a look at this. Thanks! |
Hey, I give it to @Just-Moh-it because he wrote first. Happy to give you the next one @fitzmode! 💎 This issue has a bounty on it! Read our contributing guidelines:
/bounty 500 Criteria and hintsThere should be a new component added to the It should work similarly to the The When fetching a URL, the component should take a look at the
All work the same, just the There should be a good error message if There should be a documentation page that aligns with other docs in the |
💎 $500 bounty • RemotionSteps to solve:
Thank you for contributing to remotion-dev/remotion! |
Hey, would love to have a look at this, I'm in the row. |
Hi, would like to work on this issue. I'm in the queue. |
Am also available to start working on this if chanced. |
/attempt #4175 |
@amochuko: Another person is already attempting this issue. Please don't start working on this issue unless you were explicitly asked to do so. |
@JonnyBurger Can i get this one assigned ,i am really excited to get this component done |
update: finishing this up, closing in! |
@Just-Moh-it can you open a Draft PR with the progress so far? maybe we can help finishing it |
Lemme give it a Trryyy!
|
@j4nlksh: Another person is already attempting this issue. Please don't start working on this issue unless you were explicitly asked to do so. |
@JonnyBurger lmk if this issue is up for grabs. |
@Just-Moh-it Are you still on this? |
@JonnyBurger since the issue still open I would like to contribute. Can you please assign this for me |
Whats status here ? Is this open for anyone to work ? @JonnyBurger |
I needed this, so I did create it on my own. Works with gif, webp and avif, if browser not supported or wrong content-type then falls back to use Feel free to use it in remotion, or if still needed I could make this meet all the requirements and write the docs to solve this issue. import { CSSProperties, useEffect, useMemo, useRef, useState } from 'react'
import { Img, continueRender, delayRender, useCurrentFrame, useVideoConfig } from 'remotion'
type ImageDecoderInit = {
type: string
data: ReadableStream | ArrayBuffer | ArrayBufferView | Blob | string | null
}
type ImageDecodeResult = {
image: VideoFrame
complete: boolean
}
type ImageDecoderVideoTrack = {
selected: boolean
frameCount: number
repetitionCount: number
frameSize: {
width: number
height: number
}
}
type ImageDecoderTracks = {
selectedIndex: number
selectedTrack: ImageDecoderVideoTrack
}
declare class ImageDecoder {
constructor(init: ImageDecoderInit)
readonly tracks: ImageDecoderTracks
readonly completed: Promise<void>
decode(options?: { frameIndex?: number }): Promise<ImageDecodeResult>
reset(): void
close(): void
}
const ANIMATED_IMAGE_CONTENT_TYPES = ['image/gif', 'image/webp', 'image/avif']
type AnimatedImageMetadata = {
width: number
height: number
fps: number | null
frameCount: number
}
const useAnimatedImage = (src: string) => {
const [handle] = useState(delayRender)
const [decoder, setDecoder] = useState<ImageDecoder>()
const [metadata, setMetadata] = useState<AnimatedImageMetadata>()
useEffect(() => {
const effect = async () => {
if (typeof ImageDecoder === 'undefined') return console.error('ImageDecoder not available in this browser!')
const response = await fetch(src)
const contentType = response.headers.get('Content-Type')
if (!contentType || !ANIMATED_IMAGE_CONTENT_TYPES.includes(contentType))
return console.error(`Content type '${contentType}' is not supported!`)
const decoder = new ImageDecoder({ data: response.body, type: contentType })
await decoder.completed
const frameCount = decoder.tracks.selectedTrack.frameCount
// Decoding the first frame to get metadata
const { image } = await decoder.decode({ frameIndex: 0 })
const height = image.displayHeight
const width = image.displayWidth
// image.duration can be null if it's non-animated image
const fps = image.duration ? 1000000 / image.duration : null
setDecoder(decoder)
setMetadata({ frameCount, height, width, fps })
}
effect().then(() => continueRender(handle))
}, [src])
return { decoder, metadata }
}
type AnimatedImageProps = {
src: string
style?: CSSProperties
loopBehavior?: 'loop' | 'pause-after-finish'
playbackRate?: number
}
export const AnimatedImage = ({ src, style, loopBehavior = 'loop', playbackRate = 1 }: AnimatedImageProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null)
const { fps } = useVideoConfig()
const frame = useCurrentFrame()
const { decoder, metadata } = useAnimatedImage(src)
const frameIndex = useMemo(() => {
if (!metadata || !metadata.fps) return 0
const currentFrame =
loopBehavior === 'loop'
? (frame * playbackRate) % metadata.frameCount
: Math.min(frame * playbackRate, metadata.frameCount)
return Math.floor((currentFrame * metadata.fps) / fps)
}, [frame, playbackRate, metadata, loopBehavior])
useEffect(() => {
const renderFrame = async () => {
const canvas = canvasRef.current
if (!decoder || !canvas) return
const ctx = canvas.getContext('2d')
if (!ctx) return
const { image } = await decoder.decode({ frameIndex })
ctx.drawImage(image, 0, 0)
}
const handle = delayRender()
renderFrame().then(() => continueRender(handle))
}, [decoder, frameIndex])
if (!metadata) return <Img src={src} style={style} />
return <canvas ref={canvasRef} width={metadata.width} height={metadata.height} style={style} />
} |
TIL about
ImageDecoder
: https://developer.mozilla.org/en-US/docs/Web/API/ImageDecoderSample:
Crazy that it works with animated GIFs, AVIFs and animated WebPs!
We can re-implement the
Gif
component with it and also support other image formats!The text was updated successfully, but these errors were encountered: