#はじめに
ポートフォリオで実装予定のカウントダウンタイマーを実装する。
タイマーを実装するにはjQueryが必要と書いてある記事もあるが、今回はjQueryを利用せずに実装を進めていくことにする。
#作成順序
具体的な手順としては
①制限時間のカラムであるdeadlineを作成する
②タイマーを画面上に表示させるため、HTMLの記述をする
③制限時間を表示させたいので、javascriptを使って記述を行う
#カラムの作成
まずは、いつものように
$rails generate migration Addカラム名Toテーブル名 カラム名:データ型
でマイグレーションファイルを作成する。
今回の場合、投稿部分に制限時間を表示させたいのでdeadlineをカラム名として、
$rails generate migration AddDeadlineToMission deadline:datetime
とする。
class CreateMissions < ActiveRecord::Migration[6.0]
def change
create_table :missions do |t|
t.integer :user_id
t.text :content
t.string :penalty
t.datetime :deadline
t.timestamps
end
end
end
上記のマイグレーションファイルの7行目にt.datetime :deadline
を追加する。
そして、親の顔より見た
$ rails db:migrate
を実行して、データベースに保存する。
#タイマーの実装
##HTMLの記述
上記のような赤字のタイマーを設定するには、
<p>
<%= f.hidden_field :deadline, :id => "deadline.id" %>
<input type="text" id="userYear" >年
<input type="text" id="userMonth">月
<input type="text" id="userDate" >日
<input type="text" id="userHour" >時
<input type="text" id="userMin" >分
<input type="text" id="userSec" >秒
</p>
<p id="RealtimeCountdownArea" ></p> #ここにタイマーが表示される
とする。
ここで、:id => deadline.id
は後のjavascriptの記述において効果を発揮するため、記述している。
##javascriptの記述
今回は、app/views/missions/new.html.erb
のscriptタグにjavascriptを記述することにする。
以下の通りである。
<script>
function set2fig(num) {
// 数値が1桁だったら2桁の文字列にして返す
var ret;
if( num < 10 ) { ret = "0" + num; }
else { ret = num; }
return ret;
}
function isNumOrZero(num) {
// 数値でなかったら0にして返す
if( isNaN(num) ) { return 0; }
return num;
}
function showCountdown() {
// 現在日時を数値(1970-01-01 00:00:00からのミリ秒)に変換
var nowDate = new Date();
var dnumNow = nowDate.getTime();
// 指定日時を数値(1970-01-01 00:00:00からのミリ秒)に変換
var inputYear = document.getElementById("userYear").value;
var inputMonth = document.getElementById("userMonth").value - 1;
var inputDate = document.getElementById("userDate").value;
var inputHour = document.getElementById("userHour").value;
var inputMin = document.getElementById("userMin").value;
var inputSec = document.getElementById("userSec").value;
var targetDate = new Date( isNumOrZero(inputYear), isNumOrZero(inputMonth), isNumOrZero(inputDate), isNumOrZero(inputHour), isNumOrZero(inputMin), isNumOrZero(inputSec) );
var dnumTarget = targetDate.getTime();
// 表示を準備
var dlYear = targetDate.getFullYear();
var dlMonth = targetDate.getMonth() + 1;
var dlDate = targetDate.getDate();
var dlHour = targetDate.getHours();
var dlMin = targetDate.getMinutes();
var dlSec = targetDate.getSeconds();
var msg1 = "期限の" + dlYear + "/" + dlMonth + "/" + dlDate + " " + set2fig(dlHour) + ":" + set2fig(dlMin) + ":" + set2fig(dlSec);
// 引き算して日数(ミリ秒)の差を計算
var diff2Dates = dnumTarget - dnumNow;
if( dnumTarget < dnumNow ) {
// 期限が過ぎた場合は -1 を掛けて正の値に変換
diff2Dates *= -1;
}
// 差のミリ秒を、日数・時間・分・秒に分割
var dDays = diff2Dates / ( 1000 * 60 * 60 * 24 ); // 日数
diff2Dates = diff2Dates % ( 1000 * 60 * 60 * 24 );
var dHour = diff2Dates / ( 1000 * 60 * 60 ); // 時間
diff2Dates = diff2Dates % ( 1000 * 60 * 60 );
var dMin = diff2Dates / ( 1000 * 60 ); // 分
diff2Dates = diff2Dates % ( 1000 * 60 );
var dSec = diff2Dates / 1000; // 秒
var msg2 = Math.floor(dDays) + "日"
+ Math.floor(dHour) + "時間"
+ Math.floor(dMin) + "分"
+ Math.floor(dSec) + "秒";
// 表示文字列の作成
var msg;
if( dnumTarget > dnumNow ) {
// まだ期限が来ていない場合
msg = msg1 + "までは、あと" + msg2 + "です。";
}
else {
// 期限が過ぎた場合
msg = msg1 + "は、既に" + msg2 + "前に過ぎました。";
}
// 作成した文字列を表示
document.getElementById("RealtimeCountdownArea").innerHTML = msg;
document.getElementById("deadline.id").value = targetDate; #最重要記述
}
// 1秒ごとに実行
setInterval('showCountdown()',1000);
</script>
ここで、先程の:id => deadline.id
が活きてくる。このような記述を追加することで初めて、
javascriptで処理された結果をvalue(値)として受け取り、それを画面上で表示させることができる。これでようやく、タイマーを実装することができる。
###タイマーだけを表示させたい場合
なお、日時の記入欄を表示させたくない場合も考えられる。下の写真のように期限とタイマーの残り時間だけを表示させたいという場合には、
<p>期限 <%= @mission.deadline %>
<br>
<input type="hidden" id="userYear" value = "<%= @mission.deadline.year %>" >
<input type="hidden" id="userMonth"value = "<%= @mission.deadline.month %>" >
<input type="hidden" id="userDate" value = "<%= @mission.deadline.day %>" >
<input type="hidden" id="userHour" value = "<%= @mission.deadline.hour %>" >
<input type="hidden" id="userMin" value = "<%= @mission.deadline.min %>" >
<input type="hidden" id="userSec" value = "<%= @mission.deadline.sec %>" >
</p>
<p id="RealtimeCountdownArea" ></p>
<script>
function set2fig(num) {
// 数値が1桁だったら2桁の文字列にして返す
var ret;
if( num < 10 ) { ret = "0" + num; }
else { ret = num; }
return ret;
}
function isNumOrZero(num) {
// 数値でなかったら0にして返す
if( isNaN(num) ) { return 0; }
return num;
}
function showCountdown() {
// 現在日時を数値(1970-01-01 00:00:00からのミリ秒)に変換
var nowDate = new Date();
var dnumNow = nowDate.getTime();
// 指定日時を数値(1970-01-01 00:00:00からのミリ秒)に変換
var inputYear = document.getElementById("userYear").value;
var inputMonth = document.getElementById("userMonth").value - 1;
var inputDate = document.getElementById("userDate").value;
var inputHour = document.getElementById("userHour").value;
var inputMin = document.getElementById("userMin").value;
var inputSec = document.getElementById("userSec").value;
var targetDate = new Date( isNumOrZero(inputYear), isNumOrZero(inputMonth), isNumOrZero(inputDate), isNumOrZero(inputHour), isNumOrZero(inputMin), isNumOrZero(inputSec) );
var dnumTarget = targetDate.getTime();
// 表示を準備
var dlYear = targetDate.getFullYear();
var dlMonth = targetDate.getMonth() + 1;
var dlDate = targetDate.getDate();
var dlHour = targetDate.getHours();
var dlMin = targetDate.getMinutes();
var dlSec = targetDate.getSeconds();
var msg1 = "期限の" + dlYear + "/" + dlMonth + "/" + dlDate + " " + set2fig(dlHour) + ":" + set2fig(dlMin) + ":" + set2fig(dlSec);
// 引き算して日数(ミリ秒)の差を計算
var diff2Dates = dnumTarget - dnumNow;
if( dnumTarget < dnumNow ) {
// 期限が過ぎた場合は -1 を掛けて正の値に変換
diff2Dates *= -1;
}
// 差のミリ秒を、日数・時間・分・秒に分割
var dDays = diff2Dates / ( 1000 * 60 * 60 * 24 ); // 日数
diff2Dates = diff2Dates % ( 1000 * 60 * 60 * 24 );
var dHour = diff2Dates / ( 1000 * 60 * 60 ); // 時間
diff2Dates = diff2Dates % ( 1000 * 60 * 60 );
var dMin = diff2Dates / ( 1000 * 60 ); // 分
diff2Dates = diff2Dates % ( 1000 * 60 );
var dSec = diff2Dates / 1000; // 秒
var msg2 = Math.floor(dDays) + "日"
+ Math.floor(dHour) + "時間"
+ Math.floor(dMin) + "分"
+ Math.floor(dSec) + "秒";
// 表示文字列の作成
var msg;
if( dnumTarget > dnumNow ) {
// まだ期限が来ていない場合
msg = "Mission終了まで、あと" + msg2 ;
}
else {
// 期限が過ぎた場合
msg = msg1 + "は、既に" + msg2 + "前に過ぎました。";
}
// 作成した文字列を表示
document.getElementById("RealtimeCountdownArea").innerHTML = msg;
document.getElementById("deadline.id").value = targetDate;
}
// 1秒ごとに実行
setInterval('showCountdown()',1000);
</script>
上記のようにinput type = "hidden"
とすれば、記入欄が画面上に表示されないものの、<input type = ・・・>
の6つが削除されている訳ではないためこれで正常に起動する。
また、<input type="hidden" id="userYear" value = "<%= @mission.deadline.year %>" >
の
value="<%= @mission.deadline.year %>"
の部分がなぜそのような記述となるかについて説明する。これは、deadlineはdatetimeというデータ型をとるカラムであるのだが、datetimeには年・月・日・時・分・秒といった日時の情報が保存されているからである。したがって、上記のような記述でnew.html.erbの部分で記述した期限の日時が取り出せることになる。
#まとめ
なかなか難易度が高かったが、達成感はすごかった。少しでも参考にしていただけたら幸いである。
参考記事
https://www.nishishi.com/javascript-tips/realtime-countdown-deadline.html