Graphics 画布中的文本将被下面的视频画布擦除

Graphics 画布中的文本将被下面的视频画布擦除,graphics,html5-canvas,html5-video,Graphics,Html5 Canvas,Html5 Video,注:最小工作示例。点击更改文本按钮,您可以看到文本将出现一帧,然后消失 我想给我的视频添加一个覆盖层,所以我把两张画布堆叠起来,并在透明的顶部画布上填充文本 但是,文本不会粘住。它消失了 为了测试我是否正确填充了文本,我尝试在下面使用黑色画布(没有视频) 我只需要填写一次文本,文本就保留了下来 但是,在下面的视频画布上,除非我在requestAnimationFrame中绘制文本,否则文本不会粘住,我相信它会在每个帧中绘制相同的文本,这是不必要的 文本画布应该与视频画布分开。为什么在没有requ

注:最小工作示例。点击
更改文本
按钮,您可以看到文本将出现一帧,然后消失

我想给我的视频添加一个覆盖层,所以我把两张画布堆叠起来,并在透明的顶部画布上填充文本

但是,文本不会粘住。它消失了

为了测试我是否正确填充了文本,我尝试在下面使用黑色画布(没有视频)

我只需要填写一次文本,文本就保留了下来

但是,在下面的视频画布上,除非我在requestAnimationFrame中绘制文本,否则文本不会粘住,我相信它会在每个帧中绘制相同的文本,这是不必要的

文本画布应该与视频画布分开。为什么在没有requestAnimationFrame的情况下这会被删除

我怎样才能修好它

最小的工作示例,但代码也显示在下面

import "./styles.css";
import { useEffect, useRef, useState } from "react";

export default function App() {
  const inputStreamRef = useRef();
  const videoRef = useRef();

  const canvasRef = useRef();
  const overlayCanvasRef = useRef();
  const [text, setText] = useState("Hello");

  function drawTextWithBackground() {
    const ctx = overlayCanvasRef.current.getContext("2d");
    ctx.clearRect(
      0,
      0,
      overlayCanvasRef.current.width,
      overlayCanvasRef.current.height
    );

    /// lets save current state as we make a lot of changes
    ctx.save();

    /// set font
    const font = "50px monospace";
    ctx.font = font;

    /// draw text from top - makes life easier at the moment
    ctx.textBaseline = "top";

    /// color for background
    ctx.fillStyle = "#FFFFFF";

    /// get width of text
    const textWidth = ctx.measureText(text).width;
    // Just note that this way of "measuring" height is not accurate.
    // You can measure height of a font by using a temporary div / span element and
    // get the calculated style from that when font and text is set for it.
    const textHeight = parseInt(font, 10);

    const textXPosition = canvasRef.current.width / 2 - textWidth / 2;
    const textYPosition = canvasRef.current.height - textHeight * 3;

    ctx.save();
    ctx.globalAlpha = 0.4;
    /// draw background rect assuming height of font
    ctx.fillRect(textXPosition, textYPosition, textWidth, textHeight);
    ctx.restore(); // this applies globalAlpha to just this?

    /// text color
    ctx.fillStyle = "#000";

    ctx.fillText(text, textXPosition, textYPosition);

    /// restore original state
    ctx.restore();
  }

  function updateCanvas() {
    const ctx = canvasRef.current.getContext("2d");

    ctx.drawImage(
      videoRef.current,
      0,
      0,
      videoRef.current.videoWidth,
      videoRef.current.videoHeight
    );

    // QUESTION: Now the text won't stick unless I uncomment this below,
    // which runs drawTextWithBackground in requestAnimationFrame.
    // Previosuly, with just a black canvas underneath, I just needed to fillText once
    // and the text stayed.
    // The text canvas is supposed to be separate. Why is this getting wiped out without requestAnimationFrame?
    // drawTextWithBackground();

    requestAnimationFrame(updateCanvas);
  }

  const CAMERA_CONSTRAINTS = {
    audio: true,
    video: {
      // the best (4k) resolution from camera
      width: 4096,
      height: 2160
    }
  };

  const enableCamera = async () => {
    inputStreamRef.current = await navigator.mediaDevices.getUserMedia(
      CAMERA_CONSTRAINTS
    );

    videoRef.current.srcObject = inputStreamRef.current;

    await videoRef.current.play();

    // We need to set the canvas height/width to match the video element.
    canvasRef.current.height = videoRef.current.videoHeight;
    canvasRef.current.width = videoRef.current.videoWidth;
    overlayCanvasRef.current.height = videoRef.current.videoHeight;
    overlayCanvasRef.current.width = videoRef.current.videoWidth;

    requestAnimationFrame(updateCanvas);
  };
  useEffect(() => {
    enableCamera();
  });

  return (
    <div className="App">
      <video
        ref={videoRef}
        style={{ visibility: "hidden", position: "absolute" }}
      />
      <div style={{ position: "relative", width: "90vw" }}>
        <canvas
          style={{ width: "100%", top: 0, left: 0, position: "static" }}
          ref={canvasRef}
          width="500"
          height="400"
        />
        <canvas
          style={{
            width: "100%",
            top: 0,
            left: 0,
            position: "absolute"
          }}
          ref={overlayCanvasRef}
          width="500"
          height="400"
        />
      </div>
      <button
        onClick={() => {
          setText(text + "c");
          drawTextWithBackground();
        }}
      >
        change text
      </button>
    </div>
  );
}
导入“/styles.css”; 从“react”导入{useffect,useRef,useState}; 导出默认函数App(){ const inputStreamRef=useRef(); const videoRef=useRef(); const canvasRef=useRef(); const overlayCanvasRef=useRef(); const[text,setText]=useState(“Hello”); 函数drawTextWithBackground(){ const ctx=overlycanvasref.current.getContext(“2d”); ctx.clearRect( 0, 0, 覆盖canvasref.current.width, 覆盖CanvasRef.current.height ); ///让我们在进行大量更改时保存当前状态 ctx.save(); ///设置字体 const font=“50px monospace”; ctx.font=font; ///从顶部绘制文本-使当前的生活更轻松 ctx.textb基线=“顶部”; ///背景颜色 ctx.fillStyle=“#FFFFFF”; ///获取文本的宽度 const textWidth=ctx.measureText(text).width; //请注意,这种“测量”高度的方法并不准确。 //您可以使用临时div/span元素和 //设置字体和文字时,从中获取计算的样式。 const textHeight=parseInt(字体,10); const textXPosition=canvasRef.current.width/2-textWidth/2; const textYPosition=canvasRef.current.height-textHeight*3; ctx.save(); ctx.globalAlpha=0.4; ///假设字体高度绘制背景矩形 ctx.fillRect(textXPosition、textYPosition、textWidth、textHeight); ctx.restore();//这将globalAlpha应用于此? ///文本颜色 ctx.fillStyle=“#000”; ctx.fillText(text,textXPosition,textYPosition); ///恢复原始状态 ctx.restore(); } 函数updateCanvas(){ const ctx=canvasRef.current.getContext(“2d”); ctx.drawImage( videoRef.current, 0, 0, videoRef.current.videoWidth, videoRef.current.videoHeight ); //问题:除非我在下面取消注释,否则文本不会保持不变, //它在requestAnimationFrame中运行drawTextWithBackground。 //以前,只有一块黑色的画布在下面,我只需要填充一次文本 //文本保持不变。 //文本画布应该是分开的。为什么在没有requestAnimationFrame的情况下会被删除? //drawTextWithBackground(); requestAnimationFrame(updateCanvas); } 常量摄影机约束={ 音频:是的, 视频:{ //摄像机的最佳(4k)分辨率 宽度:4096, 身高:2160 } }; const enableCamera=async()=>{ inputStreamRef.current=等待navigator.mediaDevices.getUserMedia( 摄像机约束 ); videoRef.current.srcObject=inputStreamRef.current; 等待videoRef.current.play(); //我们需要设置画布的高度/宽度以匹配视频元素。 canvasRef.current.height=videoRef.current.videoHeight; canvasRef.current.width=videoRef.current.videoWidth; OverlyCanvasref.current.height=videoRef.current.videoHeight; OverlyCanvasref.current.width=videoRef.current.videoWidth; requestAnimationFrame(updateCanvas); }; useffect(()=>{ 启用摄像头(); }); 返回( { setText(文本+c); drawTextWithBackground(); }} > 更改文本 ); }
从视频中更新画布时,画布中的所有像素都将使用视频中的像素进行更新。把它想象成一堵墙:

你在墙上画了“这是我的文字”

然后你把整面墙漆成蓝色。(你的文字不见了,墙上只有蓝色)

如果你把整面墙涂成蓝色,然后重新涂上“这是我的文字”,你仍然可以在蓝色的墙上看到你的文字

重新绘制画布时,必须重新绘制要显示在画布上的所有内容。如果视频“覆盖”了你的文本,你必须重新绘制文本


在画布的每一帧上绘制chyron(新闻故事底部的条形图)的工作示例。

当您从视频更新画布时,画布中的所有像素都会用视频中的像素更新。把它想象成一堵墙:

你在墙上画了“这是我的文字”

然后你把整面墙漆成蓝色。(你的文字不见了,墙上只有蓝色)

如果你把整面墙涂成蓝色,然后重新涂上“这是我的文字”,你仍然可以在蓝色的墙上看到你的文字

重新绘制画布时,必须重新绘制要显示在画布上的所有内容。如果视频“覆盖”了你的文本,你必须重新绘制文本

在画布的每一帧上绘制一个chyron(新闻故事底部的条形图)的工作示例。

非常简单的修复 由于如何反应更新状态,画布正在被清除

修复 最简单的修复方法是只使用一个画布,并将文本绘制到用于显示视频的画布上

从工作示例更改两个函数
updateCanvas
dra
  function drawTextWithBackground(ctx) {
    const can = ctx.canvas;
    const textH = 50;  
    ctx.font = textH + "px monospace";
    ctx.textBaseline = "top";
    ctx.textAlign = "center";
    ctx.fillStyle = "#FFFFFF66";     // alpha 0.4 as part of fillstyle
    ctx.fillStyle = "#000";
    const [W, X, Y] = [ctx.measureText(text).width, can.width, can.height - textH * 3];
    ctx.fillRect(X -  W / 2, Y, W, textH);
    ctx.fillText(text, X, Y);
  }

  function updateCanvas() {
    const vid = videoRef.current;
    const ctx = canvasRef.current.getContext("2d");
    ctx.drawImage(vid, 0, 0, vid.videoWidth,  vid.videoHeight);
    drawTextWithBackground(ctx);
    requestAnimationFrame(updateCanvas);
  }