fitzmode
fitzmode

Reputation: 1076

Play video on Canvas in React Konva

Building off of Konva documentation on how to render a video to the canvas, I would like to play the video by accessing the video reference directly from the Konva.Image instance. Below is a contrived example to simulate my goal of playing the video dynamically from outside the component. The below does not play the video as expected, even though imageRef.current.image() returns the video element reference. Any suggestions on how I can access the video reference alternatively?

import React, { useEffect, useContext, useState } from 'react'
import { Image } from "react-konva";
import Konva from 'konva'

 export const MainVideo = ({ shape, dispatch  }) =>  {
  const imageRef = React.useRef(null);
  const video = document.createElement('video');
  video.setAttribute('src',shape.url);


  useEffect(() => {
    if(imageRef) {

  imageRef.current.image().play();
  const layer = imageRef.current.getLayer();

  const anim = new Konva.Animation(() => {
  }, layer);

        anim.start()

    }
  }, [imageRef])

    return (
     <Image 
     ref={imageRef}
     opacity={shape.o}
     id={shape.id} 
     image={video} 
     x={shape.x} 
     y={shape.y} 
     zIndex={0}
     height={360}
     width={640} />
    )
}

Upvotes: 2

Views: 3134

Answers (2)

Keshav Arora
Keshav Arora

Reputation: 11

import ReactDOM from "react-dom";
import React, { useEffect, useRef, useState } from "react";
import { Image, Layer, Stage } from "react-konva";

const VideoPlayer = () => {
  const videoRef = useRef(null);
  const [videoNode, setVidoeNode] = useState();

  useEffect(() => {
    const video = videoRef.current;
    if (video) {
      video.src =
        "https://YOUR_VIDEO_LINK.mp4";

      // Konva animation frame
      const animate = () => {
        if (video.paused || video.ended) {
          return;
        }
        videoNode?.getLayer().batchDraw();
        requestAnimationFrame(animate);
      };

      video.addEventListener("loadeddata", () => {
        video.play();
        animate();
      });
    }
  }, [videoNode]);
  return (
    <>
      <Stage width={window.innerWidth} height={window.innerHeight}>
        <Layer>
          <Image
            ref={(node) => {
              setVidoeNode(node);
            }}
            image={videoRef.current}
            width={window.innerWidth}
            height={window.innerHeight}
          />
        </Layer>
      </Stage>
      <video ref={videoRef} hidden />
    </>
  );
};

export default VideoPlayer;

The useEffect hook is employed to set up the video playback and Konva.js integration. It triggers whenever the videoNode value changes. We use the videoRef to access the video element, set its source to your video URL, and define the animate function that will be responsible for updating the Konva.js layer during video playback. When the video's loadeddata event is triggered, we start playing the video and call the animate function to update the Konva.js layer continuously.

Upvotes: 0

lavrton
lavrton

Reputation: 20373

You can do this:

const Video = ({ src }) => {
  const imageRef = React.useRef(null);
  const [size, setSize] = React.useState({ width: 50, height: 50 });

  // we need to use "useMemo" here, so we don't create new video elment on any render
  const videoElement = React.useMemo(() => {
    const element = document.createElement("video");
    element.src = src;
    return element;
  }, [src]);

  // when video is loaded, we should read it size
  React.useEffect(() => {
    const onload = function() {
      setSize({
        width: videoElement.videoWidth,
        height: videoElement.videoHeight
      });
    };
    videoElement.addEventListener("loadedmetadata", onload);
    return () => {
      videoElement.removeEventListener("loadedmetadata", onload);
    };
  }, [videoElement]);

  // use Konva.Animation to redraw a layer
  React.useEffect(() => {
    videoElement.play();
    const layer = imageRef.current.getLayer();

    const anim = new Konva.Animation(() => {}, layer);
    anim.start();

    return () => anim.stop();
  }, [videoElement]);

  return (
    <Image
      ref={imageRef}
      image={videoElement}
      x={20}
      y={20}
      stroke="red"
      width={size.width}
      height={size.height}
      draggable
    />
  );
};

Demo: https://codesandbox.io/s/react-konva-video-on-canvas-oygvf

Upvotes: 3

Related Questions