canvas 中的文字被下面的视频 canvas 擦除
Text in canvas being wiped out with a video canvas underneath
注意:最小工作示例 here。点击change text
按钮,可以看到文字会出现一帧然后消失。
我想为我的视频添加叠加层,所以我堆叠了两个 canvas 并在透明顶部填充了文字 canvas。
然而,文字不粘。它消失了。
为了测试我是否正确填写了文本,我尝试在下方使用黑色 canvas(无视频)。
我只需要填充一次文本,文本就会保留下来。
但是,下面是视频 canvas,除非我在 requestAnimationFrame 中绘制文本,否则文本不会粘住,我相信它会在每一帧中绘制相同的文本,这是不必要的。
文本 canvas 应该与视频 canvas 分开。为什么没有 requestAnimationFrame 就消失了?
我该如何解决?
最小工作示例here,但代码也如下所示。
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>
);
}
当您更新视频中的 canvas 时,canvas 中的所有像素都会更新为视频中的像素。把它想象成一堵墙:
你在墙上画了“这是我的文字”。
然后你把整面墙漆成了蓝色。 (你的文字不见了,墙上只有蓝色)。
如果您将整面墙涂成蓝色,然后重新涂上“这是我的文字”,您仍然会在蓝色墙壁上看到您的文字。
重绘 canvas 时,您必须重绘想要出现在 canvas 上的所有内容。如果视频“覆盖”了您的文字,您必须重新绘制文字。
https://github.com/dougsillars/chyronavideo 上的工作示例,它在 canvas.
的每一帧上绘制一个 chyron(新闻故事底部的栏)
非常简单的修复
由于 React 更新状态的方式,canvas 正在被清除。
修复
最简单的解决方法是只使用 canvas 并将文本绘制到用于显示视频的 canvas 上。
从工作示例中更改两个函数 updateCanvas
和 drawTextWithBackground
如下。
请注意,我已经删除了 drawTextWithBackground
中的所有状态保存,因为 canvas 状态由于反应而丢失,所以为什么要使用非常昂贵的状态堆栈函数。
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);
}
注意:最小工作示例 here。点击change text
按钮,可以看到文字会出现一帧然后消失。
我想为我的视频添加叠加层,所以我堆叠了两个 canvas 并在透明顶部填充了文字 canvas。
然而,文字不粘。它消失了。
为了测试我是否正确填写了文本,我尝试在下方使用黑色 canvas(无视频)。
我只需要填充一次文本,文本就会保留下来。
但是,下面是视频 canvas,除非我在 requestAnimationFrame 中绘制文本,否则文本不会粘住,我相信它会在每一帧中绘制相同的文本,这是不必要的。
文本 canvas 应该与视频 canvas 分开。为什么没有 requestAnimationFrame 就消失了?
我该如何解决?
最小工作示例here,但代码也如下所示。
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>
);
}
当您更新视频中的 canvas 时,canvas 中的所有像素都会更新为视频中的像素。把它想象成一堵墙:
你在墙上画了“这是我的文字”。
然后你把整面墙漆成了蓝色。 (你的文字不见了,墙上只有蓝色)。
如果您将整面墙涂成蓝色,然后重新涂上“这是我的文字”,您仍然会在蓝色墙壁上看到您的文字。
重绘 canvas 时,您必须重绘想要出现在 canvas 上的所有内容。如果视频“覆盖”了您的文字,您必须重新绘制文字。
https://github.com/dougsillars/chyronavideo 上的工作示例,它在 canvas.
的每一帧上绘制一个 chyron(新闻故事底部的栏)非常简单的修复
由于 React 更新状态的方式,canvas 正在被清除。
修复
最简单的解决方法是只使用 canvas 并将文本绘制到用于显示视频的 canvas 上。
从工作示例中更改两个函数 updateCanvas
和 drawTextWithBackground
如下。
请注意,我已经删除了 drawTextWithBackground
中的所有状态保存,因为 canvas 状态由于反应而丢失,所以为什么要使用非常昂贵的状态堆栈函数。
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);
}