【教師オリジナルアプリを作ろう】英文法アプリの見た目をアプリらしく/スマホに対応させる(JavaScript)

前回の記事【教師オリジナルアプリを作ろう】英文法問題アプリの作成方法を初めから(JavaScript)では,JavaScriptでwebアプリを動かすための骨格となるフレームワークの仕組みを解説しました。今回は,このコードにcssを用いて装飾を行い,もう少し見た目をアプリらしくしていきます。

また,アプリをスマートフォンの画面で適切に表示できるように調整します。もし,学生向けにwebアプリを提供するとしたら,ほとんど全ての生徒がスマートフォンでアクセスしてくるでしょう。したがって,ここではスマートフォンでの表示を前提としてデザインを決めていきます。

スマートフォンでの表示の確認

chromeを使えば,スマートフォン上での画面の確認を簡単に行うことができます。F12を押して,Ctrl+Shift+Mを押せば,スマートフォンのプレビューを見ることができます。ここで画面を確認しながら,作業を進めると良いでしょう。

ビューポートの指定

実際のコードの説明に移ります。アプリの動作の仕組みについては前回の記事を参考にしてください。今回は,デザインの部分に絞って説明していきます。

<meta name="viewport" content="width=device-width,initial-scale=1">

まず,<head></head>にビューポートを指定します。多くのスマートフォンはwebサイトの画面を自動的に縮小して表示する仕組みになっています。ビューポートを指定することで表示をアプリ側でコントロールします。スマホに対応するためのおまじないとして,とりあえず書いておくものです。

cssの指定

<link rel="stylesheet" href="./fasgram.css">

スタイルシートを指定します。cssファイルの中でアプリのデザインを指定していきます。

cssファイルの中身を見ていきましょう。

/*フォントファミリー*/
body {
    font-family: 'Helvetica','Arial','Hiragino Kaku Gothic ProN','ヒラギノ角ゴ ProN W3','メイリオ', Meiryo,'MS Pゴシック','MS PGothic'sans-serif;
}

全体のフォント設定です。font-familyは優先するフォントを前に書きます。PCやスマホによって使用できるフォントが異なるため,なるべく多くの端末に対応できるようにフォントを並べていきます。今回は使っていませんが,webフォントを導入することで,どの端末でも同じフォントで表示できるようになります。好みに応じてフォント名を書き換えていくと良いでしょう。

/*スマートフォン用の基準文字サイズ*/
body {
    font-size: 24px;
}
/*デスクトップ用の基準文字サイズ*/
@media screen and (min-width:680px) {
    body {
        display: flex;
        justify-content: center;
        font-size: 18px;
    }
}

フォントの大きさを指定します。通常は24pxですが,画面の幅が680pxを超えた場合に18pxを用いています。このようにして,画面の幅によってスマホとPCの切り替えの判断を行い,スマホ用の画面では文字を大きめに表示するように調整しています。これも実際に表示を確認しながら,適切な大きさに調節していくと良いでしょう。

また,PC画面ではFlexboxを使って,アプリ全体を画面の中央に表示するようにしています。Flexboxの使い方は,こちらなどを参考にしてください。

ブロック要素の概要を確認します。

このように,表示する内容はブロック要素containerの中に収めていきます。ブロック要素とは,中身を入れるための箱のことでした。ここでは,containerという箱の中にheaderarticleという箱が入っていて,その中にさらに小さな箱が入っています。そして,表示したい文字などの情報を箱の中に入れていきます。

/*コンテナの幅*/
#container {
    max-width: 680px;
}

コンテナの幅を指定します。幅を指定しないと文字がブラウザの幅全体に表示されます。デスクトップPCで表示するときはかえってバランスが悪くなるので,アプリの画面が最大680pxで表示されるように指定しています。スマホの画面の幅はこれよりは小さいので,画面全体に表示されます。

これで,スマホに対応させるための設定は終わりです。最近はレスポンシブ対応のwebサイトなども増えていますが,スマホでの表示を前提にするならば,設定すべき項目はそれほど多くはありません。

次に,文字やボタンなどの要素のデザインを調整していきます。

/*フォントサイズ*/
.size-1 {
    font-size: 1.5rem;
}
.size-2 {
    font-size: 1.25rem;
}
.size-3 {
    font-size: 1.0rem;
}
.size-4 {
    font-size: 0.75rem;
}
.size-5 {
    font-size: 0.5rem;
}

後で使いまわすために,とりあえず文字の大きさを5種類用意しました。具体的な使い方はあとで説明します。font-size: 1.5rem;は文字を標準の1.5倍の大きさで表示するという意味です。スマホ画面では文字の大きさを24pxで指定しているので,その1.5倍である36pxで表示することになります。この辺りも,実際の画面を確認しながら調節していくと良いでしょう。

フォントの色

/*文字色*/
.color-blue {
    color: #3366CC;
}
.color-red {
    color: #FF6600;
}

フォントの色を指定します。ここでは青文字と,赤文字を設定しました。

マージン

/*左マージン*/
.ml-1 {
    margin-left: 10px;
}
/*下マージン*/
.mb-1 {
    margin-bottom: 10px;
}

デザインを決めていくときに,文字とボタンの間にもう少し間隔が欲しい,文字をもう少し右にずらしたい,と言った要望が出てきます。マージンを設定することで画面の端や他のブロック要素との間隔を指定することができます。とりあえず左と下のマージンを用意して,あとで必要な個所に当てはめていきます。

ボタンの共通設定

/*ボタンの共通設定*/
.btn-wrapper {
    display: flex;
    justify-content: center;
    align-items: center;
}

今回のアプリでは,ボタンは画面の中央に表示することで統一しています。Flexboxを使って.btn-wrapperの中に配置した要素を画面中央に配置するようにしています。

アプリ全体で使用するデザインの設定が終わりました。次にそれぞれのブロックにおいて設定をあてはめていきます。

スタート画面の設定

スタート画面です。

<header><p class="size-5">fasgram 英文法トレーニングwebアプリ<span class="badge">動作サンプル</span></p></header>

一番上の部分がヘッダー部です。文字が小さく表示されています。スタイルシートで設定した文字の大きさを使用しています。

スタイルは具体的にはclass=を使って指定します。<p class="size-5"></p>で囲まれた部分にはsize-5のスタイルが適応されます。cssファイルの中で.size-5font-size: 0.5rem;と書いたことを思い出しましょう。したがって,この部分の文字は0.5倍の大きさで表示されることになります。

さらに「動作サンプル」の文字を<span></span>で囲むことで,文字を青色の枠で囲むようにしています。この部分には.badgeというクラスを指定しています。このように,文字列の一部だけデザインを変えたい場合は<span></span>で囲んでクラスを指定すると良いでしょう。<span>はフォントの種類や色を変えたり,下線を引いたりするなど,さまざまな場面で活躍します。

<p class="size-2">ボタンを押して、トレーニングを開始しましょう。</p>
<div class="btn-wrapper">
  <button type="button" id="start-button" class="btn btn-start size-1">語句選択問題</button>
</div>

メッセージとボタンの部分です。メッセージには.size-2を指定して文字を大きくしています。

ボタンの部分はブロック<div class="btn-wrapper"></div>で囲まれています。こうすることで,ボタンを中央に配置しています。

<button></button>が実際のボタンを表示する部分です。

idはボタンを識別するための名前を付ける部分です。idが無いと,あとでクリック判定を行うときにどのボタンが押されたのか識別できなくなるので,start-buttonという名前を付けています。

classの設定は少し複雑に見えるかもしれません。classは文字のデザインなどのスタイルを指定しますが,ここではbtnbtn-startsize-1と3つのスタイルを使用しています。このように,複数のスタイルを同時に使用したい場合には半角スペースで区切って並べていきます。

cssファイルの中ではそれぞれのスタイルはこのように設定されています。

.btn {
    font-weight: 700;
    line-height: 1.5;
    padding: 1rem 4rem;
    cursor: pointer;
    transition: all 0.3s;
    text-align: center;
    vertical-align: middle;
    letter-spacing: 0.25rem;
    border: solid 2px #ffffff;
    border-radius: 1.0rem;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
}

要素が多すぎるので説明は省きますが,ボタンの角を丸くしたり,ボタンに影を付けるなどの設定をしています。

/*スタートボタンの色*/
.btn-start {
    color: #fff;
    background-color: #6699ff;
    border-radius: 2.5rem;

こちらでは,文字の色とボタンの色を設定しています。

また,size-1は文字を大きく表示するための設定でした。

.btn-start:hover {
    color: #fff;
    background: #70a1ff;
}

ボタンにはもう一つスタイルを指定します。:hoverはマウスカーソルがボタンの上に乗ったときに色を変える設定です。デスクトップPCでサイトを見たときのためにこの設定していますが,スマホでは関係ない部分です。スマートフォンにはカーソルがありません。

続いて,問題を表示する画面の設定をしていきます。

効果音の再生

アプリをゲーム感覚で使用するために,効果音を付け加えます。

<audio id="mp3-start" src="./mp3/start.mp3"></audio>
<audio id="mp3-correct" src="./mp3/correct.mp3"></audio>
<audio id="mp3-incorrect" src="./mp3/incorrect.mp3"></audio>

ここでは,スタートボタンを押したときの効果音start.mp3,正解をクリックしたときの効果音correct.mp3,不正解をクリックしたときの効果音incorrect.mp3の3つを用意しました。

これらは,<body>内で最初にページを表示するときに読み込むようにします。<audio></audio>はmp3ファイルを読み込むだけで,この時点では再生されません。あとで.play()を使って実際に再生します。

また,mp3にidを指定してあとで呼び出すための目印にします。

効果音のmp3はフリー素材が多く存在しているので,それらを利用すると良いでしょう。今回は効果音ラボで提供されているものを使用しました。

JavaScript側での実際の使い方を見ていきましょう。

function start_display() {
    $('#display').empty();
    let content = '<p class="size-2">ボタンを押して、トレーニングを開始しましょう。</p><div class="btn-wrapper"><button type="button" id="start-button" class="btn btn-start size-1">語句選択問題</button></div>';
    $('#display').append(content);
    $('#start-button').on('click', function() {
        $('#mp3-start')[0].play();
        setTimeout(() => {
            grammar_test_start()
        },1500);
    });
}

関数start_display()はスタート画面を表示する部分です。コードについては前回の記事を参考にしてください。

$('#mp3-start')[0].play();が効果音を再生している部分です。これはjqueryのコードです。上でスターボタンを押したときの効果音にid="mp3-start"を指定しました。jqueryでこれを指定する場合は#mp3-startと表記します。これをセレクタと呼びます。セレクタのうしろに[0]と書いてありますが,これはjqueryの仕様なので気にせず,とりあえず書いておくものだと思ってください。そして.play()によって効果音を再生します。

再生部分をクリック判定の中に書くことで,スタートボタンをクリックしたときに効果音が再生されます。

問題表示画面の設定

問題表示画面の設定に移ります。

それぞれのブロック要素とidの関係を上に示しています。

<p id="question-number" class="size-3"></p>
<p id="question" class="size-1"></p>
<div class="size-1"><p id="option1" class="box-1"></p></div>
<div class="size-1"><p id="option2" class="box-1"></p></div>
<div class="size-1"><p id="option3" class="box-1"></p></div>
<div class="size-1"><p id="option4" class="box-1"></p></div>

HTMLのコードです。問題番号と問題文の部分には文字の大きさのスタイルを指定しています。

選択肢の部分には.box-1というスタイルを指定しています。

/*選択肢の枠線*/
.box-1 {
    padding: 0.5em 1em;
    margin: 0.25em 0;
    font-weight: bold;
    color: #6091d3;
    background: #FFF;
    border: solid 2px #6091d3;
    border-radius: 10px;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
}

選択肢の部分はHTMLとしてはボタンではないのですが,デザインを変えることで実質的にボタンとして表示するようにしています。文字を枠線で囲んだり,枠に影をつけるなどの設定をしています。

ボタンのデザインについては,デザイン事例を紹介するサイトが多く存在しているので,そこから好みのデザインを探してみると良いでしょう。今回のwebアプリでは,デザイン事例を参考にしてそれを加工したものを用いています。

JavaScriptはボタンでない要素もボタンのように扱ってクリックすることができます。それならば<button>を使う必要がないように思えるかもしれませんが,コードを見たときにどの部分がクリックを求めているのか分かりにくくなるという欠点もあるので,なるべく<button>を使った方が良いのではないかと,個人的には思うところです。

まとめ

デザイン設定の説明は以上です。ここでは,webアプリのフレームワークをもとに,それぞれの要素のデザインを変更するためのスタイルの指定と,効果音を追加する方法について学びました。

webアプリを作成する場合には,まずアプリの動作を決めるフレームワークを作成し,そのあとcssを用いてアプリを使いやすいインターフェイスに加工していくと良いでしょう。

インターフェイスは生徒の興味を引き付けるために大事な要素ですが,学習効率を高めるためにはアプリのアルゴリズムが最も重要であることを忘れてはいけません。

コード全体を示します。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>fasgram 英文法トレーニングwebアプリ</title>
<meta name="description" content="">
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="./fasgram.css">
</head>
<body>
<div id="container">
<header><p class="size-5">fasgram 英文法トレーニングwebアプリ<span class="badge">動作サンプル</span></p></header>
<article>
<audio id="mp3-start" src="./mp3/start.mp3"></audio>
<audio id="mp3-correct" src="./mp3/correct.mp3"></audio>
<audio id="mp3-incorrect" src="./mp3/incorrect.mp3"></audio>
<div id="display"></div>
<div id="comment_box"></div>
<div id="console"></div>
</article>
<footer></footer>
</div>
<script>
let obj;
let texts;
//スタート画面
function start_display() {
    $('#display').empty();
    let content = '<p class="size-2">ボタンを押して、トレーニングを開始しましょう。</p><div class="btn-wrapper"><button type="button" id="start-button" class="btn btn-start size-1">語句選択問題</button></div>';
    $('#display').append(content);
    $('#start-button').on('click', function() {
        $('#mp3-start')[0].play();
        setTimeout(() => {
            grammar_test_start()
        },1500);
    });
}
//語句選択問題
async function grammar_test_start() {
    //画面の初期化とレイアウトの設定
    let content;
    content = '<p id="question-number" class="size-3"></p>';
    content += '<p id="question" class="size-1"></p>';
    content += '<div class="size-1"><p id="option1" class="box-1"></p></div>';
    content += '<div class="size-1"><p id="option2" class="box-1"></p></div>';
    content += '<div class="size-1"><p id="option3" class="box-1"></p></div>';
    content += '<div class="size-1"><p id="option4" class="box-1"></p></div>';
    $('#display').empty();
    $('#display').append(content);
    //問題文データの読み込み
    const data = await fetch('./data/test01.json');
    obj = await data.json();
    obj_json = JSON.stringify(obj); //json文字列に変換
    texts = JSON.parse(obj_json); //配列に格納
    //問題文をシャッフル
    for(let i = texts.length -1; i > 0; i--) {
        let rand = Math.floor(Math.random() * (i + 1));
        [texts[i], texts[rand]] = [texts[rand], texts[i]];
    }
    //テストを順番に行う
    let clicked;
    for(let id = 0; id < texts.length; id++) {
        //問題文と選択肢の表示
        await new Promise((resolve) => {
            $('#question-number').text('第 '+(id+1)+' 問');
            $('#question').text(texts[id][2]);
            $('#option1').text('① '+texts[id][3]);
            $('#option2').text('② '+texts[id][4]);
            $('#option3').text('③ '+texts[id][5]);
            $('#option4').text('④ '+texts[id][6]);
            $('#comment_box').empty()
            //選択肢クリック時の処理
            $('#option1').on('click', function() {
                clicked = 1;
                resolve();
            });
            $('#option2').on('click', function() {
                clicked = 2;
                resolve();
            });
            $('#option3').on('click', function() {
                clicked = 3;
                resolve();
            });
            $('#option4').on('click', function() {
                clicked = 4;
                resolve();
            });
        });
        //正解不正解の判定と解説の表示
        await new Promise((resolve) => {
            const option_char = ['①','②','③','④'];
            const correct_answer = texts[id][7]; //答えの番号を格納
            //正解不正解の判定
            if(clicked == correct_answer) {
                //正解の場合の処理
                let content = '<p class="size-1"><span class="color-blue">〇 正解</span></p>';
                $('#comment_box').append(content);
                $('#mp3-correct')[0].play();
                setTimeout(() => {
                    resolve();                    
                }, 1500);
            } else {
                //不正解の場合の処理
                let content = '<p class="size-1"><span class="color-red">× 不正解</span> ';
                content += '正解は '+option_char[correct_answer-1]+'</p>';
                content += '<p class="size-3">'+texts[id][8]+'</p>';
                content += '<div class="btn-wrapper"><button type="button" id="confirm" class="btn btn-confirm size-1">OK</button></div>';
                $('#comment_box').append(content);
                $('#mp3-incorrect')[0].play();
            }
            //OKボタンが押されたら次の問題へ
            $('#confirm').on('click', function() {
                resolve();
            });
        });
    }
    //全問終了のメッセージ
    await new Promise((resolve) => {
        let content;
        content = '<div class="size-1"><div class="mb-1">トレーニング終了です。</div>';
        content += '<div class="btn-wrapper"><button type="button" id="confirm" class="btn btn-confirm size-1">最初に戻る</button></div></div>';
        $('#display').empty();
        $('#comment_box').empty();
        $('#display').append(content);
        //OKボタンが押されたら次の問題へ
        $('#confirm').on('click', function() {
            start_display();
            resolve();
        });
    });
}
//スタート画面
start_display();
</script>
</body>
</html>

cssファイルのコード全体を示します。

/*フォントファミリー*/
body {
    font-family: 'Helvetica','Arial','Hiragino Kaku Gothic ProN','ヒラギノ角ゴ ProN W3','メイリオ', Meiryo,'MS Pゴシック','MS PGothic'sans-serif;
}
/*スマートフォン用の基準文字サイズ*/
body {
    font-size: 24px;
}
/*デスクトップ用の基準文字サイズ*/
@media screen and (min-width:680px) {
    body {
        display: flex;
        justify-content: center;
        font-size: 18px;
    }
}
/*コンテナの幅*/
#container {
    max-width: 680px;
}
/*フォントサイズ*/
.size-1 {
    font-size: 1.5rem;
}
.size-2 {
    font-size: 1.25rem;
}
.size-3 {
    font-size: 1.0rem;
}
.size-4 {
    font-size: 0.75rem;
}
.size-5 {
    font-size: 0.5rem;
}
/*文字色*/
.color-blue {
    color: #3366CC;
}
.color-red {
    color: #FF6600;
}
/*左マージン*/
.ml-1 {
    margin-left: 10px;
}
/*下マージン*/
.mb-1 {
    margin-bottom: 10px;
}
/*中央に表示するメッセージ*/
.message {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    height: 200px;
    background-color: #e5eeff;
    border-radius: 1.0rem;
    border: solid 2px #ffffff;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);/*影*/
}
/*ボタンの共通設定*/
.btn-wrapper {
    display: flex;
    justify-content: center;
    align-items: center;
}
.btn {
    font-weight: 700;
    line-height: 1.5;
    padding: 1rem 4rem;
    cursor: pointer;
    transition: all 0.3s;
    text-align: center;
    vertical-align: middle;
    letter-spacing: 0.25rem;
    border: solid 2px #ffffff;
    border-radius: 1.0rem;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
}
/*スタートボタンの色*/
.btn-start {
    color: #fff;
    background-color: #6699ff;
    border-radius: 2.5rem;
}
.btn-start:hover {
    color: #fff;
    background: #70a1ff;
}
/*OKボタンの色*/
.btn-confirm {
    color: #fff;
    background-color: #99cc33;
}
.btn-confirm:hover {
    color: #fff;
    background: #a7d741;
}
/*大問表示*/
#question-number {
    font-weight: 700;
}
/*選択肢の枠線*/
.box-1 {
    padding: 0.5em 1em;
    margin: 0.25em 0;
    font-weight: bold;
    color: #6091d3;
    background: #FFF;
    border: solid 2px #6091d3;
    border-radius: 10px;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
}
.box-1 p {
    margin: 0; 
    padding: 0;
}
/*バッジ*/
.badge {
    padding: 3px 6px;
    margin-right: 8px;
    margin-left: 1px;
    font-size: 75%;
    color: white;
    border-radius: 6px;
    box-shadow: 0 0 3px #ddd;
    white-space: nowrap;
    background-color: #58ACFA;
}