网站搜索

javascript - HTML5 Canvas 从视频绘制图像在第一次绘制时不显示


我有一个视频元素和一个画布覆盖层。然后我设置了一个绘图工具,用于在视频上绘画,并有一个保存按钮,它会先从视频再从画布执行 drawImage,以保存合成的帧。然而,第一次按保存时,我只得到画布 drawImage 的结果,视频没有显示。在随后几次保存中,我能正确地看到两个图像的叠加。我原以为这可能是视频图像加载的问题,但在点击保存之前视频已经完全加载,甚至可以前进帧,并且在第二次保存时一切正常。

这是代码...

<div style="width:960px; height:540px; display:inline-block;">
    <video id="video" src="media/_tmp/AA_017_COMP_v37.mov" width="960" height="540" ></video>
</div>
<canvas id="canvas" width="960" height="540" style="position:absolute; top:40px; left:9px; z-index:100;"></canvas>
<input type="button" value="save" id="btn" size="30" onclick="save()" style="float:left; padding:4px; margin-right:4px;" >
<div id="saved" style="border:1px solid #000; position:absolute; top:626px; left:10px; bottom:40px; width:958px; overflow:auto;">SAVED:</div>


function save() {

    //COMP CANVAS OVER VIDEOFRAME
    var video = document.getElementById("video");
    var currentFrame = Math.floor((<?php echo $mov_frames ?> / video.duration) * video.currentTime);

    var compCanvas = document.createElement('canvas');
    compCanvas.width = video.width;
    compCanvas.height = video.height;
    compContext = compCanvas.getContext('2d');
    compContext.drawImage(video, 0, 0);
    compContext.drawImage(canvas, 0, 0);
    var dataURL = compCanvas.toDataURL();

    $("#saved").append('<div style="width:954px; border-bottom:1px solid #000; padding:2px 2px 0 2px;"><img id="compFrame_'+currentFrame+'" width="180" height="90" src="'+dataURL+'" />Frame: '+currentFrame+'</div>');
}

答案#1

我最近自己也遇到了同样的问题,通过先将视频绘制到一个“虚拟”画布上,然后再进行“真实”的处理来解决。这种方法效果很好,因为我代码中所有尝试解析视频的方法都会等待视频触发“canplaythrough”事件。我将“canplaythrough”事件处理程序包装在一个 Promise 中,并且只在收到“canplaythrough”事件且尝试将视频绘制到虚拟画布之后才解析该 Promise。这似乎是 Safari 错误的一个有效解决方法。

例如:

var readyPromise = new Promise(function(resolve) {
    video.addEventListener("canplaythrough", function() {
        var canvas = document.createElement("canvas"),
            context = canvas.getContext("2d");

        context.drawImage(video, 0, 0);

        resolve(video);
    });
});

readyPromise.then(function() {
    // NOW manipulate the video, draw it onto a canvas, etc
});

答案#2

我遇到了同样的问题,并尝试了最后一条评论中的解决方案。对我来说还没有完全奏效。

我后来想明白了,并找到了一个变通方法:

在我的代码中,我有一些其他绘图,以及来自视频的 drawImage,如

context.rect(0, 0, 50, 50);
context.fillStyle = 'black';
context.fill();

即使在 'canplaythrough' 事件之后重新绘制,第二次绘制仍然无法工作。

所以我删除了上面三行,然后在 'canplaythrough' 事件之后进行了第二次绘制。然后它就工作了。

简单来说,对我有效的代码是这样的:

context.drawImage(video, 0, 0);
video.addEventListener('canplaythrough', function () {
    context.drawImage(video, 0, 0);
});