このサイトでは、分析、カスタマイズされたコンテンツ、および広告に Cookie を使用します。このサイトを引き続き閲覧すると、Cookie の使用に同意するものと見なされます。
Hi, Developers,
straightapps.com ロゴ
作成 April 22, 2022、最終更新 May 9, 2022
トップページ > Web 開発トップ > フラッシュ暗算トレーニング
line
Web 開発
line

次々現れる数字を、暗算で加算・減算せよ!!

※ 表示が正しくないと思う場合は、JavaScript を有効にしてください。


出題レベル

1 ゲームで表示する数字の数 

表示速度 


 

ゲーム完了後に回答してください。

「ギブアップ」ボタンを押すか正解すると、ここに式が表示されます。

暗算しようよ!!

暗算に強くなれば、算数・数学に強くなる!

表示された数字を足す・引くしていこう! 自動的に切り替わる 1 桁の数字を足してください。 出題レベル選択で引き算も指定したときには、 赤文字で表示された数字は、引かなくてはいけませんよ!

全部表示されたら、答えを数字ボタンで入力してください。 「スタート」の横にある「やりなおす」ボタンが利用可能になっているときは、 直前の問題をもう 1 度、繰り返して表示します。 「表示速度」を遅くしてやり直すこともできます。


現時点でのテスト済みブラウザは、

  • Windows 10 PC の Edge, Chrome, FireFox
  • Android 9 スマホの Chrome, FireFox, Edge, DuckDuckGo
  • です。

    スマホやタブレットで開く場合は、 この QR コード ( URL の QR コード
    https://www.straightapps.com/web/js-flash-anzan.html
    )
    が、このページの URL になっています。 QR コード読み取りアプリで読み取ると便利です。

    ▲ページ先頭へ

    アイコン 素因数分解トレーニング
    素因数分解に慣れよう! 表示された数字を、割り切れる素数でどんどん割っていくだけ! ゲーム感覚で素因数分解を楽しもう!



    ここから先は開発情報です

    投稿 May 9, 2022

    ここから先は、上記「フラッシュ暗算トレーニング」開発を行った際に調べたりした JavaScript コードについての情報です。

    まずは、HTML 部です。

    JavaScript ソースは、1 ファイルに書き込んでいます。 ファイル名は flash-anzan.js としていて、 JavaScript でテキスト表示などを変更している部分のあとで読み込んでいます。

    <script type="text/javascript" src="js/flash-anzan.js"></script>
    

    JavaScript でテキスト表示などを変更している部分は、 「出題レベル」から 「表示速度」までの 3 つのドロップダウンリストから、 「スタート!」「やりなおす」のボタンのあとの 数字表示エリア ( canvas になっています。 ) 、 続くメッセージと回答入力用ボタン そして最後に「「ギブアップ」ボタンを押すか正解すると、ここに式が表示されます。」と書かれた行までです。 後述しますが、いずれも、重複しない id ( タグにつけた、他と重複しない名前です。 ) を付けてあります。

    書き換えがある部分、JavaScript ファイルの読み込みより前にあるコードは次のようになっています。 なお、JavaScript ファイルを、アクセスする id の定義より先に読み込んでしまうと、 それを見つけられずに正常動作しなくなります。

    まずは「出題レベル」のドロップダウンの定義です。

    <p>
    出題レベル <span id="gameLevel"></span>
    </p>
    

    gameLevel という id、つまり名前を付けた span タグを置いています。 この部分を JavaScript から書き換えて、ドロップダウンにしています。

    ドロップダウンには「足し算のみ」、「足し算と引き算」、「足し算と引き算(途中マイナスあり)」の 3 つがセットされ、 初期状態では「足し算のみ」が選択されるようになっています。 実際には html の select タグに置き換えているだけで、 特に JavaScript で動的に変更している部分はありませんので、 直接 html コードに書いても同じなのですが、別にしたほうがスッキリするのではないかと思いました。

    JavaScript の関数 setLevelCombo については、後述しています。

    同じように、「1 ゲームで表示する数字の数」も定義しています。

    <p>
    1 ゲームで表示する数字の数 <span id="gameCount"></span>
    </p>
    

    html の select タグに置き換えるだけの関数 setCountCombo を用意しています。 id は gameCount で、詳しくは後述します。

    もう 1 つのドロップダウン、「表示速度」も同じです。

    <p>
    表示速度 <span id="gameSpeed"></span>
    </p>
    

    id は gameSpeed で、 select タグに置き換えるための JavaScript 関数は setSpeedCombo です。

    続いて「スタート!」と「やりなおす」のボタンです。

    この 2 つのボタンは 1 行で表示していますので、同じ p タグに収められています。

    <p>
    <button onclick="gameStart()" title="新しい問題を始めます"> スタート! </button>
    &nbsp;
    <button id="btnRetry" onclick="gameRestart()" title="前回の問題をもう1度繰り返します"> やりなおす </button>
    </p>
    

    button タグが、ボタンを配置するためのタグです。

    まず 1 つめ、「スタート!」ですが、ボタン表面に表示される文字は、button タグで囲った間に記述されています。 文字の左右のスペースは、確実にスペースが入るように、全角スペースになっています。

    onclick で指定している JavaScript 関数名が、 ボタンがクリック、またはタップされたときに呼び出されるものです。 カッコ内に何も書いていませんので、引数なしで呼び出されることになります。

    title では、ボタンにマウスポインタを乗せたときに表示されるヒントを定義できます。

    新しい問題を始めます

    「スタート!」ボタンについては、押されたときに JavaScript 関数 gameStart を呼び出すだけですので、id は与えていません。

    次の「やりなおす」ボタンは、少し違います。

    button タグによる定義で、ボタンに表示する「やりなおす」は button タグ間に置いている、は同じです。 onclick で指定した、クリック時に呼び出す JavaScript 関数は gameStart としており、 マウスポインタが乗った時に表示される文字列を title で指定しています。

    このページを最初に読み込んだ時、このボタンは利用不可の状態になっていることに気付くと思います。 JavaScript の初期化の際にこのボタンを利用不可の状態にしていますので、btnRetry という id を付与しています。

    続いてゲーム中に数字を表示するためのエリアで、 canvas タグを置いています。 これは自由描画できる領域となります。

    <!-- 数字描画エリア -->
    <canvas id="canvas-number"></canvas>
    

    もちろん JavaScript が描画を担当しますので、id canvas-numberを与えています。

    その下には、書き換えを伴う行データを置いています。

    <!-- ユーザー回答表示エリア -->
    <div id="answer">ゲーム完了後に回答してください。</div>
    

    id として answer を付与した、div 領域です。 初期状態では「ゲーム完了後に回答してください。」と表示されるようにしていますが、必要に応じて書き換えを行います。

    いよいよ回答入力のための数字ボタンです。

    <!-- 回答入力用ボタン -->
    <p>
    <button id="btnNum1" onclick="numPressed(1)" title=""> 1 </button>
    <button id="btnNum2" onclick="numPressed(2)" title=""> 2 </button>
    <button id="btnNum3" onclick="numPressed(3)" title=""> 3 </button>
    <button id="btnNum4" onclick="numPressed(4)" title=""> 4 </button>
    <button id="btnNum5" onclick="numPressed(5)" title=""> 5 </button>
    </p>
    
    <p>
    <button id="btnNum6" onclick="numPressed(6)" title=""> 6 </button>
    <button id="btnNum7" onclick="numPressed(7)" title=""> 7 </button>
    <button id="btnNum8" onclick="numPressed(8)" title=""> 8 </button>
    <button id="btnNum9" onclick="numPressed(9)" title=""> 9 </button>
    <button id="btnNum0" onclick="numPressed(0)" title=""> 0 </button>
    </p>
    

    1 つ 1 つのボタンは、「スタート!」や「やりなおす」と同じように、 button タグで定義されています。

    基本的にはすべてのボタンが同じ構成ですので、まとめて解説します。

    まずはそれぞれのボタンに btnNum1btnNum0 までの id を付けています。 数字が表示され終わるまでは利用不可にするために、ボタンへのアクセスが必要です。

    onclick には、クリックまたはタップされたときに呼び出す関数名を記述します。 JavaScript で定義している関数名は numPressed で、ボタンごとに異なる引数が指定されて呼び出されることになります。 詳しくは、関数の説明部分に書いています。

    title で指定している文字列は、それぞれのボタンにマウスポインタを乗せたときに表示されるものです。

    ボタン面に表示される文字列は button タグの内側で指定されています。 数字は全角で、その両側にあるスペースは、必ず隙間があくよう、全角で入力しています。 半角スペースを明示的に入れる場合は、&nbsp; が安心です。

    そのすぐ下の「再入力」と「ギブアップ」ボタンも、基本的には同様です。

    <p>
    <button id="btnNumReset" onclick="numReset()" title="再入力"> 再入力 </button>
    <button id="btnGiveUp" onclick="gameGiveUp()" title="ギブアップ"> ギブアップ </button>
    </p>
    

    それぞれのボタンの id は btnNumResetbtnGiveUp で、 呼び出す JavaScript 関数は numReset と gameGiveUp としています。

    最後にふたたび、書き換えを行うテキストデータ領域を用意しています。

    <!-- ここに正解と計算式が表示されます -->
    <div id="history">「ギブアップ」ボタンを押すか正解すると、ここに式が表示されます。</div>
    

    id として history を与えています。

    JavaScript からアクセスする id の定義が終わりましたので、JavaScript を読み込みます。

    <script type="text/javascript" src="js/flash-anzan.js"></script>
    

    js サブフォルダに置いた、flash-anzan.js ファイルをここで読み込む、という指定です。

    以降のセクションで、それぞれ詳細を確認していきます。

    なお、ご参考までに、JavaScript ソースコードそのものを、拡張子 js を拡張子 txt に変えて、次のリンクより開けるようにしてあります。
    flash-anzan.txt
    ※ 作成時はタブを 4 文字としていますので、環境によってはタブが 8 文字のため、 コメント等がずれて見えるかも知れません。
    ※ 念のため書き添えますが、このソースコードをこのまま転載・公開することはご遠慮ください。

    また、html および JavaScript に関する情報は、 最初に作成した JavaScript プログラム 「素因数分解トレーニング」の 「ここから先は開発情報です」のほうが詳しいかも知れません。

    現時点では、以下の情報は「素因数分解トレーニング」の情報で、 このプログラム用には更新されていません。 ご注意ください。


    ▼ セクション一覧

    JavaScript コードのロード位置
    ページ読み込み時の処理
    【基礎】HTML 要素のテキストの書き換え
    【基礎】Object 型の変数の使い方
    【基礎】Math クラスの利用
    【基礎】可変サイズの配列と操作
    問題作成関数 makeNewNumber
    ボタン入力処理
    途中計算式の描画(canvas)
    このあとは・・・

    なお、本サイトのご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。

    ▲ページ先頭へ

    JavaScript コードのロード位置

    投稿 September 26, 2020

    soinsuu-init.js は、 <head> 〜 </head> タグ内でロードしていますが、 この場所で読み込む場合は、ページ本体のデータ(body 部)が読み込まれる前に、ロードされるようです。

    なので、この中で、例えばあとで定義されている div id="left-value" にアクセスしようとしても、アクセスできません。

    var pow = new Object();		// 正解を保持するオブジェクト
    var numOrg = 1;			// 素因数分解される元の値
    var num = 1;			// 残りの素因数分解される値
    var ans = new Object();		// プレーヤーの回答を保持するオブジェクト
    var inp = [];			// 要素数未定の配列
    

    soinsuu-init.js にあるのは上記のものだけです。 C/C++ と違うのは、変数の型が緩いということです。 (古い)Visual Basic の Variant 型と同じように、整数でも実数でも文字列でも入ります。 今の Visual Basic には Variant 型はないようですので、比較はできませんが。 実際には、ちゃんと 目的別に用意することもできる ( Microsoft の TypeScript というものを使うと管理しやすいようですが、まだ調べていません。 ) ようですが、今は気にしません(便利さに甘えます)。

    ほぼすべての機能が実装されている soinsuu.js は、 body タグ内でロードされています。 これは、HTML の body を先頭から読み込んでいき、 そのコードに到達したときに読み込まれる、というものです。

    <script type="text/javascript" src="js/soinsuu.js"></script>
    

    ここでは、これより前で定義された div id="left-value"canvas id="canvas-calc" にアクセスできます。

    なお、JavaScript のコードは、head や body タグ内に直接記述することも可能です。 機能テストには有効かもしれませんが、ごちゃごちゃになるので、おすすめできる方法ではありません。 また、HTML タグの onClick などに直接コードを書くこともできるようですが、小規模でない限りは、混乱のもとになるかと思います。

    <script type="text/javascript">
    <!--
    	<!-- JavaScript コードを記述 -->
    // -->
    </script>
    <noscript>
    	<!-- JavaScript が無効の場合の注意書きなどを記述 -->
    </noscript>
    
    ▲ページ先頭へ

    ページ読み込み時の処理

    投稿 September 26, 2020

    body が読み進まれていき、script の行まで到達すると、src で指定されたファイルが読み込まれます。

    <script type="text/javascript" src="js/soinsuu.js"></script>
    

    soinsuu.js は、 div id="left-value"canvas id="canvas-calc" にアクセスしますので、 この script の行は、それらよりあとに記述されていなくてはなりません。 一般的には </body> の直前でいいようですが、 このページでは、読みにくくならないように、 必要なすべての HTML 要素の定義が終わった直後、 つまり canvas の定義の直後に置いています。

    なお、ただ単純に、ブラウザがその行を解析するときに読む、というだけのようですから、 script 行が複数あれば、それぞれそのタイミングで読み込まれるようです。

    soinsuu.js の先頭部分は、次のような流れになっています。

    まずは、canvas のサイズを決定しています。

    var canvas = document.getElementById('canvas-calc');
    var w = document.documentElement.clientWidth;
    var h = 48;
    
    if (w > 480){			// 十分広い場合は
    	w = 480;		// 480 ピクセルまでとします
    }
    
    canvas.width = w;		// canvas の幅設定
    canvas.height = h;		// canvas の高さ設定
    

    変数 canvas に、canvas id="canvas-calc" を取得しています。 変数 w にブラウザの表示可能領域の横幅を取得し、変数 h に最小の高さ 48 を設定しています。 48 は、このプログラムで使うテキストを、1行表示できるだけのサイズです。

    横幅が 480 ピクセル以上ある場合は、480 ピクセルまでとしています。 今この解説文を書いている時点では、
    w = Math.min( 480, w );
    と書くと思いますが、このときはまだ知りませんでした。 自由に利用できる Math クラスの min 関数を呼び出して、480 と w の小さいほうを w にセットする、という意味です。

    サイズが決まったら、canvas の width と height プロパティにセットします。 body タグ内で読み込まれた JavaScript ファイルは先頭から順に実行されていきますので、 このコード(および続くコード)は、自動的に実行されることになります。

    続けて、「コンテキストを取得」しています。 これは、canvas の描画設定を行うための「手続き」です。

    var ctx = canvas.getContext('2d');
    

    例えば、テキストを描画するためのフォントの設定は、コンテキスト ctx を使用して、次のように記述します。

    ctx.font = "36px 'ヒラギノ角ゴ Pro', 'Hiragino Kaku Gothic Pro', 'MS ゴシック', 'MS Gothic', sans-serif";
    

    サイズは 36 ピクセルで、フォント名を記述している順に探し、最初に見つかったフォントを使用する、という意味です。

    そのあと正確には canvas の幅の調整を行っていますが、それは重要ではないので飛ばし、 新しい問題を作成するための関数呼び出しに進みます。

    makeNewNumber();
    

    これは、自分で作成した関数 makeNewNumber を、引数なしで呼び出している、という意味です。 どこでこの関数を定義しているかと言えば、このファイルのあとのほうです。 同一ファイル内にあるからいいのか、別のファイルでもいいのかどうかは調べていませんが、 C/C++ なら、定義前の関数を呼び出すことはできません。 融通が利くというのか、甘い言語仕様なのかはわかりません。

    関数本体はあとにまわし、続きの処理を見ていくと、 次にメインループを定義しています。

    function main()
    {
    	<!-- 繰り返し実行する処理を記述 -->
    	requestAnimationFrame( main );
    }
    

    まず、function で定義されている関数は、呼び出されるまで実行されません。 なんだか変な気分ですが、そうなので、そういうことです。 ですので、処理上は、関数の終了まで読み飛ばされます。

    もし main 関数が呼び出され、実行されると、記述した処理を実行できます。 最後に書くべき requestAnimationFrame 関数は、 引数に main を渡しているので、このあとまた main 関数を実行する、という意味だそうです。 極めて厳密にはどのようになっているかわかりませんが、 メインループを構築するときの一般的な手続きのようですから、こうしています。

    また、この素因数分解プログラムでは、ユーザーの入力なく自動的に表示が更新されることがありませんので、 本来ループで描画する必要はないと思われますが、 今後もこの形で進もうとしていましたので、この形で残しています。

    main 関数の定義のあと、いよいよ起動部分が記述されています。

    addEventListener('load', main(), false);
    

    「リスナー」の概念が、個人的にはわかりにくいのですが、 「監視する者」という感じで解釈しています。 この場合は、load、すなわちページの読み込みが完了したのを検知し、 main 関数を実行する、という意味になるようです。 最後の false については、今は気にしていません。

    このあとは関数の定義しかありませんので、上記 addEventListener 関数が実行されたら この soinsuu.js の実行は終わりで、続きの HTML が読み込まれていくことになると思います。 そしてすべて読み込まられた、main 関数を開始し、ループし続ける、という流れです。

    もちろん、main 関数の前に呼び出している makeNewNumber 関数も定義しています。 詳細はあとで触れます。

    ▲ページ先頭へ

    【基礎】HTML 要素のテキストの書き換え

    投稿 September 26, 2020

    HTML 要素を取得し、 innerHTML プロパティを書き換えることにより、 HTML 要素にある内部の文字列を動的に書き換えることができます。

    div id="left-value" 部分の書き換えは、 updateValue 関数で行っています。 この関数の呼び出しは、 新しい問題が作成されたとき makeNewNumber 関数からと、 素因数ボタンが押されたときに呼び出される numPressed 関数 からのみです。

    var elem = document.getElementById('left-value');
    var data = '置き換える文字列';
    elem.innerHTML = data;
    

    div 〜 /div の文字列が、「置き換える文字列」に置き換えられます。 もともとあった文字列は削除されます。

    このプログラムでは、left-value には、updateValue 関数で、 「を、下の素数ボタンを押して、素因数分解せよ!」と、 「は、何で割り切れるかな?」の2行を表示するようにしています。 また、正解が出たときには「おめでとう!」表示に切り替わります。

    コード詳細を確認

    ▲ページ先頭へ

    【基礎】Object 型の変数の使い方

    投稿 September 28, 2020

    C/C++ で言うところの構造体が、 なんと定義することなく自由に作成できてしまいます。 メンバーを完全に自由に定義でき、自由に参照できてしまうため、 タイプミスを犯した場合もエラーとならず、問題点を探すのが大変難しそうです。

    soinsuu-init.js で次のように定義した pow に、 makeNewNumber 関数 で、値を設定している部分です。

    var pow = new Object();		// 正解を保持するオブジェクト
    
    pow.n2 = Math.floor(Math.random() * 6);	// 2の0乗〜5乗までを適当に決める
    pow.n3 = Math.floor(Math.random() * 5);	// 3は4乗まで
    pow.n5 = Math.floor(Math.random() * 4);	// 5は3乗まで
    <!-- 同様の部分は省略 -->
    

    n2 や n3 といった メンバー変数 ( C/C++ の呼び方ですので、JavaScript では違う呼び方があるかも知れません。 ) は、事前に定義することなく、いきなり値を代入できています。 Math クラスの使い方は、このあとすぐ扱います。

    ここでは整数ばかり設定していますが、結局は変数ですので、自由に設定可能です。

    ball.img = new Image();
    ball.img.src = 'img/pin.png';
    

    このように、画像データを設定することも可能です(このプログラムでは画像は利用していません)。

    ▲ページ先頭へ

    【基礎】Math クラスの利用

    投稿 September 28, 2020

    Math クラスがどんな関数を持っているかは、ネットを検索すればすぐわかりますので、ここでは基本的なもののみ触れておきます。

    Math.random() は、0 以上 1 未満 ( 「未満」のときは、その値 1 を含みません。 ) の乱数を返します。

    多くの場合は 0 〜 1 の乱数が欲しいのではなく、「1 〜 10 の間の数」のような値が欲しいと思います。 このため、Math.floor( 値 ) 関数を組み合わせます。 この関数は、「値」以下の最大の整数を返してくれます。 正の値なら小数点以下を切り捨てですが、負の値の場合はそうではありませんので、ご注意ください。

    pow.n2 = Math.floor(Math.random() * 6);	// 2の0乗〜5乗までを適当に決める
    

    この場合は、コメントにある通り、pow.n2 に 0 〜 5 のいずれかの値を設定します。 Math.random() * 6 では、0 以上 6 未満の値が返り、それを Math.floor で小数部をカットしている形です。

    このほか、よく使うと思われるものを挙げておきます。

    Math.abs( 値 ) は、値の絶対値を返します。 つまり、1 なら 1 を、-1 でも 1 を返してくれます。

    Math.max( 値1, 値2 ) は、値1と値2を比較し、 大きいほうの値を返してくれます。

    逆に、Math.min( 値1, 値2 ) は、値1と値2を比較し、 小さいほうの値を返してくれます。

    このプログラムでも使っている、Math.pow( 値1, 値2 ) だと、 値1の「値2」乗を返してくれます。 つまり、Math.pow( 2, 3 ) だと2の3乗なので、8 が返されます。

    まだまだあるようですので、計算が必要になったときに、関数を調べてみると、見つかるかも知れません。

    ▲ページ先頭へ

    【基礎】可変サイズの配列と操作

    投稿 September 28, 2020

    ここでは触れませんが、2次元配列はとても使いにくいと思います。 が、ここで触れる1次元配列は、自由度が高く、とても使いやすいです。

    soinsuu-init.js で次のように定義した inp を、 makeNewNumber 関数 で、初期化しています。

    var inp = [];			// 要素数未定の配列
    
    inp.length = 1;			// 要素数1
    inp[0] = 0;			// 値は0
    

    このようにして、要素数を 1 に設定しています。 C/C++ 等と同じように、添字は 0 から開始されます。 最初の要素(この場合は1つなので唯一です)の値を 0 としています。 もちろん、設定できるのは数値に限られるわけではなく、文字列を設定したりもできます。

    素因数ボタン ( HTML の button 要素で、「2」や「3」などと書かれているボタンのことです。 ) が押されたとき、押された値の順を記録するため、次にように配列を拡張できます。

    inp.length ++;
    inp[ inp.length - 1 ] = value;
    

    inp.length に1を足し、要素数を1つ拡張しています。 そして新しく追加された要素に、値 value をセットしています。 value は押されたボタンの値を引数で渡されたもので、「2」なら 2、「3」なら 3 です。 最後の要素は、例えば要素数 3 なら添字は 0 〜 2 なので、inp.length - 1 になっています。

    ここで、length は関数ではなくプロパティですので、参照にカッコは不要で、 読み出し・設定とも可能です。

    ▲ページ先頭へ

    問題作成関数 makeNewNumber

    投稿 September 28, 2020

    新しい問題を作成する関数 makeNewNumber は、メインループ定義のあと、 addEventListener('load', main(), false); 呼び出しのすぐあとに、 記述されています。 記述位置は、同一ファイル内であれば、どこでもいいのではないかと思います。

    HTML や JavaScript の新しい要素は特にありませんので、ご覧になりたい方は、 折りたたまれている部分「コード詳細を確認」を展開してください。

    概要としては、 C/C++ でいうところの構造体のように使えるオブジェクト pow に、 正解となる値を設定します。 「素因数分解される値」を構成するのが、2の何乗、3の何乗・・・13の何乗かをランダムに決め、 設定しています。 自由に設定しすぎると、とても大きな値になってしまいかねませんので、 2は5乗まで、3は4乗まで・・・11は2乗まで、13は1乗までと制限し、 さらに11と13は同時にあると大きくなるので、どちらかのみに制限しています。

    それをもとに、変数 numOrg に、pow から計算された「素因数分解される値」を設定しています。 プレーヤーの回答は、オブジェクト ans に格納するため、 各値をゼロクリアしています。 最後に、それを表示し、また、オブジェクト inp を初期化し、 このあとユーザーが入力した値を順番に記録できるようにしています。 同時に、canvas のサイズも初期化しています。

    コード詳細を確認

    ▲ページ先頭へ

    ボタン入力処理

    投稿 September 29, 2020

    素因数ボタン ( HTML の button 要素で、「2」や「3」などと書かれているボタンのことです。 ) が押されたとき、numPressed 関数が呼び出されます。

    <button onclick="numPressed(2)" title="2で割る"> 2 </button>
    <button onclick="numPressed(3)" title="3で割る"> 3 </button>
    <!-- 同様の記述は省略 -->
    

    button 要素の onclick イベント、すなわちボタンが押されたとき、 numPressed 関数に、押されたボタンの数字を渡して、呼び出しています。

    numPressed 関数では、その値で割り切れるかどうかを判断し、 割り切れるならその入力を配列 inp に記録して、 途中経過を更新します。 div タグの文字列の更新については「HTML 要素のテキストの書き換え」に、 canvas への途中経過の描画については「途中計算式の描画(canvas)」に書いています。

    HTML や JavaScript の新しい要素は特にありませんので、ご覧になりたい方は、 折りたたまれている部分「コード詳細を確認」を展開してください。

    コード詳細を確認

    「別の問題に挑戦」ボタンは、初期化時に呼び出したものと同じ、 makeNewNumber 関数を呼び出します。

    <button onclick="makeNewNumber()">別の問題に挑戦</button>
    

    新しい問題を作成し、ユーザーの回答や途中経過などを初期化しています。

    ▲ページ先頭へ

    途中計算式の描画(canvas)

    投稿 September 29, 2020

    canvas への 途中計算式の描画は、main 関数で行っています。 先にも書いた通り、ユーザーの入力なく自動的に更新されることはありませんので、 ループ内で描画する必要がないと言えますが、このあとのプログラムに続く部分ですので、 このままにしています。

    まず最初に、canvas 全体をグレーで塗りつぶしています。 Windows デスクトップアプリとしては、 画面 DC ( デバイス コンテキストと呼んでいます。 ) に対してこんなことをしたら、 ウィンドウがチラついてチラついて仕方がありませんが、 Android アプリなどと同じように、いちいち全画面を描画しても大丈夫みたいです。

    ctx.fillStyle = "rgb( 230, 230, 230 )";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    

    位置を決めて、一番最初の数字(問題とした数字) numOrg を記入します。

    var x;
    var y = 40;
    
    ctx.font = "36px 'ヒラギノ角ゴ Pro', 'Hiragino Kaku Gothic Pro', 'MS ゴシック', 'MS Gothic', sans-serif";
    ctx.fillStyle = '#000';				// 黒文字で
    x = x_num - ctx.measureText( numOrg ).width;	// 右端を x_num にあわせる
    ctx.fillText( numOrg, x, y );			// canvas 領域の座標(y座標は'下'を指している)
    y += 40;
    

    ctx.font に、フォント情報を設定しています。 ここではサイズを 36 ピクセルにして、その後に続くフォントを順番に探して、見つかったフォントを使います。 ですので、このままなら、可能性としては、利用環境ごとに正しい表示が得られません。

    ctx.fillStyle では、文字の色を設定しています。 塗りつぶしの時は rgb で指定していますが、CSS での指定と同じように、# のあとに RGB 値を 16 進数で並べても OK です。

    y 座標には、変数定義時に 40 を設定しています。 この値は、Windows とは違い、文字の下の座標を示していますので、注意が必要です。 そいて x 座標には、ctx.measureText( 文字列 ).width で、 選択中のフォントで「文字列」を描画するとき、どれだけの幅が必要かを返す関数を使用しています。 x_num は、事前に10文字表示するための x 座標の右端を設定していますので、 「10桁表示で右詰め」となる x となります。

    ctx.fillText( 文字列, x 座標, y 座標 ) で、テキストを描画しています。 なお、numOrg は数値として扱っていますが、文字列として渡すと文字列として解釈されます。 個人的には便利なので好きですが、バグの温床になることは間違いありません

    var numTmp = numOrg;					// 途中経過の表示用
    for ( var i = 1;  i < inp.length; i++ ){
    

    途中経過表示用に numTmp を定義し、もともとの値 numOrg をコピーしています。 以下ループで、配列 inp の要素数(正確にはそれマイナス1)だけ繰り返されるようにしています。 配列 inp の要素数は、ユーザーの入力回数(正確にはそれマイナス1)です。

    	numTmp /= inp[i];
    	if (numTmp > 1){					// 割ったあと1になるなら表示しません
    
    		ctx.fillStyle = '#F00';				// 赤文字で
    		x = 50 - ctx.measureText( inp[i] ).width;	// 右端を 50 にあわせる
    		ctx.fillText(inp[i], x, y - 40);		// 1行上に表示
    

    途中経過表示用の numTmp を、ユーザー入力の値 inp[ i ] で割ります。 この値が1になるなら、表示しません(そういうルールにしているため)。

    この部分では、割られる数字の左に表示している赤い文字(入力文字)を描画しています。 赤を指定し、文字の右端を 50 ピクセル位置で右詰め表示できる位置を決定しています。 表示位置は、今の y より1行上になるべきなので、1行のピクセル数 40 を引いて描画しています。

    		ctx.fillStyle = '#000';
    		ctx.fillText(')', 55, y - 40);			// 1行上に表示すべき
    		ctx.fillRect(55, y - 40 + 2, x_num - 55 + 10, 2);// ラインを引く
    

    この部分では、ラインの部分を書いています。 描画が面倒なので、閉じかっこを描画したあと、下に横ラインを引いています。 デバイスや環境によっては、少しずれてしまいますが、今は良しとします。

    途中計算部分

    		x = x_num - ctx.measureText( numTmp ).width;	// 右端を x_num にあわせる
    		ctx.fillText( numTmp, x, y);			// クライアント領域の座標(y座標は'下'を指しているよう)
    	}
    	y += 40;
    }
    

    最後に、今 y が指す行に、割られた後の数値を描画しています。

    ▲ページ先頭へ

    このあとは・・・

    投稿 September 26, 2020

    このプログラムの次に作成した DaysUntil では、日付の取得やコンボボックス(ドロップダウンリスト)の操作を扱っています。

    ▲ページ先頭へ
    line
    関連トピックス
    line

    素因数分解トレーニング

    約分や因数分解の基礎となる、素因数分解を完全マスターするために練習しよう!

    素因数分解トレーニング Android アプリ版

    素因数分解トレーニングアプリを入れて、オフラインでも練習しよう!


    その他のおすすめ

    ウェブ開発に関するトピックは、「ウェブ開発トップ」にまとめられています。



    © 2017-2022 StraightApps.com 無断転載を禁じます。No reproduction without permission.