Reactでタイマーアプリを作った

要件

要件は以下の通り。

・ユーザはタイマーを開始できる
・ユーザはタイマーを停止できる
・ユーザはタイマーを一時停止できる
・ユーザはタイマーをリセットできる

画面イメージ

画面イメージ

画面イメージはGIMPで書いた。某アプリっぽいがまぁこんな感じで良いだろう。(あとで気づいたのだが、このイメージには致命的な問題がある。)

UIの実装

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>

完成

完成したタイマー

動作はこんな感じ。円のプログレスバーを採用したのは視覚的に見やすくてよかったと思う。

まとめ

デザインの段階でユーザの立場になって操作するイメージを持たなかったので、「ボタンがない…!」みたいな自体に陥った。デザインを起こすのはそうした事故を起こさない意味もあるので、せっかくデザインをするならユーザの視点をもって評価すべきだと反省した。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA