Vue.js でタイピング練習ウェブアプリを作ってみた

Vue.jsで、多言語タイピング練習ウェブアプリを作成しました。

URL:  https://www.typing-up.pro/

この記事では、主に技術的なところでポイントとなる部分を、備忘録的に解説していきます。githubでコードを公開しているので、細かいところは省き、ポイントのみの解説です。

対象は、Vue.jsの基礎をある程度知っている方です。

プログラミングの練習課題として、タイピング練習ソフトを作ることは定番かと思います。あくまで解法の一つですが、どうぞ参考にしてください。

[Githubリポジトリはこちら](https://github.com/seimatz/typingup-pro)

※ソースコードは、ローカル環境での個人利用に限り改変自由です。



## 仕様の概要

このアプリの特徴は、主に次の２つです。

- **オリジナル問題の利用**：　自分で作った問題集をアップロードして、タイピング練習をすることができます。ユーザー登録は必要ありません。

- **多言語対応**：　アルファベット入力をする言語であれば、おおよそどの言語でも対応しています。自然言語だけでなく、プログラミング言語なども対応しています。日本語のように変換が必要だったり、複雑な入力体系を持つ言語は対応していません。

使い方の詳細は、ブログ記事をご覧ください。

[【オリジナル問題が使える！】多言語タイピング練習アプリを公開しました](https://blogdajapan.blogspot.com/2021/09/blog-post.html)



## タイピング正誤判定のロジック

まずタイピングソフトの肝となる、キーの正誤判定のロジックです。

大まかに、次の３ステップで正誤判定をしています。



1. data で問題文、入力されたテキストを定義する
2.  問題文の文字を１文字ずつ分解し配列に変換
3. 入力された文字と問題文の配列の中身を比較し、一致していれば正、不一致であれば誤とする



### 1.data 定義

```javascript
  data () {
    return {
      question: 'This is test', //問題文。サンプルテキストを入れています
      typed: '', //入力された文字
      mistyped: '', //ミスタイプならこちらへ表示
      charnum: 0, //正答入力された文字数をカウント
      wordtoArray: "", //問題文を１文字ずつ分解した配列を格納
    }
  },

```



### 2.問題文の分解

wordtoArrayに、問題文を１文字ずつバラバラにして格納します。

```javascript
　this.wordtoArray = this.question.split('');
```



### 3.問題文と入力文字の比較

キー入力は、mountedのイベントリスナで監視しています。v-modelでtexareaへの入力を監視する方法もあります。

```javascript
  mounted: function() {
    document.addEventListener('keypress', this.onKeyDown);
  }
```



methodsのonKeydownで正誤判定をしています。charnumでいま何文字目まで入力しているか把握しています。wordtoArrayのn文字目と比較するというロジックです。

```javascript
    onKeyDown: function(e) {
        //正しい入力の時
        if(this.wordtoArray[this.charnum] == e.key){
          this.typed += e.key; //入力された文字列
          this.charnum += 1;　//現在n文字目
           //間違えた時
        } else {
          this.mistyped = e.key;
        }
    }
```



あとはHTMLを設定すれば、基本の仕組みは完成です。

```html
<p>{{question}}<p>
<textarea placeholder="type here" autofocus readonly>{{typed}}</textarea>
<p>{{mistyped}}<p>
```



## 問題文の読み込み

基本的な正誤判定ができたら、次は問題文を読み込む仕組みを作ります。

問題文はコードに直接記載するのではなく、外部ファイルから読み込むようにしました。

ステップとしてはおおよそ次の通りです。



1. assetまたはstaticフォルダにcsvファイルを設置
2. csvファイルをfetchで読み込み
3. 最大問題数までランダムで取得しdataへ



２のステップからコードを載せます。



### 2.csvファイルをfetchで読み込み

筆者はwebpackを使いましたが、外部ファイル設置はassetでは上手くいかず、staticに設置しました。

```javascript
      fetch('/static/english.csv')
      .then(res => res.text())
      .then(data => (this.questions_all = this.convertCsvStringToArray(data)))
    }
```

参考：[Vue.js で CSV ファイルの読み込み](https://www.nightswinger.dev/2020/03/load-csv-file-in-vuejs/)



methodsのconvertCsvStringToArray問題文を抽出します。

```javascript
    convertCsvStringToArray(str) {
      str = str.replace( /[^\u0000-\u1FFF]+/g, ''); //unicodeの範囲を指定。範囲外の文字を削除
      str = str.split("\n").filter(Boolean); //１行ずつ問題文を抽出。空の行は削除。
      var arr = [];
      //ランダムで１５問のみ抽出して返す
      for (let i = 0 ; i < 15 ; i++){
        let num = Math.floor(Math.random() * str.length);
      }
      return arr;
    },

```



これで question_allに15問の問題が格納されます。

それをquestion へ１つずつ取り出して、問題を表示しタイピング練習をします。

正解なら次の問題へ行き、最後の問題になったら成績ウィンドウを表示しますが、その辺りは省略します。



Unicodeで文字の範囲を指定したのは、このタイピング正誤判定ロジックでは対応できない漢字などを排除したかったからです。

かといって英語だけに限定したくなかったので、ローマ字やキリル文字、ギリシャ文字などを含むアルファベットの文字を示すUnicodeのざっくりとした範囲を設定しました。

参考：[Unicode一覧 0000-0FFF - wikipedia](https://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_0000-0FFF)



## オリジナル問題の読み込み

このタイピング練習アプリでは、独自の問題を使えるという点が特徴です。

csvファイルをアップロードする仕組みを作り、あとのロジックは前述の「問題文の読み込み」と共通のfunctionを使います。



### csvファイルのアップロード

inputでtype="file" のボタンを設置します。loadCsvのメソッドを設定します。

```html
<input type="file" @change="loadCsv">
```



loadCsvは次のように記述し、前述の　を使います。

```javascript
    loadCsv(e) {
      let file = e.target.files[0];
      let reader = new FileReader();

      if (!file.type.match("text/csv")) {
        this.errorMessage = "Chose only csv file.";
        return;
      }

      reader.readAsText(file);
      reader.onload = () => {
        let data = reader.result;
        localStorage.setItem('questions_all', data);
        this.questions_all = this.convertCsvStringToArray(data);
      };
      
    }
```

参考：[Vue.js CSVファイルを読み込み表示[FileReader]](https://noumenon-th.net/programming/2020/02/19/js-csv/)



## 本番公開

本番公開はNetlify、Github Pageなどで行えます。

Vue.jsしか使っていないので、DB環境の構築などは不要です。

筆者はLaravelを使うことが多いため、Forge Laravel で本番公開しました。Forge Laravelでvueアプリを公開する方法については別記事にまとめてあります。

[Laravel Forgeにvue.jsだけのSPAをデプロイする方法](https://qiita.com/seimatz_dev/items/dfecaaacc085fef8404f)



## 最後に

実用性と「動くこと」を第一にコードを書きました。

「もっとこういうロジックがある」というようなコメントがあれば、ぜひお願いします。

なおデザインの部分はCSSフレームワークBulmaの[こちらのテンプレート](https://bulmathemes.com/theme/admin-light)を使っています。

Bulma、余分なjsが無く、使いやすくて良いです。