来自用户输入的动态视频 - 它是如何工作的?
Dynamic videos from user inputs - How does it work?
有没有办法通过代码生成视频帧的内容?
例如:
我想制作一个程序,将一些 string
变量作为输入,然后给出相同文本的输出视频,但现在对文本本身具有特殊效果。
看到 Facebook 在其网站上完成的一些项目后,我想到了这个想法。他们有一个与用户相关的图片、评论、朋友、事件、喜欢等的数据库。他们利用所有这些信息制作视频,例如 朋友节,一个与想要 post 的用户完全相关的视频。
这些东西是如何工作的?
我可以使用某种软件吗?有人可以给我一个起点吗?
I want to make a program that takes some string variable as input and then gives an output video of same text but now with special effects on the text itself.
您可以使用 canvas 呈现文本并使用新的 Media Recorder API 记录结果。
当然可以通过多种方式制作效果,文本内容本身可以针对数据库使用,或者结合机器学习 structure/obtain 一些与文本相关的数据。不过,这范围很广,比这里适合的范围更广。
无论如何,视频本身的生成如下所示。这是一个简单的示例,您可以在其中输入一些文本,它会生成动画并录制为视频。当您单击停止时,将显示实际视频(您可以允许用户下载)。您可以扩展其他效果、故事板功能,以便您可以录制场景并对其进行开发以制作整部电影。
请注意所有动画都发生在 real-time 中的 canvas 上。典型的视频以每秒 30 帧而不是每秒 60 帧的速度录制,后者是 canvas 的典型帧速率。这也是关于平滑度需要考虑的事情。但是,您可以通过以 30 FPS 为目标来利用这一优势,从而为您的代码留出更多时间来处理。 API 假设通过不提供帧速率作为参数来支持 step-recording,但我还没有能够完成这项工作。
目前对 API 的支持存在限制,因为并非所有浏览器都支持它。
例子
// un-refined code for demo use only
// set up globals, get key elements
var div = document.querySelector("div");
var btn = document.querySelector("button");
var txt = document.querySelector("input");
var c = document.querySelector("canvas");
// make 2d context without alpha channel
var ctx = c.getContext("2d", {alpha: false});
var isRecording = false;
// stream vars
var rec, stream, tref;
// if clicked, lets go
btn.onclick = setup;
function setup() {
// toggle button text/status for demo
if (isRecording) {
this.disabled = true;
this.innerHTML = "Making video...";
stop();
return
}
else {
isRecording = true;
this.innerHTML = "Click to stop & show video";
}
// Setup canvas for text rendering
var ct1 = document.createElement("canvas");
var ct2 = document.createElement("canvas");
var ctxText1 = ct1.getContext("2d");
var ctxText2 = ct2.getContext("2d");
var w, h;
w = ct1.width = ct2.width = c.width;
h = ct1.height = ct2.height = c.height;
setupCtx(ctxText1, "#333", "#FF9F05");
setupCtx(ctxText2, "#FF9F05", "#000");
function setupCtx(ctxText, bg, fg) {
ctxText.textAlign = "center";
ctxText.textBaseline = "middle";
ctxText.font = "92px sans-serif";
ctxText.fillStyle = bg;
ctxText.fillRect(0, 0, w, h);
ctxText.fillStyle = fg;
ctxText.translate(w/2, h/2);
ctxText.rotate(-0.5);
ctxText.fillText(txt.value.toUpperCase(), 0, 0);
}
// populate grid (see Square object below which handles the tiles)
var cols = 18,rows = 11,
cellWidth = (c.width / cols)|0,
cellHeight = (c.height / rows)|0,
grid = [],
len = cols * rows, y = 0, x,
index, hasActive = true;
for (; y < rows; y++) {
for (x = 0; x < cols; x++) {
grid.push(new Square(ctx, x * cellWidth, y * cellHeight, cellWidth, cellHeight, ct1, ct2, 0.01));
}
}
x = 0;
// start recording canvas to video
record();
//animation loop (refactor at will)
function loop() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.globalAlpha = 1;
ctx.clearRect(0, 0, w, h);
// trigger cells
for (y = 0; y < rows; y++) {
var gx = (x | 0) - y;
if (gx >= 0 && gx < cols) {
index = y * cols + gx;
grid[index].trigger();
}
}
x += 0.25;
// update all
for (var i = 0; i < grid.length; i++) grid[i].update();
tref = requestAnimationFrame(loop);
}
// setup media recorder to record canvas @ 30 FPS (note: canvas = 60 FPS by def.)
function record() {
stream = c.captureStream(30);
rec = new MediaRecorder(stream);
rec.addEventListener('dataavailable', done);
rec.start();
requestAnimationFrame(loop);
}
}
// stop recorder and trigger dataavailable event
function stop() {
rec.stop();
cancelAnimationFrame(tref); // stop loop as well
}
// finish up, show new shiny video instead of canvas
function done(e) {
var blob = new Blob([e.data], {"type": "video/webm"});
var video = document.createElement("video");
document.body.removeChild(c);
document.body.appendChild(video);
// play, Don
video.autoplay = video.loop = video.controls = true;
video.src = URL.createObjectURL(blob);
video.onplay = function() {
div.innerHTML = "Playing resulting video below:";
};
}
// stolen from a previous example I made (CC3.0-attr btw as always)
// this is included for the sole purpose of some animation.
function Square(ctx, x, y, w, h, image, image2, speed) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.height = h;
this.width = w;
this.image = image;
this.image2 = image2;
this.first = true;
this.alpha = 0; // current alpha for this instance
this.speed = speed; // increment for alpha per frame
this.triggered = false; // is running
this.done = false; // has finished
}
Square.prototype = {
trigger: function () { // start this rectangle
this.triggered = true
},
update: function () {
if (this.triggered && !this.done) { // only if active
this.alpha += this.speed; // update alpha
if (this.alpha <= 0 || this.alpha >= 1)
this.speed = -this.speed;
}
var t = Math.sin(Math.PI * this.alpha);
if (t <= 0) this.first = !this.first;
this.ctx.fillStyle = this.color; // render this instance
this.ctx.globalAlpha = Math.max(0, Math.min(1, t));
var cx = this.x + this.width * 0.5, // center position
cy = this.y + this.width * 0.5;
this.ctx.setTransform(t*0.5+0.5, 0, 0, t*0.5+0.5, cx, cy); // scale and translate
this.ctx.rotate(-Math.PI * (1 - t)); // rotate, 90° <- alpha
this.ctx.translate(-cx, -cy); // translate back
this.ctx.drawImage(this.first ? this.image : this.image2, this.x, this.y, this.width, this.height, this.x, this.y, this.width, this.height);
}
};
body {background: #555;color:#ddd;font:16px sans-serif}
<div>
<label>Enter text to animate: <input value="U R AWESOME"></label>
<button>Click to start animation + recording</button>
</div>
<canvas width=640 height=400></canvas>
有没有办法通过代码生成视频帧的内容?
例如:
我想制作一个程序,将一些 string
变量作为输入,然后给出相同文本的输出视频,但现在对文本本身具有特殊效果。
看到 Facebook 在其网站上完成的一些项目后,我想到了这个想法。他们有一个与用户相关的图片、评论、朋友、事件、喜欢等的数据库。他们利用所有这些信息制作视频,例如 朋友节,一个与想要 post 的用户完全相关的视频。
这些东西是如何工作的?
我可以使用某种软件吗?有人可以给我一个起点吗?
I want to make a program that takes some string variable as input and then gives an output video of same text but now with special effects on the text itself.
您可以使用 canvas 呈现文本并使用新的 Media Recorder API 记录结果。
当然可以通过多种方式制作效果,文本内容本身可以针对数据库使用,或者结合机器学习 structure/obtain 一些与文本相关的数据。不过,这范围很广,比这里适合的范围更广。
无论如何,视频本身的生成如下所示。这是一个简单的示例,您可以在其中输入一些文本,它会生成动画并录制为视频。当您单击停止时,将显示实际视频(您可以允许用户下载)。您可以扩展其他效果、故事板功能,以便您可以录制场景并对其进行开发以制作整部电影。
请注意所有动画都发生在 real-time 中的 canvas 上。典型的视频以每秒 30 帧而不是每秒 60 帧的速度录制,后者是 canvas 的典型帧速率。这也是关于平滑度需要考虑的事情。但是,您可以通过以 30 FPS 为目标来利用这一优势,从而为您的代码留出更多时间来处理。 API 假设通过不提供帧速率作为参数来支持 step-recording,但我还没有能够完成这项工作。
目前对 API 的支持存在限制,因为并非所有浏览器都支持它。
例子
// un-refined code for demo use only
// set up globals, get key elements
var div = document.querySelector("div");
var btn = document.querySelector("button");
var txt = document.querySelector("input");
var c = document.querySelector("canvas");
// make 2d context without alpha channel
var ctx = c.getContext("2d", {alpha: false});
var isRecording = false;
// stream vars
var rec, stream, tref;
// if clicked, lets go
btn.onclick = setup;
function setup() {
// toggle button text/status for demo
if (isRecording) {
this.disabled = true;
this.innerHTML = "Making video...";
stop();
return
}
else {
isRecording = true;
this.innerHTML = "Click to stop & show video";
}
// Setup canvas for text rendering
var ct1 = document.createElement("canvas");
var ct2 = document.createElement("canvas");
var ctxText1 = ct1.getContext("2d");
var ctxText2 = ct2.getContext("2d");
var w, h;
w = ct1.width = ct2.width = c.width;
h = ct1.height = ct2.height = c.height;
setupCtx(ctxText1, "#333", "#FF9F05");
setupCtx(ctxText2, "#FF9F05", "#000");
function setupCtx(ctxText, bg, fg) {
ctxText.textAlign = "center";
ctxText.textBaseline = "middle";
ctxText.font = "92px sans-serif";
ctxText.fillStyle = bg;
ctxText.fillRect(0, 0, w, h);
ctxText.fillStyle = fg;
ctxText.translate(w/2, h/2);
ctxText.rotate(-0.5);
ctxText.fillText(txt.value.toUpperCase(), 0, 0);
}
// populate grid (see Square object below which handles the tiles)
var cols = 18,rows = 11,
cellWidth = (c.width / cols)|0,
cellHeight = (c.height / rows)|0,
grid = [],
len = cols * rows, y = 0, x,
index, hasActive = true;
for (; y < rows; y++) {
for (x = 0; x < cols; x++) {
grid.push(new Square(ctx, x * cellWidth, y * cellHeight, cellWidth, cellHeight, ct1, ct2, 0.01));
}
}
x = 0;
// start recording canvas to video
record();
//animation loop (refactor at will)
function loop() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.globalAlpha = 1;
ctx.clearRect(0, 0, w, h);
// trigger cells
for (y = 0; y < rows; y++) {
var gx = (x | 0) - y;
if (gx >= 0 && gx < cols) {
index = y * cols + gx;
grid[index].trigger();
}
}
x += 0.25;
// update all
for (var i = 0; i < grid.length; i++) grid[i].update();
tref = requestAnimationFrame(loop);
}
// setup media recorder to record canvas @ 30 FPS (note: canvas = 60 FPS by def.)
function record() {
stream = c.captureStream(30);
rec = new MediaRecorder(stream);
rec.addEventListener('dataavailable', done);
rec.start();
requestAnimationFrame(loop);
}
}
// stop recorder and trigger dataavailable event
function stop() {
rec.stop();
cancelAnimationFrame(tref); // stop loop as well
}
// finish up, show new shiny video instead of canvas
function done(e) {
var blob = new Blob([e.data], {"type": "video/webm"});
var video = document.createElement("video");
document.body.removeChild(c);
document.body.appendChild(video);
// play, Don
video.autoplay = video.loop = video.controls = true;
video.src = URL.createObjectURL(blob);
video.onplay = function() {
div.innerHTML = "Playing resulting video below:";
};
}
// stolen from a previous example I made (CC3.0-attr btw as always)
// this is included for the sole purpose of some animation.
function Square(ctx, x, y, w, h, image, image2, speed) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.height = h;
this.width = w;
this.image = image;
this.image2 = image2;
this.first = true;
this.alpha = 0; // current alpha for this instance
this.speed = speed; // increment for alpha per frame
this.triggered = false; // is running
this.done = false; // has finished
}
Square.prototype = {
trigger: function () { // start this rectangle
this.triggered = true
},
update: function () {
if (this.triggered && !this.done) { // only if active
this.alpha += this.speed; // update alpha
if (this.alpha <= 0 || this.alpha >= 1)
this.speed = -this.speed;
}
var t = Math.sin(Math.PI * this.alpha);
if (t <= 0) this.first = !this.first;
this.ctx.fillStyle = this.color; // render this instance
this.ctx.globalAlpha = Math.max(0, Math.min(1, t));
var cx = this.x + this.width * 0.5, // center position
cy = this.y + this.width * 0.5;
this.ctx.setTransform(t*0.5+0.5, 0, 0, t*0.5+0.5, cx, cy); // scale and translate
this.ctx.rotate(-Math.PI * (1 - t)); // rotate, 90° <- alpha
this.ctx.translate(-cx, -cy); // translate back
this.ctx.drawImage(this.first ? this.image : this.image2, this.x, this.y, this.width, this.height, this.x, this.y, this.width, this.height);
}
};
body {background: #555;color:#ddd;font:16px sans-serif}
<div>
<label>Enter text to animate: <input value="U R AWESOME"></label>
<button>Click to start animation + recording</button>
</div>
<canvas width=640 height=400></canvas>