如何缓存或预渲染画布动画
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何缓存或预渲染画布动画相关的知识,希望对你有一定的参考价值。
我正在使用画布对内联html5视频进行一些轻微修改(颜色键控)。现在,这个视频是一个循环,我认为有更多的最佳方式,然后每次评估和重新绘制循环视频。如果有的话,我可以做什么优化a)预渲染整个动画或b)缓存原始循环,以便不再需要进行处理/评估。我发现通过不断让它运行,我在CPU /内存使用方面效率非常低。
我目前正在使用以下代码(注意,我使用Vue.js,所以假设所有当前函数和变量赋值已经正常工作):
loadVideo() {
this.video = document.getElementById('globe');
this.c1 = document.getElementById("c1");
this.ctx1 = this.c1.getContext("2d");
let that = this;
this.video.addEventListener("play", function() {
that.vWidth = that.video.videoWidth / 2;
that.vHeight = that.video.videoHeight / 2;
that.fpsInterval = 1000 / 120;
that.then = Date.now();
that.startTime = that.then;
that.computeFrame();
}, false);
}
computeFrame() {
requestAnimationFrame(this.computeFrame);
this.now = Date.now();
this.elapsed = this.now - this.then;
if (this.elapsed > this.fpsInterval) {
this.ctx1.canvas.width = this.video.offsetWidth;
this.ctx1.canvas.height = this.video.offsetHeight;
if (this.video.offsetWidth > 0 && this.video.offsetHeight > 0) {
this.then = this.now - (this.elapsed % this.fpsInterval);
this.ctx1.drawImage(this.video, 0, 0, this.ctx1.canvas.width, this.ctx1.canvas.height);
let frame = this.ctx1.getImageData(0, 0, this.ctx1.canvas.width, this.ctx1.canvas.height);
let l = frame.data.length / 4;
let primaryColor = this.ctx1.getImageData(0, 0, 8, 8).data;
let primaryR = primaryColor[60];
let primaryG = primaryColor[61];
let primaryB = primaryColor[62];
for (let i = 0; i < l; i++) {
let r = frame.data[i * 4 + 0];
let g = frame.data[i * 4 + 1];
let b = frame.data[i * 4 + 2];
if (r == primaryR && g == primaryG && b == primaryB) {
frame.data[i * 4 + 1] = 255;
frame.data[i * 4 + 2] = 0;
frame.data[i * 4 + 3] = 0;
}
}
this.ctx1.putImageData(frame, 0, 0);
}
}
}
loadVideo();
答案
您可以使用qazxsw poi记录画布qazxsw poi方法返回的视频流以记录第一遍,然后直接在循环中读取生成的录制视频 :
MediaRecorder
captureStream()
btn.onclick = e => {
// initialise the <video>
const vid = document.createElement('video');
vid.muted = true;
vid.crossOrigin = true;
vid.src = "https://upload.wikimedia.org/wikipedia/commons/transcoded/a/a4/BBH_gravitational_lensing_of_gw150914.webm/BBH_gravitational_lensing_of_gw150914.webm.480p.webm";
vid.playbackRate = 2;
vid.onplaying = startProcessing;
vid.play();
btn.remove();
log.textContent = 'fetching';
};
function startProcessing(evt) {
// when video is playing
const vid = evt.target;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = vid.videoWidth;
canvas.height = vid.videoHeight;
// show the canvas for first round
document.body.appendChild(canvas);
// force first frame
anim();
const chunks = []; // we'll store our recorder's data here
const canvasStream = canvas.captureStream();
const vp = 'video/webm; codecs="vp',
vp8 = vp + '8"',
vp9 = vp + '9"';
const recorder = new MediaRecorder(canvasStream, {
mimeType: MediaRecorder.isTypeSupported(vp9) ? vp9:vp8,
videoBitsPerSeconds: 5000000
});
// every time new data is available
recorder.ondataavailable = evt => chunks.push(evt.data);
// record until the video ends
vid.onended = evt => {
recorder.stop();
};
recorder.onstop = exportVid;
recorder.start();
log.textContent = "recording";
function anim() {
if(vid.paused) {
console.log('stop drawing');
return;
}
ctx.drawImage(vid, 0, 0);
applyFilter(ctx);
requestAnimationFrame(anim);
}
function exportVid() {
// concatenate all our chunks in a single Blob
const blob = new Blob(chunks);
const url = URL.createObjectURL(blob);
// we reuse the same <video> (for Safari autoplay)
vid.onplaying = vid.onended = null;
vid.src = url;
vid.loop = true;
vid.playbackRate = 1;
log.textContent = "playing";
vid.play().then(()=> canvas.replaceWith(vid));
}
}
function applyFilter(ctx) {
const img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data = new Uint32Array(img.data.buffer);
for(let i=0; i<data.length; i++) {
if(data[i] < 0xFF111111) data[i] = ((Math.random()*0xFFFFFF) + 0xFF000000 | 0);
}
ctx.putImageData(img, 0, 0);
}
以上是关于如何缓存或预渲染画布动画的主要内容,如果未能解决你的问题,请参考以下文章