Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9008

エイプリルフールに間に合ったw ウソは言わないけど絶妙に役に立たない時計アプリ

$
0
0

ざっくり時計

開発の経緯

いわゆるITドカタと呼ばれる某大手系ソフトウェア会社を退職して十余年、最近のイカしたモダンなブラウザ機能にはトンと疎くなってしまったなぁ。たまには最新の(に近い)カッコイイCSSとかJavaScriptとかを使いこなしてみたいぜ... と思っていた今日この頃。
某テレビ番組で、所ジョージさんが「DAITAI時計」というのを紹介していたのを見て、これすげー面白いじゃん、でも自分の通常の時間感覚として、今の時刻は?と聞かれて「XX時前後です」や「YY時前半です」とは言わないよなーって思ったので、自分の感覚に会う「ざっくりした時刻表現」が表示できる時計を作ってみました。

作ろうと思い立ったのが3月初めくらい。若い勢いのあるプログラマであれば、こんなの1日かそこらでできちゃうんだろうけど、ジジィには瞬発力が無い。なんだかんだと他のことしながらスキマ時間でチマチマ作って、ようやく4月1日に間に合った。Qiitaのユーザ登録もさっきした。

設計方針

開発の方針としては、以下の5点:
* 分・秒単位の数字は出さずに「それなりに」現在時刻が把握できる。
* おやつの時間とか「特別な時間帯」にメッセージを出すことも可能にする。
* 1個のHTMLファイルで完結。ファイルを一個コピーして開くだけで使える。
* 誰でもテキストエディタで開いて、自分の時間感覚に合わせてカスタマイズが容易に。
* 文字色、背景色、マージンなどのデザインはCSS部分でいじれて、ロジック部分はなるべく影響なしに。
あと、できるかどうか分からんけど、もし日本語以外の他言語化に対応しようとしたらやりやすいように、Intl.DateTimeFormatオブジェクトを使っておいた。

ご使用について

エイプリルフールのプレゼント(?)です。以下のソースを全選択してファイルに保存してお使いください。まぁ良くわかんないけどクリエイティブ・コモンズライセンスにしといたから、ライセンスを継承してくれれば、営利目的に使ってくれても、全然気にしません。
プログラミングスキルは、ジジィになった現在でもまぁまぁの水準だと自負してるけど、美的センスはカラッキシなので、見た目の派手さが全く無いですね。そっち方面に長けた方は、うまいことデザインを変えて販売してみたらいかがでしょうかw

余談

ちなみに、Edge(Windows10)、Safari(macOS)、Firefox(macOS)、Vivaldi(macOS)で動作確認済み。Chromeや他のブラウザとか、スマホ・タブレットとかで開いたらどうなるかは知らない... googleさんに山ほど個人情報を吸い取られるのはかなわんので、chromeはインストールしない主義w
ちなみにpart2、動作テスト中に「Firefoxでは、ja-JPロケールで、hour:'2-digit'が効かない」というバグを見つけてしまった。Bugzillaには去年すでに報告が上がってるようだ。
ちなみにpart3、’Zackly というのが、英語のスラング(?)でexactlyの意味だっていうのも、ちょっとシャレが利いてて面白いよねw(自画自賛)

zacly.html
<!DOCTYPE html><html><head><metacharset="utf-8"><title>ZACKLY Clock</title><!--
    ざっくり時計 V1.0 -- ざっくりとした現在時刻を表示する
    inspired by 所ジョージさんのDAITAI時計
--><script type="text/javascript">varlastFoundStr="";varlastFoundTime=-1;functiongetZacklyStr(dateTime){//Dateオブジェクトを渡して、文字盤に表示させる文字列の作成をする//毎分呼ばれるけど、前回と同じ内容で書き換え必要なしならnullを返すconstmapForZackly=newMap([//「分」の数字(未満)と表示文字列の対応表// %dはその時点の「時」、%eは次の「時」に置換される[1,"%d丁度"],//0分台は「丁度」[5,"%dごろ"],//1分台〜5分未満は「ごろ」...以下同様[15,"%d過ぎ"],[25,"%d半前"],[29,"%d半近く"],[31,"%d半"],[35,"%d半ごろ"],[45,"%d半過ぎ"],[55,"%e前"],[59,"%e近く"],[60,"%e丁度"]]);constmapForSpecialPeriod=newMap([//スペシャル・ピリオド「特別な時間帯」と表示文字列の対応表//Mapのkey 1個目の数字は曜日(0=日曜〜6=土曜)//2個目、3個目は表示される時刻の下限と上限(24時間制)[["0123456","12:00","12:15"],"お昼ご飯の時間だよ〜"],[["12345","09:50","09:59"],"まもなく%e、休憩時間になります"],[["12345","14:50","14:59"],"まもなく%e、おやつの時間で〜す"],[["0","17:55","17:59"],"もうすぐ日曜%e、ちびまる子ちゃん始まるよ〜!"]]);consth23format=newIntl.DateTimeFormat('ja-JP',{hour12:false,hour:'numeric'});consth11format=newIntl.DateTimeFormat('ja-JP',{hour12:true,hour:'numeric'});consthhmmformat=newIntl.DateTimeFormat('en-US',{hour12:false,hour:'2-digit',minute:'2-digit'});// Firefoxでは、ja-JPロケールで、hour:'2-digit'が効かないのでしかたなくen-US//console.log("getZacklyStr: " + dateTime.toString());letstrFound="";letmins=dateTime.getMinutes();lettimeMilli=dateTime.getTime();letdayOfWeek=dateTime.getDay();lethhmm=hhmmformat.format(dateTime);//console.log(`getZacklyStr: dayOfWeek=${dayOfWeek} hhmm=${hhmm}`);//console.log(`getZacklyStr: options=${JSON.stringify(hhmmformat.resolvedOptions())}`);if(modeSpecial)for(let[spec,val]ofmapForSpecialPeriod){if(spec.shift().includes(dayOfWeek)&&spec.shift()<=hhmm&&spec.shift()>=hhmm){if(lastFoundStr===val)returnnull;strFound=val;lastFoundStr=val;lastFoundTime=timeMilli;break;}}if(strFound==="")for(let[key,val]ofmapForZackly){if(mins<key){if(lastFoundStr===val&&(timeMilli-lastFoundTime)<3600*1000)returnnull;strFound=val;lastFoundStr=val;lastFoundTime=timeMilli;break;}}if(strFound.includes("%d")){returnstrFound.replace("%d",(mode12H?h11format:h23format).format(dateTime));}elseif(strFound.includes("%e")){dateTime.setHours(dateTime.getHours()+1);returnstrFound.replace("%e",(mode12H?h11format:h23format).format(dateTime));}returnstrFound;}functionupdateFace(){//文字盤の表示内容更新letzacklyStr=getZacklyStr(newDate());if(zacklyStr!=null){//console.log(`updateFace: ${zacklyStr}`);letface=document.getElementById("face");while(face.hasChildNodes())face.removeChild(face.firstChild);face.append(zacklyStr);//フォントを縮める調整をしてあったら、再度選択されたサイズに戻してresizeBoxへif(isAdjusted)changeSize();elseresizeBox();}}functionupdateByTimer(){//1分ごとのタイマーで文字盤書き換えupdateFace();setTimeout(updateByTimer,(60-newDate().getSeconds())*1000);}varmode12H=false;functiontoggle12H(){//12時間制/24時間制の切り替えmode12H=!mode12H;//console.log(`toggle12H: ${mode12H}`);document.getElementById("mode12H").setAttribute("value",mode12H?"on":"off");lastFoundStr="";updateFace();}varmodeDark=false;functiontoggleDark(){//ダークモード/ライトモードの切り替えmodeDark=!modeDark;//console.log(`toggleDark: ${modeDark}`);document.getElementById("modeDark").setAttribute("value",modeDark?"on":"off");document.getElementById("outer-box").setAttribute("class",modeDark?"darkMode":"lightMode");}varmodeSpecial=false;functiontoggleSpecial(){//「特別な時間帯」表示/非表示の切り替えmodeSpecial=!modeSpecial;//console.log(`toggleSpecial: ${modeSpecial}`);document.getElementById("modeSpecial").setAttribute("value",modeSpecial?"on":"off");updateFace();}functiontoggleAbout(){//about表示/非表示の切り替えletabout=document.getElementById("about");about.setAttribute("value",about.value=="on"?"off":"on");document.getElementById("license").style.display=about.value=="on"?"block":"none";}functionautoHideAbout(){//about表示は15秒ほどで自動的に消えるようにするletabout=document.getElementById("about");if(about.value==="on"){//console.log("autoHideAbout: start timer")setTimeout(()=>{//console.log("autoHideAbout: called by timer")if(about.value==="on")toggleAbout();},15*1000);}}functionchangeSize(){//メニューで選択されたフォントサイズを設定varselect=document.getElementById("selectSize");//console.log(`chageSize: ${select.value}`);document.getElementById("face").style.fontSize=select.value;isAdjusted=false;resizeBox();}varadjuster=null;varisAdjusted=false;functionresizeBox(){//ウィンドウのサイズに応じて文字盤の位置調整とフォントサイズ調整をするletbox=document.getElementById("outer-box");box.style.width=(window.innerWidth-marginOfBody)+"px";box.style.height=(window.innerHeight-marginOfBody)+"px";//console.log(`resizeBox: ${box.style.width}, ${box.style.height}`);letface=document.getElementById("face");if(box.clientHeight>=face.clientHeight){face.style.top=((box.clientHeight-face.clientHeight)/2)+"px";}elseif(adjuster==null){//文字盤がウィンドウに収まらない時、フォントを縮める//時間がかかる(かもしれない)ので非同期処理adjuster=newPromise((resolve,reject)=>{while(box.clientHeight<face.clientHeight){letcurSize=face.style.fontSize||defFontSize;letnewSize=Number.parseInt(curSize,10)-10;if(newSize<20){reject(window.innerHeight);return;}//console.log(`adjuster: ${curSize} -> ${newSize}`);face.style.fontSize=newSize+"px";isAdjusted=true;}resolve();}).catch((height)=>{//console.log(`adjuster: failed -- window height: ${height}`);}).finally(()=>{face.style.top=((box.clientHeight-face.clientHeight)/2)+"px";adjuster=null;});}}varmarginOfBody=16;vardefFontSize=80;functioninitialize(){//スタイル指定に使うグローバル変数の初期化for(letrulesofdocument.styleSheets[0].cssRules){if(rules.selectorText=="body"&&rules.style.margin!=""){//resizeBoxで使うマージン → body要素のマージン*2marginOfBody=Number.parseInt(rules.style.margin,10)*2;}if(rules.selectorText=="#face"&&rules.style.fontSize!=""){//文字盤のデフォルトフォントサイズdefFontSize=rules.style.fontSize;}}//console.log(`marginOfBody: ${marginOfBody}, defFontSize: ${defFontSize}`);updateByTimer();}window.onload=initialize;window.onresize=resizeBox;</script><style type="text/css">div.lightMode{color:midnightblue;background-color:mintcream;}div.darkMode{color:mintcream;background-color:midnightblue;}#face{position:relative;font-family:"しっぽり明朝B1","MS P明朝","Times New Roman",serif;font-weight:bold;text-shadow:5px3px4pxgray;font-size:80px;line-height:1.1;padding:0px10px;text-align:center;}#control{border:1pxgraysolid;background-color:lightgrey;border-radius:6px;padding:10px;width:65px;height:20px;overflow:hidden;position:absolute;left:20px;bottom:-33px;transition:bottom.5s.2s;}#control:hover{width:280px;bottom:0px;background-color:mintcream;}button,select{position:absolute;margin:0px;}button[value='on']{background-color:plum;border-style:inset;}button[value='off']{border-style:outset;}#control:hover#mode12H{left:16px;}#control:hover#modeDark{left:62px;}#control:hover#modeSpecial{left:112px;}#control:hover#selectSize{left:160px;}#control:hover#about{left:260px;}#license{display:none;position:absolute;padding:10px;top:0px;left:0px;right:0px;background-color:lightgray;font-size:12px;opacity:0.7;}#licenseimg{float:left;}body{background-color:lightgray;margin:8px;overflow:hidden;}html{box-sizing:border-box;}</style></head><body><divid="outer-box"class="lightMode"><divid="face"></div></div><divid="control"onmouseleave="autoHideAbout()"><buttonid="mode12H"value="off"onclick="toggle12H()"title="12時間制/24時間制を切り替えます">12H</button><buttonid="modeDark"value="off"onclick="toggleDark()"title="ダークモード/ライトモードを切り替えます">Dark</button><buttonid="modeSpecial"value="off"onclick="toggleSpecial()"title="スペシャル・ピリオドの表示On/Offを切り替えます">S.P.</button><buttonid="about"value="off"onclick="toggleAbout()"title="About ZACKLY Clock">i</button><selectid="selectSize"onchange="changeSize()"title="最大のフォントサイズを指定します"><optionvalue="20px">20px</option><optionvalue="40px">40px</option><optionvalue="60px">60px</option><optionvalue="80px"selected>80px</option><optionvalue="100px">100px</option><optionvalue="120px">120px</option><optionvalue="150px">150px</option></select></div><divid="license"><arel="license"href="http://creativecommons.org/licenses/by-sa/4.0/"><imgalt="クリエイティブ・コモンズ・ライセンス"src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png"width="88"height="31"loading="lazy"/></a><spanxmlns:cc="http://creativecommons.org/ns#"property="cc:attributionName">sigmoc</span>作『<spanxmlns:dct="http://purl.org/dc/terms/"href="http://purl.org/dc/dcmitype/InteractiveResource"property="dct:title"rel="dct:type">ZACKLY Clock V1.0</span>』は <arel="license"href="http://creativecommons.org/licenses/by-sa/4.0/">クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンス</a>で提供されています。</div></body></html>

Viewing all articles
Browse latest Browse all 9008

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>