2

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} />
    )
}
fitzmode
  • 1,007
  • 1
  • 18
  • 29

2 Answers2

3

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

lavrton
  • 18,973
  • 4
  • 30
  • 63
0
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 - 0.5}>
        <Layer>
          <Image
            ref={(node) => {
              setVidoeNode(node);
            }}
            image={videoRef.current}
            width={window.innerWidth}
            height={window.innerHeight - 0.5}
          />
        </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.