The first frame captured by the canvas becomes completely black after outputting to the img



I try many articles and methods but still can’t solve it, I hope to get a little hint

Then English is not my native language, sorry

  var video = document.getElementById('video');
  var canvas = document.createElement('canvas');
  var videobg = document.getElementById('bgc');

  video.addEventListener('loadeddata', function() {
    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;
    canvas.getContext('2d');
    var img = new Image;
    img.onload = function() {
      ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
    }
    var imgURL = canvas.toDataURL('image/jpeg');
    img.setAttribute('src', imgURL);
    videobg.appendChild(img);
  })
}
  <div class="flexbord">
    <div id="bgc" class="bgc">
      <video id="video" src="https://kwibao-video.s3.ap-northeast-1.amazonaws.com/h7C886i3HFJs98SVUQG5_video_1626853820000.mp4" type="mp4" autoplay muted loop></video>
    </div>
    <div class="qwe"></div>
  </div>

Answer

You are mixing a few pieces of code here…

The img.onload part works for when you want to draw that img on the canvas. Here you are not in this case, you want to draw the video, and then (probably) populate the img with an image extracted from the canvas.

So in this case, you don’t need to wait for the image load, you can draw directly the video to the canvas. However, the loadeddata doesn’t mean that a frame has been presented to the video element yet, so you may prefer to wait for an other event like playing instead.

var video = document.getElementById('video');
var canvas = document.createElement('canvas');
var videobg = document.getElementById('bgc');
var ctx = canvas.getContext("2d");
video.addEventListener('playing', function() {
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  canvas.getContext('2d');
  // draw the video directly
  ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
  var img = new Image;
  // now this represents the canvas, when the video has been drawn on it
  var imgURL = canvas.toDataURL('image/jpeg');
  img.setAttribute('src', imgURL);
  videobg.appendChild(img);
}, {
  once: true
});
.bgc { display: flex; }
<div class="flexbord">
  <div id="bgc" class="bgc">
    <!-- don't forget to set the crossorigin attribute -->
    <video id="video" src="https://kwibao-video.s3.ap-northeast-1.amazonaws.com/h7C886i3HFJs98SVUQG5_video_1626853820000.mp4" crossorigin type="mp4" autoplay muted loop></video>
  </div>
  <div class="qwe"></div>
</div>

However you’d be better avoiding toDataURL and prefer toBlob instead, which uses a lot less memory

var video = document.getElementById('video');
var canvas = document.createElement('canvas');
var videobg = document.getElementById('bgc');
var ctx = canvas.getContext("2d");
video.addEventListener('playing', function() {
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  canvas.getContext('2d');
  // draw the video directly
  ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
  var img = new Image;
  canvas.toBlob(async(blob) => {
    img.src = URL.createObjectURL(blob);
    await img.decode(); // wait for the image is loaded
    URL.revokeObjectURL(img.src);
    videobg.appendChild(img);
  }, 'image/jpeg');
}, {
  once: true
});
.bgc { display: flex; }
<div class="flexbord">
  <div id="bgc" class="bgc">
    <!-- don't forget to set the crossorigin attribute -->
    <video id="video" src="https://kwibao-video.s3.ap-northeast-1.amazonaws.com/h7C886i3HFJs98SVUQG5_video_1626853820000.mp4" crossorigin type="mp4" autoplay muted loop></video>
  </div>
  <div class="qwe"></div>
</div>

or, since you are apparently only “presenting” this frame, append the <canvas> directly, which is even better memory wise:

var video = document.getElementById('video');
var canvas = document.createElement('canvas');
var videobg = document.getElementById('bgc');
var ctx = canvas.getContext("2d");
video.addEventListener('playing', function() {
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  canvas.getContext('2d');
  // draw the video directly
  ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
  // show the canvas
  videobg.appendChild(canvas);
}, {
  once: true
});
.bgc { display: flex; }
<div class="flexbord">
  <div id="bgc" class="bgc">
    <!-- don't forget to set the crossorigin attribute -->
    <video id="video" src="https://kwibao-video.s3.ap-northeast-1.amazonaws.com/h7C886i3HFJs98SVUQG5_video_1626853820000.mp4" crossorigin type="mp4" autoplay muted loop></video>
  </div>
  <div class="qwe"></div>
</div>


Source: stackoverflow