はじめに
3年ぶりの投稿です。
最近、Codepenなどを使ってCanvasで遊んでいるのですが、Canvasの画像・動きを動画として残せないかと考えるようになりました。
調べてみると、CCapture.jsなど、Canvasから動画を生成するライブラリがあるようなので、Herokuを使って、簡単なCanvas動画生成サービスを作ってみました。
作ったもの
Canvasから動画を生成するWebサービスを試作してみました。https://t.co/Nd265nEUGw pic.twitter.com/sweoAaaEZt
— t_mat (@t_mat) March 2, 2019
使い方
htmlヘッダーに以下の行を追加します。なお、idはcanvasのid、fpsはフレームレート、timeは録画時間(ms)を指定します。
'C'キーを推すとCanvasのキャプチャが開始され、録画終了後、動画(webm)がダウンロードされます。
実装
■JavaScript
Jsは、CCaputureのサンプルコードを参考に、以下のように実装しました。なお、$URL
、$ID
、$FPS
、$TIME
はサーバー側で文字列に置換します。
window.onload = function(){
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "$URL";
document.body.appendChild(script);
};
window.addEventListener('keydown', event => {
if(event.key=='c'||event.key=='C'){
let canvas=document.getElementById("$ID");
startCapture(canvas,$FPS,$TIME);
}
});
let startCapture=function(canvas,fps,time){
let cap=new CCapture({format:'webm',framerate: fps,verbose: true});
let render=function(){
requestAnimationFrame(render);
cap.capture( canvas );
};
requestAnimationFrame(render);
cap.start();
setTimeout(function(){
cap.stop();
cap.save();
},time);
};
■サーバー
サーバー側はリクエストパラメータを解析し、CapCanvas.jsを書き換えて送信します。
個人的にお気に入りのSpark Frameworkで作成しています。
package net.termat.webapp.capcanvas;
import static spark.Spark.get;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import spark.ModelAndView;
import spark.Spark;
import spark.template.mustache.MustacheTemplateEngine;
public class Main {
private static String code;
public static void main(String[] args) {
Spark.staticFileLocation("/public");
code=getJs();
Optional<String> optionalPort = Optional.ofNullable(System.getenv("PORT"));
optionalPort.ifPresent(p -> {
int port = Integer.parseInt(p);
Spark.port(port);
});
get("/", (request, response) -> {
Map<String, Object> model = new HashMap<>();
return new ModelAndView(model, "index.mustache");
}, new MustacheTemplateEngine());
get("/capcanvas/:param", (request, response) -> {
try{
String url="https://capcanvas.herokuapp.com/js/CCapture.all.min.js";
Map<String,String> map=paramMap(request.params("param"));
String id=map.get("id");
String fps=map.get("fps");
String time=map.get("time");
response.status(200);
String ret=new String(code);
ret=ret.replace("$URL", url);
ret=ret.replace("$ID", id);
ret=ret.replace("$FPS", fps);
ret=ret.replace("$TIME", time);
return ret;
}catch(Exception e){
e.printStackTrace();
response.status(400);
response.type("application/json");
return "";
}
});
}
private static String getJs(){
try{
URL url=Main.class.getResource("CapCanvas.js");
BufferedReader br=new BufferedReader(new InputStreamReader(url.openStream()));
StringBuffer buf=new StringBuffer();
String line=null;
while((line=br.readLine())!=null){
buf.append(line);
}
return buf.toString();
}catch(Exception e){
return "";
}
}
private static Map<String,String> paramMap(String param) throws Exception{
Map<String,String> ret=new HashMap<String,String>();
String[] p=param.split("&");
for(int i=0;i<p.length;i++){
String[] k=p[i].split("=");
if(k.length<2)continue;
ret.put(k[0], k[1]);
}
return ret;
}
}
デプロイ
herokuへは、heroku-maven-pluginを使用してデプロイしました。
Port番号の解決に気が付かず、実際に動作させるまで2~3時間かかってしまいました。