概要
スポーツシーズンですが、世のお父さん方は、子供達が活躍する所をせっせとビデオ撮影し、寝る間も惜しんで編集作業に勤しんでいることと思います。同情します。
そんなお父さんの動画クオリティーをアップさせるタイマーを用意しました。
https://www.exabugs.com/timer/index.html
これでカッコいい動画を作成すれば、嫁や子供達から「パパ素敵!」と言われること間違いなし!やったね!
なお、本記事では iMovie と QuickTime を使用します
利用方法
タイマー使い方
- ブラウザで上記URLにアクセス。(Chrome/Safariは動作確認済み)
- 「リセット」でタイマーが出現し、「スタート」で開始します。
- 設定はローカルに保存されますが、「クリア」を押すと初期化されます。
- 緑色背景はアスペクト比 16:9 です。
- 設定項目(左から)
- 試合時間を「分」単位で指定
- cd : カウントダウンかどうか。ONの場合は試合時間をカウントダウンします。
- abs : 円形プログレスバーの一周(360度)を60分とする(サッカー方式?)か、試合時間とするか。
- タイマーの大きさを画面幅との比率で指定
- プログレスバーの色を指定。RGB値かred,blueのようなウェブカラー名。
WEB色見本 https://www.colordic.org/ - プログレスバーの前景の幅を指定
- プログレスバーの背景の幅を指定
- 背景色を指定。クロマキー用途には緑か青。LT大会で使うには白がいいでしょう。
- タイマーのフォント。
Googleウェブフォントを使用しています。 - タイマーのフォントサイズ
- タイマーの水平位置
- タイマーの垂直位置
- 緑色部分の幅と高さ。アスペクト比16:9になっています。
タイマー動画作成
動画編集
1. タイマーの合成
- iMovie を起動して、スポーツ動画とタイマー動画の両方をマイメディアに取り込みます
- 「グリーン/ブルースクリーン」を選択すると、クロマキー合成されます。
- タイムや怪我人の救護等で時計の進行が止まる場合は、iMovieでは「フリーズフレーム」でタイマー動画を止めるとよいでしょう。
- ここまでで一旦、合成された動画をエクスポートします。
2. ハイライトシーンの編集
- 1.の動画を開いて、ハイライトシーンを編集します。
プログラム
- 一応コードも貼っておきます。
- 簡単のため一つのHTMLに内包しています。
- 肝心の円形プログレスバーの部分はcirclifulを使わせて頂きました。
https://github.com/pguso/jquery-plugin-circliful
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Timer</title>
<link href="css/jquery.circliful.css" rel="stylesheet" type="text/css" />
<link href='https://fonts.googleapis.com/css?family=Roboto:900,400' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Rajdhani:900,400' rel='stylesheet' type='text/css'>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="js/jquery.circliful.min.js"></script>
<style type="text/css">
body {
margin: 0;
font-size: small;
}
div {
margin: 3px;
}
button, input, select {
font-size: 100%;
}
</style>
</head>
<body>
<div id="container">
<div id="header">
<button id="RESET">リセット</button>
<button id="START">スタート</button>
<input id="total" size="2">分
<input id="countdown" type="checkbox">cd
<input id="abs" type="checkbox">abs
<input id="size" size="2">%
<input id="color" size="9">
<input id="fgborder" size="2">
<input id="bgborder" size="2">
<select id="bg">
<option value="#ffffff">白</option>
<option value="#00ff00">緑</option>
<option value="#0000ff">青</option>
<option value="#000000">黒</option>
</select>
<select id="font_family">
<option value="Roboto" selected>Roboto</option>
<option value="Rajdhani">Rajdhani</option>
</select>
<input id="font_size" size="3">px
<select id="horizontal">
<option value="0">左</option>
<option value="1">中</option>
<option value="2">右</option>
</select>
<select id="vertical">
<option value="0">上</option>
<option value="1">中</option>
<option value="2">下</option>
</select>
<span id="divsize"></span>
<button id="CLEAR">クリア</button>
</div>
<div id="contents">
<div id="clock"></div>
</div>
<div id="footer">
</div>
</div>
<script>
function time(s) {
var min = Math.floor(s / 60);
var sec = Math.floor(s % 60);
return ("0" + min).slice(-2) + ":" + ("0" + sec).slice(-2);
}
function disp(prog, total, countdown) {
var _t = prog;
if (total < prog) {
_t = prog - total;
} else if (countdown) {
_t = total - prog;
}
return time(_t / 1000);
}
function percent(prog, total, abs) {
var hour = 60 * 60 * 1000;
var all = abs ? hour : total;
return Math.max((total - prog) / all, 0);
}
function shadow(color) {
return [
[2, 0], [-2, 0], [0, -2], [0, 2],
[2, 2], [-2, 2], [2, -2], [-2, -2],
[1, 2], [-1, 2], [1, -2], [-1, -2],
[2, 1], [-2, 1], [2, -1], [-2, -1]
].map(function(i) {
return color + " " + i.map(function(j) {
return j + "px"
}).join(" ")
}).join(",");
}
function conf(total, abs, countdown) {
var font_size = $('#font_size').val();
var font_family = $('#font_family').val();
var textStyle = [
["font-size", font_size + "px"],
["font-family", font_family],
["text-shadow", shadow("black")]
].map(function(i) {
return i[0] + ": " + i[1] + ";"
}).join(" ");
var prog = 0;
return {
percent: Math.floor(percent(prog, total, abs) * 100),
replacePercentageByText: '',
text: disp(prog, total, countdown),
textStyle: textStyle,
foregroundBorderWidth: $("#fgborder").val(),
backgroundBorderWidth: $("#bgborder").val(),
foregroundColor: $("#color").val(),
textColor: "white",
backgroundColor: "white",
}
}
///////////////////////////////////////////////////
var storage = localStorage;
function saveText(id) {
storage.setItem(id, $("#" + id).val());
}
function saveCheck(id) {
var val = $("#" + id).prop("checked");
storage.setItem(id, val);
}
function loadText(id, def) {
var val = storage.getItem(id);
val = (val === null ? def : val);
$("#" + id).val(val).change();
}
function loadCheck(id, def) {
var val = storage.getItem(id);
val = (val === null ? def : (val === "false" ? false : true));
$("#" + id).prop("checked", val).change();
}
function save() {
saveCheck("countdown");
saveCheck("abs");
saveText("total");
saveText("size");
saveText("color");
saveText("bg");
saveText("font_family");
saveText("font_size");
saveText("horizontal");
saveText("vertical");
saveText("fgborder");
saveText("bgborder");
}
function load() {
loadCheck("countdown", true);
loadCheck("abs", true);
loadText("total", 45);
loadText("size", 15);
loadText("color", "#3498DB");
loadText("bg", "#00ff00");
loadText("font_family", "Roboto");
loadText("font_size", 36);
loadText("horizontal", 0);
loadText("vertical", 0);
loadText("fgborder", 11);
loadText("bgborder", 15);
}
function clear() {
storage.clear();
load();
}
///////////////////////////////////////////////////
var data = {};
function stop() {
clearInterval(data.timer);
delete data.timer;
}
function reset() {
stop();
save();
data.total = Number($("#total").val()) * 60 * 1000;
data.countdown = $("#countdown").prop("checked");
data.abs = $("#abs").prop("checked");
var countdown = data.countdown;
var total = data.total;
var abs = data.abs;
$('#clock').remove();
$('#contents').append('<div id="clock"></div>');
$('#clock').circliful(conf(total, abs, countdown));
var svg = $("#clock").children("svg").eq(0);
data.svg = {
container: svg,
text: svg.children("text").eq(0),
circle: svg.children("circle").eq(1)
};
resize();
}
function start() {
stop();
data.start = Date.now();
data.timer = setInterval(function() {
var countdown = data.countdown;
var total = data.total;
var abs = data.abs;
var prog = Date.now() - data.start;
var degree = Math.floor(percent(prog, total, abs) * 360);
data.svg.text.text(disp(prog, total, countdown));
data.svg.circle.css("stroke-dasharray", degree + ", 20000");
}, 200);
}
$('#RESET').click(reset);
$('#START').click(start);
$('#CLEAR').click(clear);
///////////////////////////////////////////////////
$("#bg").change(function() {
$("#contents").css('background-color', $(this).val());
});
function resize() {
var contents = $("#contents");
contents.height(Math.floor(contents.width() / 16 * 9));
$("#divsize").text("(" + contents.width() + "x" + contents.height() + ")");
if (data.svg) {
var ratio = $("#size").val() / 100;
var size = contents.width() * ratio;
var left = (contents.width() - size) / 2 * $("#horizontal").val();
var top = (contents.height() - size) / 2 * $("#vertical").val();
var svg = data.svg.container;
svg.css('width', size);
svg.css('padding-left', left);
svg.css('padding-top', top);
}
}
$("#horizontal").change(resize);
$("#vertical").change(resize);
$(window).resize(resize);
///////////////////////////////////////////////////
load();
resize();
</script>
</body>
</html>