暇人の寝室
技術系の記事や読書・アニメの感想などを投稿します。
プログラミング
要件は以下の通り。
・ユーザはタイマーを開始できる
・ユーザはタイマーを停止できる
・ユーザはタイマーを一時停止できる
・ユーザはタイマーをリセットできる
画面イメージはGIMPで書いた。某アプリっぽいがまぁこんな感じで良いだろう。(あとで気づいたのだが、このイメージには致命的な問題がある。)
Reactを用いてUIを実装する。時間表示とプログレスバーの部分はSVGで描画することにした。
// Timer.js(抜粋)
return (
<svg
height={radius * 2}
width={radius * 2}
>
<circle
stroke="#3CB371"
fill="transparent"
strokeWidth={ stroke }
strokeDasharray={ this.circumference + ' ' + this.circumference }
style={ { strokeDashoffset } }
transform={`rotate(-90,${radius},${radius})`}
r={ this.normalizedRadius }
cx={ radius }
cy={ radius }
/>
<text
x={radius}
y={radius}
style={{fontSize: '60px'}}
fill='#3CB371'
textAnchor='middle'
dominantBaseline='central'
>{text}</text>
</svg>
ReactのStateで
・開始ボタンを押した時刻
・タイマーの設定時間
・開始ボタンを押してからの経過時間
・開始ボタンを押す前の経過時間(一時停止をした場合に経過時間を保持するため)
・setIntervalのID
を管理して、タイマー機能を実装する。
時間のカウントはsetIntervalで1000ミリ秒ごとに開始ボタンを押した時刻と現在時刻の差分を取得し、Stateに経過時間として渡すことで行う。setIntervalは開始ボタンを押したときに発火させる。
ここまで実装して、設定時間を変えられない致命的な欠陥に気づいた。なので、タイマーの時間表示の上下にボタンを追加して対応した。こちらはpolygonを使って三角を描画し、そこにonChangeTimeハンドラを適用した。
// Timer.js(抜粋)
return (
<svg
height={radius * 2}
width={radius * 2}
>
<circle
stroke="#3CB371"
fill="transparent"
strokeWidth={ stroke }
strokeDasharray={ this.circumference + ' ' + this.circumference }
style={ { strokeDashoffset } }
transform={`rotate(-90,${radius},${radius})`}
r={ this.normalizedRadius }
cx={ radius }
cy={ radius }
/>
<polygon
points="110 160,130 160,120 140"
fill="#3CB371"
onClick={()=>{onChangeTime(3600)}}
/>
<polygon
points="190 160,210 160,200 140"
fill="#3CB371"
onClick={()=>{onChangeTime(60)}}
/>
<polygon
points="270 160,290 160,280 140"
fill="#3CB371"
onClick={()=>{onChangeTime(1)}}
/>
<polygon
points="110 240,130 240,120 260"
fill="#3CB371"
onClick={()=>{onChangeTime(-3600)}}
/>
<polygon
points="190 240,210 240,200 260"
fill="#3CB371"
onClick={()=>{onChangeTime(-60)}}
/>
<polygon
points="270 240,290 240,280 260"
fill="#3CB371"
onClick={()=>{onChangeTime(-1)}}
/>
<text
x={radius}
y={radius}
style={{fontSize: '60px'}}
fill='#3CB371'
textAnchor='middle'
dominantBaseline='central'
>{text}</text>
</svg>
動作はこんな感じ。円のプログレスバーを採用したのは視覚的に見やすくてよかったと思う。
デザインの段階でユーザの立場になって操作するイメージを持たなかったので、「ボタンがない…!」みたいな自体に陥った。デザインを起こすのはそうした事故を起こさない意味もあるので、せっかくデザインをするならユーザの視点をもって評価すべきだと反省した。
ソースコードはこちら。