2019年12月22日

プログラミング

ペースの計算を行う【Reactで作るマラソンペースメーカー】

目次

  1. はじめに
  2. ヘッダーをつけてみる
  3. 入力フォームを作る
  4. ペース計算処理を作る
  5. まとめ

はじめに

新しい技術にチャレンジし続けるpalanのアドベントカレンダーDay22です!

昨日は『サーバーレス時代のデータベース FaunaDB で GraphQL を試してみた』についての記事でした。

サーバーレス時代のデータベース FaunaDB で GraphQL を試してみた

このシリーズでは、Reactを使用しマラソンペース計算システムを作っています。
前回はReact Hooksを使う、という記事でReactの新機能であるhookを使用し、関数型コンポーネントの解説をしていきました。

React Hooksを使う【Reactで作るマラソンペースメーカー】

今回は、シリーズの続きとしてReactのhookも使いながら、実際にマラソンのペースを計算する機能を作っていきます。

ヘッダーをつけてみる

まず、今回作っていく完成形のUIはこちらです。
 2019-12-22 22.01.44.png (102.1 kB)

距離とタイム(時間・分)を設定することで、1kmあたりのペースが表示されるシステムです。
またせっかくMaterial-UIを使っているので、ヘッダーについてもMaterial-UIのコンポーネントを使っていきましょう。

Material-UIの公式サイトから、こちらのApp Barコンポーネントを見てみましょう。
 2019-12-22 22.17.13.png (159.6 kB)

こちらのSimple App Barが良さそうですね!

App.jsに(次回以降でコンポーネントをちゃんとファイル分けしていきます)、このようにインポートしていきます。


import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; import IconButton from '@material-ui/core/IconButton';

その後はこのようにUIを作っていきます。


<AppBar position="static"> <Toolbar> <IconButton edge="start" className={classes.menuButton} color="inherit" aria-label="menu"> <MenuIcon /> </IconButton> <Typography variant="h5" className={classes.title}> Marathon Pace Maker </Typography> </Toolbar> </AppBar>

入力フォームを作る

さて、次は入力フォームを作っていきます。
どれくらいの距離を走るのか、どれくらいの時間で走りたいのか(時間と分) を元に、実際に1kmあたり何分何秒で走ればよいのかを計算し出力していきます。

まず、どれくらいの距離を走るのか、については主要なフルマラソン(42.195km)、ハーフマラソン(21.0975km)、10km、5kmの4つから選択するようにしてみます。

選択式のフォームですが、TextFieldコンポーネントを使用していきます。
また実際に選択する選択肢部分はMenuItemを使用していきます。


import TextField from '@material-ui/core/TextField'; import MenuItem from '@material-ui/core/MenuItem'; ```` そして選択肢部分のラベルと値を配列に格納していきます。 ```jsx const distances = [ { value: 42.195, label: 'フルマラソン(42.195km)', }, { value: 30, label: '30km', }, { value: 21.0975, label: 'ハーフマラソン(21.0975km)', }, { value: 10, label: '10km', }, { value: 5, label: '5km', }, ];

次にstateの初期値を設定していきます。
後々に目標時間なども設定するのでvaluesに格納していきます。


const [values] = React.useState({ distance: 42.195, });

次に選択肢が選択された際に、値をvaluesに設定する関数を書いていきます。
値を数値型として設定する為に、Numberで数値型にします。


const handleChange = prop => event => { values[prop] = Number(event.target.value); }); };

そして肝心のフォーム部分のコードです。


<TextField id="distance" select label="距離" className={classes.textField} onChange={handleChange('distance')} value={values.distance} SelectProps={{ MenuProps: { className: classes.menu, }, }} helperText="走るレースの距離を選択してください" margin="normal" > {distances.map(option => ( <MenuItem key={option.value} value={option.value}> {option.label} </MenuItem> ))} </TextField>

主要なところを解説します。

select こちらがあることで、選択型のSelect APIを使用することができます。

こちらで先程のhandleChangeを呼び出しています。
valuesのキーであるdistanceを引数にしています。
onChange={handleChange('distance')}

こちらはTextFieldの値となります。
value={values.distance}

こちらで選択肢部分のメニューに対し、Propsを設定することができます。


SelectProps={{ MenuProps: { className: classes.menu, },

次に、入れ子の形で選択肢部分の値を設定していきます。


{distances.map(option => ( <MenuItem key={option.value} value={option.value}> {option.label} </MenuItem> ))}

配列distancesにvalueとlabelを設定しましたが、そちらを使用して選択肢を作っています。
mapで展開し、MenuItemのkey、valueに設定し、また表示上のラベルも設定しています。

これで距離の選択フォームができました。

次に目標時間、分のフォームを作っていきます。


<TextField id="standard-number" label="目標タイム(時間)" type="number" className={classes.numberField} onChange={handleChange('hour')} value={values.hour} InputLabelProps={{ shrink: true, }} InputProps={{ inputProps: { min: 0, max: 10 }, endAdornment: <InputAdornment position="end">h</InputAdornment>, }} margin="normal" /> <TextField id="standard-number" label="目標タイム(分)" type="number" className={classes.numberField} value={values.minute} onChange={handleChange('minute')} InputLabelProps={{ shrink: true, }} InputProps={{ inputProps: { min: 0, max: 59 }, endAdornment: <InputAdornment position="end">m</InputAdornment>, }} margin="normal" />

先程のフォームと異なる点は、select型でないくらいで、あとはほぼ一緒です。
InputProps というPropsを使うことで、入力に関するPropsを設定できます。
最小、最大値の設定はInputPropsの更にその中に値を設定する必要があるので注意です。

また、この最後にhやmといった単位のUIを作る為にInputAdornmentコンポーネントを使用しています。
 2019-12-22 23.04.59.png (14.4 kB)

ペース計算処理を作る

次に、ペースの計算処理を作っていきます。

hookのuseStateを使用し、paceMinuteとpaceSecondの初期値を設定しています。

  const [paces, setPaces] = React.useState({
    paceMinute: 5,
    paceSecond: 41,
  });

次に先程フォームの入力時につくった、handleChange関数に計算処理を入れていきます。
フォームの値を入力したら、ペース部分にペースが表示される仕様を実現する為のものです。


const handleChange = prop => event => { values[prop] = Number(event.target.value); // 分数を計算 const minute = (values.hour * 60) + values.minute; // 分数を距離で割り、1キロあたりの分数を計算 const paceMinute = (minute) / values.distance; // 分数の少数以下に60をかけ秒数を計算 const paceSecond = (paceMinute - Math.floor(paceMinute)) * 60 setPaces({ ...paces, paceMinute: Math.floor(paceMinute), paceSecond: Math.floor(paceSecond), }); };

コメントに書いたように、まずvaluesのminiteとhourから全体の分数を計算し、そこを距離であるdistanceで割ることで目標のペースが計算できます。
あとは小数点以下を丸めたり処理し、setPacesでpaceMinuteとpaceSecondのstateに値を設定していきます。

最後にUI部分のコードです。


<p> 目標をクリアする為には、1kmあたり <strong> {paces.paceMinute}分{paces.paceSecond}秒</strong> で走る必要があります。 </p>

これで先程のpaceMinuteとpaceSecondに設定されたら、正しくもく目標の値が計算されて表示されていきますね!

 2019-12-22 23.16.10.png (102.5 kB)

 2019-12-22 23.16.52.png (97.4 kB)

こんな形で、それぞれの値を変更しても連動しって正しく目標ペースが表示されるようになりました!

まとめ

前回のReact Hooksを実際に使いながら、今回は計算処理を作っていきました。
まだコンポーネント分割できていなかったり、また動的にペースを変更したい(〜キロまではこれくらいのペース、そこから先はこれくらいのペースといった具合)など、まだ改善していく必要があります。

次回以降に対応していきますので、お楽しみに!
ソースコードや実際の公開されているURLはこちらです。
https://github.com/palan-inc/marathon-pace-maker
https://palan-inc.github.io/marathon-pace-maker/

0

0

AUTHOR

eishis

Eishi Saito 総務

SIerやスタートアップ、フリーランスを経て2016年11月にeishis, Inc.を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。

アプリでもっと便利に!気になる記事をチェック!

記事のお気に入り登録やランキングが表示される昨日に対応!毎日の情報収集や調べ物にもっと身近なメディアになりました。

簡単に自分で作れるWebAR

「palanAR」はオンラインで簡単に作れるWebAR作成ツールです。WebARとはアプリを使用せずに、Webサイト上でARを体験できる新しい技術です。

palanARへ
palanar

palanはWebARの開発を
行っています

弊社では企画からサービスの公開終了まで一緒に関わらせていただきます。 企画からシステム開発、3DCG、デザインまで一貫して承ります。

webar_waterpark

palanでは一緒に働く仲間を募集しています

正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。

話を聞いてみたい

運営メンバー

eishis

Eishi Saito 総務

SIerやスタートアップ、フリーランスを経て2016年11月にeishis, Inc.を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。

sasakki デザイナー

アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。

しまだ

しまだ デザイナー

WebAR/VRのデザインと3DCG制作がメインです。 肩書きは「アニメ案件に関わりたいデザイナー」。

Miu マーケター

ドイツでWEBマーケティングしています。

しんのき エンジニア

主に React Native を使ったアプリ開発と AWS や Firebase を使ったサーバーレスアーキテクチャを担当しています。元々はインフラとかPHPをやっていました。

yamakawa

やまかわたかし デザイナー

フロントエンドデザイナー。デザインからHTML / CSS、JSの実装を担当しています。最近はReact NativeやReact360をよく触っています。

furuya エンジニア

サーバーサイド、フロントエンド、Unityと色々手を出してる雑食系エンジニア。ReactNativeが最近のマイブーム。

Sayaka Osanai デザイナー

Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。

はらた

はらた エンジニア

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

うえまつゆい エンジニア

サーバーサイドエンジニアからフロントエンドエンジニアになりました。主にReact Nativeでのアプリ開発をしています。

kobori

こぼり ともろう エンジニア

サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。

sasai

ささい エンジニア

フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。

damien

Damien

WebAR/VRを中心に企画やディレクションやエンジニアもちょっとやっています。森に住んでいます。

デザイナーゲスト

ゲスト デザイナー

CONTACT PAGE TOP