【やってみよう】授業用チャイムを鳴らすWEBアプリを作る

プログラムを作ってみよう
チャイムを鳴らすページ

オリジナルプログラムを作るには考え方が大事

jQueryとJavaScriptの基本をおぼえたら、プログラムを作ってみましょう。
ここで難しいのが、オリジナルプログラム作成は「サンプルコードを写して動かす」先にはないということです。

まず「これを作ろう!」というものがあり、それを手持ちの知識と技術でどう実現するかを考えられることがプログラミングをできるようになるために必要な思考です。

この記事では、簡単なプログラムを作りる過程において考えていることをできるだけ言語化してみたいと思います。

この文を書いている時点では、きちんと最後までプログラムが完成できるかわかっておらず、試行錯誤しようと考えている状態です。

「授業用チャイム」を鳴らすためのプログラムを作りたい!

このサイトの管理人はとある組織でWEB制作の授業をしているのだが、
授業が始まった、終わったは時計をみて判断している・・・

チャイムを鳴らしたい・・・・

かといって、普段使うスマホにチャイムを設定すると授業が休みの日に鳴ってうざい・・・・

せや!
ブラウザのそのページを開いている間だけ動くアラームを作成すればいいんや!

ということで、今回はチャイムを鳴らすWebサイトを作ってみます。

「チャイムを鳴らす」プログラムの要件とは?

プログラムを作る際にはその機能を細分化して考える必要があります。
ここであいまいな言葉にしてしまうと、実際につくることができません。
(一人で作る場合は作りながら明確にすることもありますが、会社などで共同作業である場合はそうもいきませんので、自分がどんなものを作ろうとしているのかを明確に言語化します。)

「チャイムを鳴らす」プログラム要件

  • ブラウザ上で動作する
  • 特定の時刻になったらアラーム音が鳴る
  • 授業用のチャイムなので授業開始、授業終了の特定時刻にアラームを発動させる必要がある
  • アラームに気づいたら、アラームをなんらかの方法で止めることができる
  • 授業は複数あるため、複数の時刻が設定できる
  • 任意の時刻を設定できる
  • (できれば)ブラウザがアクティブでなくても動く→タブでひらいて後ろに置いておけばいいということ
  • 時刻設定は保存、保持しておける
  • 音は複数あり選べる

最後の要件は、同様のシステムを探した際に音が気に入らなくて使えなかったという経験からです。

技術と実現手段を考える

要件化して大事なのは、手段は置き換え可能である、ということです。

目的が満たせれば「手段はなんでもいい」ということです。
進めているとある地点でうまくいかないことがわかり、詰まってしまうことはよくあります。
手段は代替可能なので、他の方法はないか探るというのが大事になってきます。

自分の今の所考えている実装手段をリストアップしてみます。

  • HTML/CSS/JavaScriptでブラウザで動くプログラムとする
  • jQueryを使用する
  • アラーム設定された時刻がリストで表示される
  • inputタグ等でアラーム時刻を選択できる
  • 追加ボタンを押すことで新規のアラーム設定を追加できる(登録)
  • 登録された時刻になるとアラームが鳴る(毎秒チェックする)
  • アラームとしてなる音はチャイム、電子音を用意する。
  • 音源は再配布可能なものとする
  • 登録された時刻のリストはURL変数で管理する
  • サイトアクセス時にURL変数に有効な情報があった場合にリストにその情報が復元される
  • リストの時刻は削除できる(削除ボタン)
  • (できれば)時刻になったらOSのアラート通知が届くようにする
  • 登録時に音源を選べるようにする
  • 音源の視聴ができるボタンを設置する
  • OSの音量が低い場合はその旨を表示する
  • 時刻はpcに設定されたものではなく、極力正しいものを設定する
  • アラーム停止ボタンを押すと音が停止する

こんな感じです。
できるかよくわからないものも混ざっています。検証しながら作っていきます。
ここまでリストアップすれば、あとは記載された内容をひとつづつ実現していくだけですね。

この記事の最終的なデモはこちら

HTML/CSS/JavaScriptで要素を作る

では組んでいきます。


上記を最初のファイルとします。

HTML
<p>次の時刻にアラームが鳴ります</p>
<ul id="time-chime">
</ul>
<p>音声テスト : アラームを再生 / チャイムを再生 </p>
<input id="time-set" type="time">
<select id="music">
<option value="alarm">アラーム</option>
<option value="chime">チャイム</option>
</select>
<button id="registration">時刻の追加</button>

おそらく追加、編集が必要ですがこんなところでしょうか。

リストにアラームが鳴る時間を追加表示するプログラム(JavaScript)
$("#registration").click(
function () {
let time = $("#time-set").val();
//console.log(time);
let music = $("#music").val();
//console.log(music);
$("#time-chime").append("<li data-time='" + time + "' data-music='" + music + "'>" + time + " / " + music + "<button>削除</button></li>");
}
);

これでリストに要素が追加されます。
data属性で時間とアラームの情報も持たせています。

 

時間になったらアラームが鳴るプログラム(JavaScript)

1秒に一回チェックを行い、該当時刻だったらアラームが鳴るようにします。
止めるプログラムを入れていないので、音を止める時はリロードする必要があります。

1秒ごとに動作させる仕組みはsetInterval()を使います。
音源はNHKライブラリのものが商用でなければ再配布可能のようなのでこちらを利用したいと思います。

https://www2.nhk.or.jp/archives/movies/?id=D0002011518_00000
https://www2.nhk.or.jp/archives/movies/?id=D0002011514_00000

HTML (少し修正、説明文を追加)

<h1>チャイムを鳴らすページ</h1>
<p id="nowtime"></p>
<p>次の時刻にアラームが鳴ります</p>
<ul id="time-chime">
</ul>

<p>
時刻 : <input id="time-set" type="time" value="09:00">
サウンド : <select id="music">
<option value="chime">チャイム</option>
<option value="alarm">鳩時計</option>
</select>
<button id="registration">時刻の追加</button>
</p>
<p>音声テスト : <a id="sanple1" href="#">チャイムを再生</a> / <a id="sanple2" href="#">鳩時計を再生</a> </p>
<p>使用音源 : www.nhk.or.jp/archives/creative/rule.html</p>

JavaScript

setInterval(check, 1000);
function check() {
let date = new Date();
let now = ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2) + ':' + ('0' + date.getSeconds()).slice(-2);

$("#nowtime").html("現在時刻 : " + now);

$("#time-chime > li").each(function () {
let dataTime = $(this).attr("data-time");
let dataMusic = $(this).attr("data-music");
//console.log(dataTime);
//console.log(dataMusic);
if (dataTime == now) {
if (dataMusic == "chime") {
let audioChime = new Audio('sound/chime.m4a');
audioChime.play();
} else if (dataMusic == "alarm") {
let audioHato = new Audio('sound/hato.m4a');
audioHato.play();
}
}
});
}
$("#sanple1").click(function () {
let audioChime = new Audio('sound/chime.m4a');
audioChime.play();
return false;
});
$("#sanple2").click(function () {
let audioHato = new Audio('sound/chime.m4a');
audioHato.play();
return false;
});

これで時間になったら音が鳴る、基本的な動作は完成しました。
(さらっと書いてますが、いろんな技術を使っています。いろいろ説明端折ってます)

URLでアラームを鳴らす時間を保持する!

基本的な仕組みはできましたが、リロードするたびにデータがリセットされてしまっては、実用性がありません。

  • URLでデータを保存する
  • URLにデータがある場合はリストを復元する

この2つを実装していきます。

URLパラメータで実装します。
URLパラメータはURLの後に「?」で繋いで情報を持たせることができます。

今回は

?list=09:00c_09:50c_10:00a

といった感じでデータを持たせてみようかなと思います。

#registrationをクリックした際の動作に次のコードを追加します。

let listpram = [];
$("#time-chime > li").each(function () {
let dataTime = $(this).attr("data-time");
let dataMusic = $(this).attr("data-music");
listpram.push(dataTime + dataMusic);
});
str = listpram.join('_');

const url = new URL(window.location);
url.searchParams.set('list', str);
window.history.pushState(null, null, url);

window.history.pushState()という命令でURLが書き換えられるようなので、上記で持たせる形式になるように「?」以降を書き換える仕組みを追加しました。

リストを元データとしています。

次に、URLのデータがあった場合に、リストの内容を書き換えるように修正してみたいと思います。

こんな感じです。 URL内では「:」を使ってはいけないようなので、データの持たせ方も修正しています。

?list=09-00-00*chime_09-00-00*chime

こうするためにもろもろ直してまとめると次のコードになっています。

var urlParam = decodeURIComponent(location.search);
console.log(urlParam);
if (urlParam != "") {
urlParam = urlParam.replace("?list=", "");
ary = urlParam.split('_');
ary.forEach(function (item) {
item = item.replace(/-/g, ":");
let array = item.split('*');
$("#time-chime").append("<li data-time='" + array[0] + "' data-music='" + array[1] + "'>" + array[0].slice(0, -3) + " / " + array[1] + "<button>削除</button></li>")
});
alert("このサイトでは音が出ます。チャイムのカウントを開始してよいでしょうか?");
}

$("#registration").click(
function () {
let time = $("#time-set").val();
//console.log(time);
let music = $("#music").val();
//console.log(music);
$("#time-chime").append("<li data-time='" + time + ":00' data-music='" + music + "'>" + time + " / " + music + "<button>削除</button></li>");

let listpram = [];
$("#time-chime > li").each(function () {
let dataTime = $(this).attr("data-time");
dataTime = dataTime.replace(/:/g, "-");
let dataMusic = $(this).attr("data-music");
listpram.push(dataTime + "*" + dataMusic);
});
str = listpram.join('_');

const url = new URL(window.location);
url.searchParams.set('list', str);
window.history.pushState(null, null, url);
}
);

setInterval(check, 1000);
function check() {
let date = new Date();
let now = ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2) + ':' + ('0' + date.getSeconds()).slice(-2);

$("#nowtime").html("現在時刻 : " + now);

$("#time-chime > li").each(function () {
let dataTime = $(this).attr("data-time");
let dataMusic = $(this).attr("data-music");
//console.log(dataTime);
//console.log(dataMusic);
if (dataTime == now) {
if (dataMusic == "chime") {
let audioChime = new Audio('sound/chime.m4a');
audioChime.play();
} else if (dataMusic == "alarm") {
let audioHato = new Audio('sound/hato.m4a');
audioHato.play();
}
}
});
}
$("#sanple1").click(function () {
let audioChime = new Audio('sound/chime.m4a');
audioChime.play();
return false;
});
$("#sanple2").click(function () {
let audioHato = new Audio('sound/chime.m4a');
audioHato.play();
return false;
});

URLを読み込んだ際になにも操作しないとchromeの自動再生ポリシーに引っかかって、音声が自動再生されませんでした。
ユーザーが何かしら操作すればいいようなので、アラートを表示して「チャイムを動かしていい?」と聞くようにしました。
ここでOKさえユーザーに押して貰えば問題なく動くようです。
これでかなりまともに使えるようになってきました。
→追記:それでは足りなかったので、audioの作成を最初にクリックで行うようにしたら安定して動くようになりました。

あとは余分に追加してしまった項目を削除できるようにします。
それとなんだかんだを入れて次のようになりました

html

<h1>チャイムを鳴らすページ</h1>
<p id="nowtime"></p>
<p>次の時刻にアラームが鳴ります</p>
<ul id="time-chime">
</ul>

<p>
時刻 : <input id="time-set" type="time" value="09:00">
サウンド : <select id="music">
<option value="chime">チャイム</option>
<option value="alarm">鳩時計</option>
</select>
<button id="registration">時刻の追加</button>
</p>
<p>音声テスト : <a id="sanple1" href="#">チャイムを再生</a> / <a id="sanple2" href="#">鳩時計を再生</a> </p>
<p>使用音源 : www.nhk.or.jp/archives/creative/rule.html</p>

CSS

#restart {
background: rgb(216, 199, 199);
font-size: 20px;
border: none;
width: 300px;
padding: 20px;
border-radius: 30px;
}

.button-area {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 300px;
}

.button-area p {
color: #fff;
}

.cover {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.658);
}

クリックしたら要素の色が変わるスクリプトを追加

JavaScript

$(function () {
var urlParam = decodeURIComponent(location.search);
let audioArray = new Array;
if (urlParam != "") {
urlParam = urlParam.replace("?list=", "");
ary = urlParam.split('_');
ary.forEach(function (item) {
item = item.replace(/-/g, ":");
let array = item.split('*');
$("#time-chime").append("<li data-time='" + array[0] + "' data-music='" + array[1] + "'>" + array[0].slice(0, -3) + " / " + array[1] + "<button>削除</button></li>")
});
$("body").prepend("<div class='cover'><div class='button-area'><p>このページでは音声が流れます</p><button id='restart'>OK</button></div></div>")
}

$("#registration").click(
function () {
let time = $("#time-set").val();
//console.log(time);
let music = $("#music").val();
//console.log(music);
$("#time-chime").append("<li data-time='" + time + ":00' data-music='" + music + "'>" + time + " / " + music + "<button>削除</button></li>");

let listpram = [];
$("#time-chime > li").each(function () {
let dataTime = $(this).attr("data-time");
dataTime = dataTime.replace(/:/g, "-");
let dataMusic = $(this).attr("data-music");
listpram.push(dataTime + "*" + dataMusic);
});
str = listpram.join('_');
const url = new URL(window.location);
url.searchParams.set('list', str);
window.history.pushState(null, null, url);
location.reload();
}
);

setInterval(check, 1000);
function check() {
let date = new Date();
let now = ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2) + ':' + ('0' + date.getSeconds()).slice(-2);

$("#nowtime").html("現在時刻 : " + now);

$("#time-chime > li").each(function () {
let dataTime = $(this).attr("data-time");
//let dataMusic = $(this).attr("data-music");
//console.log(dataTime);
//console.log(dataMusic);
//console.log($(this).index());
if (dataTime == now) {
audioArray[$(this).index()].play();
}
});
}

$(document).on("click", "#time-chime button", function () {
$(this).parent().remove();
let listpram = [];
$("#time-chime > li").each(function () {
let dataTime = $(this).attr("data-time");
dataTime = dataTime.replace(/:/g, "-");
let dataMusic = $(this).attr("data-music");
listpram.push(dataTime + "*" + dataMusic);
});
str = listpram.join('_');

const url = new URL(window.location);
url.searchParams.set('list', str);
window.history.pushState(null, null, url);

console.log(str);
if (str == "") {
let urlOrigin = new URL(window.location);
window.history.pushState(null, null, urlOrigin.origin);
}
});
$("#restart").click(function () {
$(".cover").remove();
$("#time-chime > li").each(function () {
let dataMusic = $(this).attr("data-music");
if (dataMusic == "chime") {
let audios = new Audio('sound/chime.m4a');
audioArray.push(audios);
} else {
let audios = new Audio('sound/hato.m4a');
audioArray.push(audios);
}

});

});
$("#sanple1").click(function () {
let audioChime = new Audio('sound/chime.m4a');
audioChime.play();
return false;
});
$("#sanple2").click(function () {
let audioHato = new Audio('sound/chime.m4a');
audioHato.play();
return false;
});
});

いろいろ工夫しています。
登録した際に、それを正常動作させる仕組みを作るのがめんどくさかったので、登録したあとはかならずリロードするようにしました。
登録する作業は一度やったらあまりないと思いますので、そこまで使いづらくならない想定です。

要件チェック!

当初の目的はできているでしょうか?

  • ブラウザ上で動作する
  • 特定の時刻になったらアラーム音が鳴る
  • 授業用のチャイムなので授業開始、授業終了の特定時刻にアラームを発動させる必要がある
  • アラームに気づいたら、アラームをなんらかの方法で止めることができる
  • 授業は複数あるため、複数の時刻が設定できる
  • 任意の時刻を設定できる
  • (できれば)ブラウザがアクティブでなくても動く→タブでひらいて後ろに置いておけばいいということ
  • 時刻設定は保存、保持しておける
  • 音は複数あり選べる

停止の仕組みはないですが、つかってみてなくてもあまり支障はなさそうです。
バックグラウンド動作は検証が必要ですね・・・実際動くかどうかわかりません。

改善が必要な点

  • バックグラウンドで動くか未検証
  • 音声停止の仕組みがない
  • 設定によっては止めなければ鳴り止まない、という機能も必要か?
  • 時刻設定がない時の画面表示を改善したい(時刻が設定されていないから入れてね、というメッセージ)
  • 鳴る間際になったら、あと何秒で鳴ります、といった表示をする
  • 時刻リストの並び順は実際の時間順に並べ替えを自動でした方が使いやすそう
  • OSの通知機能を動かす
  • デバイス音量がミュート場合に警告する
  • URLパラメータ付きのURLをコピーするボタンを設置する

改善点は多くありそうですね。
今回のプログラム作成は以上としたいと思います。

 

タイトルとURLをコピーしました