【JavaScript】アラーム付き時計

サブディスプレイに、タスクのメモを書いて時刻をアラーム表示できたらなぁ・・・
という思いから、ちょいと書いてみたソースコードです。
ソースコードをHTMLファイルに保存してどこかに配置して開けば使えます。ご自由に流用してください。

ここでできること

  • ブラウザにアラーム付き時計を表示させる
  • テキストエリアに HH:MMで始まる文字列(タスク)を入力すると、アラーム表示対象にできる
  • アラームは 時計の文字 を指定の時刻より数分前までは DeepPink、指定時刻から数分後までは Red にして表現する
  • アラームは 00:00 から 24:59 までを指定できる
  • アラームは 10から1分前、0から5分後まで設定できる
  • テキストエリアやアラームは ブラウザのローカルストレージ領域に記録できる
  • 記録は値を変更したときに自動的に記録できる
    • テキストエリアは枠外にフォーカスが移動すると変更した扱いになる

イメージ

clock

ソースコード

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<title>Task Clock</title>
<style type="text/css">
.clock {background-color:#FFFFFF;text-align:center;display:inline-block;width:100%; height:18em;}

.clockMain {font-size:13em; font-weight:bold;}
.def {color:black;}
.warnBef {color:deeppink;}
.warnAft {color:red;}

.calYm {color:black; font-size:5em;}
.calAmPm {color:black; font-size:4em;}

.alarmSetting {font-size:1em; font-weight:bold;}
.setBef {float:left;margin:0px 0px 0px 10px;}
.setAft {float:left;margin:0px 0px 0px 0px;}
</style>
</head>
<body>
<div class="clock">
  <div style="float:left;margin:40px 100px 0px 10px;">
    <span id="calDay" class="calYm">00/00</span><br/>
    <span id="calTm" class="calAmPm">PM</span>
    <br/>
    <div class="alarmSetting">
      <div class="setBef warnBef">
      Before <select id="beforeAlm"><option>1</option><option>2</option><option>3</option><option>4</option><option>5</option><option>6</option><option>7</option><option>8</option><option>9</option><option>10</option></select> min & 
      </div>
      <div class="setAft warnAft">
      After <select id="afterAlm"><option>0</option><option>1</option><option>2</option><option>3</option><option>4</option><option>5</option></select> min
      </div>
    </div>
  </div>
  <div class="clockMain" style="float:left;">
    <span id="clock" style="margin:0px;" class="def">00:00:00</span> 
  </div>
</div>
<textarea id="todo" style="font-size:10em;width:100%;height:65vh;">
</textarea>
</body>
<script>
'use strict';

//定義
var clock = document.getElementById('clock');
var calDay = document.getElementById('calDay');
var calTm = document.getElementById('calTm');
var txtTodo = document.getElementById('todo');
var selBef = document.getElementById('beforeAlm');
var selAft = document.getElementById('afterAlm');

var timerId;
var clockDate; //時計に表示する日付オブジェクト
var alarmBefMin = 5; //アラーム(事前)
var alarmAftMin = 2; //アラーム(事後)
var alarmTimes = []; //アラームリスト(許容:00:00-24:59)

function zeroPad(value, digit) {
    if(!value) {
        return '0'.repeat(digit);
    }
    return String(value).padStart(digit, '0');
}

//時計の表示
function updateClock(){
    clockDate = new Date(); //現在時刻
    var dm = clockDate.getMonth()+1;
    var dt = clockDate.getDate();
    var h = clockDate.getHours();
    var m = clockDate.getMinutes();
    var s = clockDate.getSeconds();
    calDay.textContent = zeroPad(dm, 2) + '/' + zeroPad(dt, 2)
    clock.textContent = zeroPad(h, 2) + ':' + zeroPad(m, 2) + ':' + zeroPad(s, 2);
    calTm.textContent = ( h < 13 ? "AM" : "PM"); 
}
//テキストエリアからアラーム対象(先頭が時刻(HH:MM))を取得
function setAlarmArray() {
    alarmTimes.length = 0;
    alarmTimes = new Array();

    const pattern = /^(0[0-9]|1[0-9]|2[0-4])(:[0-5][0-9])/g;
    const textline = txtTodo.value.split(/\r?\n/);
    for ( var line of textline ) {
        var hourmin = line.match(pattern);
        if( hourmin != undefined ) {
            if ( !alarmTimes.includes( String(hourmin) ) ) {
                alarmTimes.push( String(hourmin) );
            }
        }
    }
}
//セレクトからアラーム時間を設定
function setAlarmRange() {
    alarmBefMin = selBef.value;
    alarmAftMin = selAft.value;
}
//時刻から日付オブジェクトを取得
function convertToDate( hourmin, addDate ) {
    var dt = new Date();
    var ymd = dt.getFullYear() +'/'+ zeroPad((dt.getMonth()+1), 2) +'/'+ zeroPad(dt.getDate() + addDate, 2);
    var date = new Date(ymd + ' '+ hourmin +':00.000');
    return date;
}
//アラーム表示の判定
function differAlarm() {
    var h = clockDate.getHours();  
    var alarmDiv = "def";
    for(var alarm of alarmTimes) {
        var baseDate;
        if ( alarm.indexOf("24:") > -1 || alarm.indexOf("00:") > -1 ) {
            //24時台が設定されていたら 0時 として比較する(今23時なら翌日0時)
            baseDate = convertToDate( alarm.replace("24:","00:"), (h == 23) ? 1 : 0 );
        }
        else if ( h == 0 && alarm.indexOf("23:") > -1 ) {
            //今0時の場合、23時台が設定されていたら昨日23時と比較する 
            baseDate = convertToDate( alarm, -1 );
        }
        else {
            baseDate = convertToDate( alarm, 0 );
        }
        //時計とアラーム時刻を比較し ミリ秒 から 分 に変換する
        var diffMin = Math.floor((baseDate.getTime() - clockDate.getTime()) / (60 * 1000)) + 1;
        if ( diffMin <= alarmBefMin && diffMin > 0 ) {
            alarmDiv = "warnBef";
            break;
        }
        else if ( diffMin >= ( alarmAftMin * -1) && diffMin <= 0 ) {
            alarmDiv = "warnAft";
        }
    }
    clock.className = alarmDiv;
}
//時計のメイン処理
function clockMain(){
    timerId = setTimeout(function(){
      updateClock();
      differAlarm();
      clockMain();
    },250);
}
//ページロード時の処理
window.addEventListener('load',function(){
  // テキストエリアにストレージの内容を読み込む
  const todoValue = localStorage.getItem("todo");
  txtTodo.value = todoValue;
  // セレクトにストレージの内容を設定する
  const befAlarmValue = localStorage.getItem("alarmBefMin");
  const aftAlarmValue = localStorage.getItem("alarmAftMin");
  selBef.value = befAlarmValue == null ? "5" : befAlarmValue;
  selAft.value = aftAlarmValue == null ? "5" : aftAlarmValue;
  // アラーム対象の設定
  setAlarmArray();
  setAlarmRange();
  // 時計のメイン処理
  clockMain();
});

//テキストエリアの変更時の処理
txtTodo.addEventListener("change", function(e){
  // ストレージにテキストエリアの内容を書き込む
  localStorage.setItem("todo", e.target.value);
  // アラーム対象の設定
  setAlarmArray();
});

//
selBef.addEventListener("change", function(e){
  // ストレージに選択内容を書き込む
  localStorage.setItem("alarmBefMin", e.target.value);
  setAlarmRange();
});
//
selAft.addEventListener("change", function(e){
  // ストレージに選択内容を書き込む
  localStorage.setItem("alarmAftMin", e.target.value);
  setAlarmRange();
});
</script>
</html>
タイトルとURLをコピーしました