20201019のJavaScriptに関する記事は11件です。

ゲームを作りながら学ぶ!JavaScript レベルアップ講座 part5

こんにちは、Yuiです。

今回は前回描画したスライムを動かす方法について書いていきます。
JavaScript道場はこちら

今回の成果物はこちらです。
Image from Gyazo

キーボードの↑↓→←キーで動かしています。

スライムのデフォルトの場所を中心にする

前回は(0,0)を始点にしてましたが、それだと少し見にくいのでスライムのデフォルトの位置を中央にしましょう。
絶対値で表しても良いのですが、今回はcanvasサイズから計算します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <canvas id="canvas" width="640" height="480" style="background-color: black;"></canvas>
  <script>
    const CANVAS_SIZE_W = 640
    const CANVAS_SIZE_HW = 320 // 画面の半分の広さ
    const CANVAS_SIZE_H = 480
    const CANVAS_SIZE_HH = 240 // 画面の半分の高さ
    const CHARACTER_SIZE = 100 // キャラクターの大きさ
    const CHARACTER_HSIZE = 50 // キャラクターの半分の大きさ
    let CHARACTER_POS_X = CANVAS_SIZE_HW - CHARACTER_HSIZE
    let CHARACTER_POS_Y = CANVAS_SIZE_HH - CHARACTER_HSIZE
    const canvas = document.getElementById("canvas")
    const ctx = canvas.getContext("2d")
    const img = new Image()
    img.src = "https://cache-www.dragonquest.jp/hoshidora/assets_190310/narikirislime/pc/slime.png"
    img.onload = () => ctx.drawImage(
      img,
      CANVAS_SIZE_HW - CHARACTER_HSIZE,
      CANVAS_SIZE_HH - CHARACTER_HSIZE,
      CHARACTER_SIZE,
      CHARACTER_SIZE
    )
  </script>
</body>
</html>

これでスライムが中央に来ました。

キーボードに反応してスライムが動くようにする

そして今回肝となるキーボードに反応する部分です。
そのためにはaddEventListenerを使います。

公式ドキュメントはこちら→https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener

今回はwindowが対象になりますので、window.addEventListenerという風に書きます。

以下を追加します。

    window.addEventListener("keydown", (e) => {
      if (e.key === "ArrowLeft") {
        CHARACTER_POS_X -= 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowUp") {
        CHARACTER_POS_Y -= 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowRight") {
        CHARACTER_POS_X += 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowDown") {
        CHARACTER_POS_Y += 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
    })

ここで、keydownというのはkeyがdownしたとき、つまりキーを押してる状態のことをいいます。
そのときにどのキーが押されたかによって反応を変えたいのでeという変数に入れてあげます。
もし→がArrowRightなどと覚えている場合は良いのですが、もし覚えていない場合はここでconsole.log(e)などをするとわかりやすいかと思います。

スクリーンショット 2020-10-19 22.08.21.png

console.log(e)をした状態で→のキーを押してログを出した状態です。
これを見ると→をクリックすると、e.keyArrowRightになることがわかります。これを利用して↑↓→←で動きをそれぞれ付けたのが上記のコードです。

この状態で動かすとこのようになります。

Image from Gyazo

スライムが動くことは動くのですが、軌跡まで残ってしまいます。

スライムが動くたびに毎回描画し直す

軌跡を消すために使うのがcanvasの中のclearRectというメソッドです。
これは範囲を指定することで、その範囲内のものをすべてリセットすることができます。

そういうわけで、ctx.clearRect(0 ,0, CANVAS_SIZE_W, CANVAS_SIZE_H);を以下のように追加します。

    window.addEventListener("keydown", (e) => {
      ctx.clearRect(0 ,0, CANVAS_SIZE_W, CANVAS_SIZE_H);
      if (e.key === "ArrowLeft") {
        CHARACTER_POS_X -= 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowUp") {
        CHARACTER_POS_Y -= 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowRight") {
        CHARACTER_POS_X += 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
      if (e.key === "ArrowDown") {
        CHARACTER_POS_Y += 10
        ctx.drawImage(
          img,
          CHARACTER_POS_X,
          CHARACTER_POS_Y,
          CHARACTER_SIZE,
          CHARACTER_SIZE
        )
      }
    })

これで軌跡が消え、いい感じにスライムが動くようになりました!

クラス構文を使って書く

上記のままでももちろん良いのですが、今後スライムがたくさん出てきたときのために、クラス構文化して書いておきます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <canvas id="canvas" width="640" height="480" style="background-color: black;"></canvas>
  <script>
    const CANVAS_SIZE_W = 640
    const CANVAS_SIZE_HW = 320 // 画面の半分の広さ
    const CANVAS_SIZE_H = 480
    const CANVAS_SIZE_HH = 240 // 画面の半分の高さ
    const CHARACTER_SIZE = 100 // キャラクターの大きさ
    const CHARACTER_HSIZE = 50 // キャラクターの半分の大きさ

    const canvas = document.getElementById("canvas")
    const ctx = canvas.getContext("2d")
    class Slime {
      constructor(posX, posY) {
        const img = new Image()
        img.src = "https://cache-www.dragonquest.jp/hoshidora/assets_190310/narikirislime/pc/slime.png"
        this.img = img
        this.posX = posX
        this.posY = posY
        this.sizeX = 100
        this.sizeY = 100
        img.onload = () => this.drawImage()
      }
      drawImage() {
        ctx.drawImage(this.img, this.posX, this.posY, this.sizeX, this.sizeY)
      }
      move(e) {
        ctx.clearRect(0 ,0, CANVAS_SIZE_W, CANVAS_SIZE_H);
        if (e.key === "ArrowLeft") {
          this.posX -= 10
        }
        if (e.key === "ArrowUp") {
          this.posY -= 10
        }
        if (e.key === "ArrowRight") {
          this.posX += 10
        }
        if (e.key === "ArrowDown") {
          this.posY += 10
        }
        this.drawImage()
      }
    }
    let s = new Slime(CANVAS_SIZE_HW - CHARACTER_HSIZE, CANVAS_SIZE_HH - CHARACTER_HSIZE)
    window.addEventListener("keydown", (e) => {
      s.move(e)
    })
  </script>
</body>
</html>

こうすることで、今後スライムを追加したい場合はnew Slimeと書くだけで生成することができますね!

クラス構文、便利ですね。

今回はスライム一個だけの設計なので、正直クラス構文にしてもしなくてもどちらでも問題は無いです。
ただ、今後ゲームを作っていく中で、敵をいくつか生成したりすることがあるかとおもいます。
そのたびに毎回同じコードを書くようでは効率が悪いので、使えるときはクラス構文でまとめておくのが楽でいいですね。

それでは今回はスライムを動かすところまでやりました。

それでは次回はパーティメンバーのステータスを表示する部分を実装していきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ブラウザでコード譜を書く : "fumen" の紹介

あらまし

ブラウザでコード譜を書くためのMarkdownベースのJavascriptライブラリ fumen の紹介です。

Github : https://github.com/hbjpn/fumen/
ドキュメント : https://hbjpn.github.io/fumen/
プレイグラウンド : https://fumenbook.com/

ずいぶん前(2013年〜)から公開していましたが、以前のバージョンは速度が遅かったり描画の純軟性にかける問題があったので、根本的な作り直しを行いました。また、fumenをコアにしたコード譜作成WEBサービスfumenbookも作りました。

モチベーション

譜面を作るツールは多くあるのですが、音符等まで含めた綺麗な譜面を作ることを主眼においたものが殆どです。一方でポピュラー音楽をバンドで合わせるような場面では、コード、尺、構成(繰り返し)、シンコペーションといったレベルでの演奏指示がわかれば十分で、音符レベルの情報が必要なケースは多くありません。実際のところコード譜をテキストで書いている人も多いくらいですので、マークアップ形式とした方が手軽で便利だろうとの観測のもと作りました。
マークアップ形式の楽譜作成ソフトにはlilypond等がありますが、fumenはコード譜作成に特化したシンプルな構文になっており、より直感的に簡単に作ることができます。

何ができるの?

  • 平易な構文に基づいたマークダウンテキストを入力として、HTML Canvasエレメント上にコード譜を描画します。
  • ポピュラーミュージックではよく使われるコード譜、マスターリズム譜と言われる譜面が作れます。
  • キーのトランスポーズを自動化できます。
  • マークダウンソースコード自体が、コード譜としてヒューマンリーダブルです。ただのテキストなのでバンドメンバや仲間との共有も簡単です。
  • Clientオンリーで動くので、HP, ブログ等に簡単に埋め込みできます(Wordpressプラグイン申請中)

サンプル

%TITLE="サンプル"
%SUB_TITLE="〜サブタイトル〜"
%ARTIST="アーティスト"
%SHOW_STAFF="YES"

[A]
| <S> CM7 | Am7 | FM7 | F/G |
| Em7 | Am7 | Dm7 | G7 |

[B]
||: '(コメント)' Am7:1 | CM7:4. FM7:8~ :2 | [1.] `歌詞`@Dm7:2 `など`@C/E:2 | `かける`@F:2 `さ`@F/G:2 :||
>| [2.] Dm7:1 | G7:1 <D.S.> |

レンダリングイメージは下記の様になります。



Cheat Sheet

下記のような要素をサポートしています。詳しくは https://hbjpn.github.io/fumen/ を参照ください。

Markdown Description
Cm7-5#11 コード.
C/D On bass表記付きのコード
/D On bass表記のみ
C:4. 長さ指定付きのコード. 付点四分の例.
r:4. 休符
-2- 小節休符
| 小節境界(1本線)
|| 小節境界(2本線)
||. 小節境界(2本線, 二本目太め表示)
||: リピート開始
:|| リピート終了
:||x3 リピート終了(リピート数つき)
:||: リピート終了と開始のコンビネーション
./|/. Simileマークがオーバーレイされた小節境界
> 右揃え(行の先頭に指定)
< 左揃え(行の先頭に指定)
(4/4) 拍子記号
<Coda> コーダ. "Coda"に続き整数番号を指定できる.
<to Coda> To コーダ. 整数番号を指定可能.
<S> セーニョ. 整数番号を指定可能.
<D.S.> ダルセーニョ. 整数番号を指定可能.
<D.S. al Coda> ダルセーニョ アル コーダ. アルフィーネも同様に指定可能.
<D.C.> ダカーポ
<D.S. al Coda> ダカーポ アル コーダ. アルフィーネも同様に指定可能.
<Fine> フィーネ
[1.] ヴォルタ記号(1番カッコ、2番カッコ)等の指定
./. Simile 記号(シングル)
.//. Simile 記号(ダブル)
"text" テキスト. コードと同じ縦位置に描画.
'text' テキスト. コードの一個上の行に描画.
'text'@ C7 特定コードの上にテキストを描画
`text`@ C7 特定コードの下にテキストを描画. 歌詞などに使用. "/"で改行可能.
, 空白(1コード分に相当)を挿入

Fumenbook

fumenbookはfumenで書いたコード譜をクラウド上へ保存するWEBサービス・Mobile Appです。スコアはクラウド上に保存され、複数の端末ブラウザで同期されます。セットリスト機能、PDFエクスポート機能など便利な機能搭載しています。主にリハやライブでモバイル端末から参照することを念頭に置いています。マークアップ形式での記述がベースですが、モバイル端末からだと書きにくい部分もあるので、GUIベースでの編集も可能になっています。

WEB: https://fumenbook.com/
iOS : https://apps.apple.com/jp/app/fumenbook/id1511784636
Android: https://play.google.com/store/apps/details?id=com.fumenbook

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kinx ライブラリ - 自動組版(Tiny Tyesetting)

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。今回は Tiny Typesetting 最新情報です。これ の続きです。

ココんトコロ、こればかりやってました。はい。一先ず最初にやりたかったことはある程度できるようになりましたので、公開します。

ライブラリというより、単機能として使用できるよう KiTTy って名付けてあります(今時点では)。kxkitty.exe (Windows) と kxkitty (Linux) という実行コマンドも用意しています。

ひとまず、マニュアルをエッサホイサと作ったので、それを参照してみてください。マニュアル自体を KiTTy で作成しています(日本語と英語両方作るのは大変だった...)。ここでは日英両方載せておきます。英語はざざーっと仕上げることを優先したので、かなり自信ありません。

元のファイルはこれです。

これを KiTTy 処理すると以下のようになります。自信はないですが、英語版も載せておきましょう。

どうでしょう?

ちなみに処理速度は今のところ遅いといえば遅いのですが、私個人的にはまぁ我慢できます(私の子なので贔屓目だとは思います)。実行速度を向上できるだろう改善のアイデアはあるのですが、ちょっと時間的な余裕がないのでしばらくはこのままでしょう。あらかじめご容赦いただけるよう先に謝っておきたいと思います。すみません。

使っている技術情報などなど

サポート機能一覧

マニュアルにも載せていますが、KiTTy は組版機能として以下の機能をサポートしています。なお、カーニングは現在サポートしていません。詳しくはマニュアルを読んでみてもらえると嬉しい。

  • 基本組版機能
    • ハイフネーション・ジャスティフィケーション・行分割
    • ウィドウ/オーファン
    • マルチカラム
    • 箇条書き
    • 数式
    • イメージ
    • グラフ(チャート)
    • テーブル
    • フォント
    • 合字・特殊文字
    • プログラム・コード
    • タイトル・カバーページ・目次
    • 見出し
    • 相互参照
    • 引用
    • 脚注
  • 日本語用組版機能
    • 日本語禁則処理
    • 日本語ルビ
  • PDF 機能
    • 外部リンク
    • 相互参照リンク
    • しおり

日本語組版機能も一部サポートしています。ルビも振れます。縦書きはサポートしていません。

主要な個別技術情報

せっかくの技術ブログなので、技術情報を少々。

Knuth-Plass Line Breaking Algorithm

LaTeX で使われている行分割のアルゴリズムです。ハイフネーション処理をした後、個別の Box 単位に Glue と Penalty を挿入し、Penalty が最小になる位置で改行するようにコントロールします。

技術的な詳細は、日本語だと以下が参考になるでしょう。

また、実装は、以下(BSD-2-Clause License)を修正して利用させていただきました。見た目は JavaScript の本領発揮です。

数式

LaTeX を目指す(目指してはいませんが)なら数式が必要です。そこで、JavaScript には KaTeX とか MathJax とかあるので、ここでも 見た目は JavaScript の特長を生かして進めようと思いましたが、細部(フォントとか CSS が必要とか)が違い過ぎて 無理でした。なので、ここでは Duktape + KaTeX で実装しました。が、しかし、レンダリングで躓き、結局最終的には Phantomjs ベースで画像化します。Duktape は必要だったのか、という気もしますが、Duktape は他でも使いようがあるかもしれないので、まぁこのままでいいかな、と実装はステイにしました(修正する時間もあまりなく)。

なお、今時点で Phantomjs を使うのは微妙、という声もちらほらググると出てきますが、動いているので良しとします。node.js とか Chrome とか使わずに単一コマンドでできるのでライブラリ内に閉じて良いと思ってますが、他の手段でも「そう(単一コマンドだけで閉じる感じに)できますよ」という情報があれば下さい。

Chart.js

Phantomjs を内蔵したので、色々できるのでは、と最初にチャレンジしたのがうまくいきました。文書内に自由にチャートをかけるのは非常に便利な気がします。というか便利ですよね。

ちなみに、気をよくして Mermaid.js を組み込もうとして失敗しています(UMLとか書けるといいなー)。そのうち再チャレンジするかもしれません。

その他

その他も紹介できそうなものは色々ありそうですが、時間的制約から本日はここまで。

インストール方法

インストール方法もマニュアルに載っていますが、紹介しておきます。元が Markdown なので、基本コピーな感じでいけますね(ちょっと修正が必要ですが)。

インストールは以下の 2 ステップを実施します。

  1. Kinx のインストール
  2. KiTTy 追加モジュールのインストール

ちなみに、Kinx コアモジュールは 8.5~9M 程度ですが、KiTTy 追加モジュールは(フォントとかも含まれていて)74M くらいあります。ここ(リリースページ) の Assets を見ると分かります。パッケージマネージャーを作るのは大変なので、全部コアに入れない方法を模索しました。パッケージマネージャー相当は今後用意したいですが、今はこの方法で。

Linux

Linux では以下のようにモジュールをダウンロードします。
v0.15.2 は KiTTy ライブラリが正式に追加されたバージョンです。
最新バージョンを使用する場合は書き換えてください。
ワークディレクトリを作成し、移動してください。

$ mkdir temp
$ cd temp

最初に kinx モジュールをダウンロードし展開し、
次に KiTTy パッケージをダウンロードして展開します。

$ curl -L \
    https://github.com/Kray-G/kinx/releases/download/v0.15.2/package_linux-amd64.tar.gz \
    --output package_linux-amd64.tar.gz
$ tar -xvf package_linux-amd64.tar.gz
$ curl -L \
    https://github.com/Kray-G/kinx/releases/download/v0.15.2/package_kitty.zip \
    --output package_kitty.zip
$ unzip package_kitty.zip

展開するとバージョン番号のフォルダができますので、
移動して install.sh コマンドを実行します。

$ cd v0.15.2
$ sudo ./install.sh

これでインストールは完了です。
実行フィルの位置1を確認してみましょう。

$ which kxkitty
/usr/bin/kxkitty

Windows

Windows では Release ページ から最新のパッケージ(以下 2 点)をダウンロードします。

  • package_win64.zip
  • package_kitty.zip

それぞれ展開し、package_kitty.zip の中身を lib フォルダ配下にコピーします2

その際、lib 配下に fonts、phantomjs フォルダが配置されるようにしてください。
圧縮ファイル内のファイル構成が必ずしもそうなっていない可能性があります。

ビルド

通常、ビルドから実施する必要はありません。
既にビルドされた実行モジュールが提供されており、手順にしたがってインストールを実施することで本システムを利用することができます。
あえてビルドから実行したい、といった場合は以下の手順によってビルドを実施できます。

Linux

Github よりクローンし、make します。

$ git clone https://github.com/Kray-G/kinx.git
$ cd kinx
$ make

インストールします。

$ git clone https://github.com/Kray-G/kinx.git
$ cd kinx
$ sudo make install
$ sudo make kitty-install

Windows

Github よりクローンし、make します。

$ git clone https://github.com/Kray-G/kinx.git
$ cd kinx
$ make.cmd

特に現在はインストール用のコマンドを用意していませんが、
ビルドした環境で使用可能です。
ビルドした環境でご使用ください。

hello, world

次の文書を作成し、helloworld.md ファイルとして保存します。

% Hello Kinx Tiny Typesetting
% Your name
% October 7, 2020

<param style="ArticleA4"/>

# Greeting
hello, world

以下のように kxkitty コマンドを実行することで、helloworld.pdf が作成されます。

$ kxkitty helloworld.md

なお、現在はサンプル程度の記載が必要ですが、
もう少しシンプルなサンプルを提示できるよう改善する予定です。

おわりに

多少なりとも「まぁまぁいいんじゃない?」と思いましたら、GitHub スターくれると嬉しいです(いつも同じこと言ってますが)。また、これまで色々と応援してくださっている方々にもあらためて感謝します。ちょっと更新が遅くなり気味な近頃ですが、ちょっとずつ進めていきたいと思いますので、GitHub 上で Issue なり Pull Request なりいただけると大変有難く思います。

では、今後ともよろしくお願いいたします。


  1. マニュアルだと kinx の場所を探してますね。こちらであるべきでしょう。そのうち直します。 

  2. 現時点でインストーラは用意できていませんが、将来的に Windows インストーラを用意する予定です。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JS】各要素に対応する配列があるか調べるプログラム

【JS】各要素に対応する配列があるか調べるプログラム

JSの勉強。

ある配列の各要素の2乗に対応する要素が含まれているか確認するプログラム(same)を作成する。

・同不順
・出現回数が合っていること

例1
//関数sameの結果例
arr1 = [3,2,1,7,2]
arr2 = [49,4,4,9,1]

same(arr1, arr2)
//true

対応する要素がある場合は、trueを返す。


例2
//関数sameの結果例
arr1 = [3,2,1,7,2]
arr2 = [49,4,9,1]

same(arr1, arr2)
//false

2乗に対応する値が存在しても、出現回数が合っていない場合はfalseを返す。

same1

解答1
function same1(arr1, arr2){
    if (arr1.length !== arr2.length){
        return false
    }

    else{   
        arr1 = arr1.sort((a,b)=>a-b)
        arr2 = arr2.sort((a,b)=>a-b)

        for(let i=0; i<arr1.length; i++){
            if(arr1[i]**2 !== arr2[i]){
                return false
            }
        }
     return true
    }
}

sortメソッドを使って各配列をソートしてから比較する。


※注
sortは文字列として並べ替えするため、2桁の数値がある場合は並びが意図しない形になる。

引数でsort((a,b)=>a-b)を指定することで、2桁の数値も正しく並び替えることができる。

sort
arr1 = [6,5,8,10,1,72,25,9]
arr1.sort()

// [1, 10, 25, 5, 6, 72, 8, 9]
js
arr1 = [6,5,8,10,1,72,25,9]
arr1.sort((a,b)=>(a-b))

// [1, 5, 6, 8, 9, 10, 25, 72]


same2

解答2
function same2(arr1, arr2){
    //要素の個数が合っていない場合はfalse
    if(arr1.length !== arr2.length){
        return false;
    }
    for(let i=0; i<arr1.length; i++){
        //arr2の中で、arr1の2乗と対応する値の配列番号を格納していく。
        let correctIndex = arr2.indexOf(arr1[i] ** 2)

        //該当する値がなければ(-1)、falseを返す
        if(correctIndex === -1){
            return false
        }
    }
    return true
}

indexOfメソッドを使う方法。
arr.indexOf(str)

指定した値の配列番号を返す。値がない場合は-1を返す。


same3

解答3
function same3(arr1, arr2){
    if(arr1.length !== arr2.length){
        return false;
    }
    //オブジェクトを2つ用意
    let frequencyCounter1 = {}
    let frequencyCounter2 = {}
    for(let val of arr1){
        //指定したキーに値があればその値+1、なければ0+1する。(キーに該当する数値が何回出現したかカウントする)    
        frequencyCounter1[val] = (frequencyCounter1[val] || 0) +1
    }
    for(let val of arr2){
        frequencyCounter2[val] = (frequencyCounter2[val] || 0) +1
    }
    for(let key in frequencyCounter1){
        //指定した値の有無を確認。なければfalseを返す
        if(!(key**2 in frequencyCounter2)){
            return false
        }
        //出現回数が合っているか確認
        if(frequencyCounter2[key**2] !== frequencyCounter1[key]){
            return false
        }
    }
    //カウンター確認用
    console.log(frequencyCounter1)
    console.log(frequencyCounter2)

    return true

}

オブジェクトを用意し、数値の出現回数をカウントして比較する。

確認
arr1=[3,5,2,1,3]
arr2=[25,4,1,9,9]
same3(arr1, arr2)

//{1: 1, 2: 1, 3: 2, 5: 1}
//{1: 1, 4: 1, 9: 2, 25: 1}
//true

・オブジェクトはkey:valueで定義される配列。
obj[key]=valueで指定したキーに値を代入する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】ページ移動先の、非同期通信のお作法

ページ移動先で使うプロパティ

よくページ構築時で使うプロパティとしてmoutedがありますが、それだと例えばv-forでコンポーネントを作成する時に、移動した後に構築される動きが見られてしまいます。

そうならないためにも、ページコンポーネントを作成された段階には、v-forで回すデータを格納しておく必要があります。

ここで、登場するのがNuxt.jsだけに許されたasyncDataプロパティというものがあります。

ページコンポーネント構築前の非同期通信と、後の非同期通信の比較

通信タイミング プロパティ 用途 /_idの取得
データ初期化前 async asyncData (context) コンポーネントのdataにデータを格納したい時 context.params.id
データ初期化前 async fetch (context) Vuexのstateにデータを格納したい時 context.params.id
データ初期化以降 async mounted () コンポーネントのdataにデータを格納したい時 this.$route.params.id

mounted等と違うのは、ページロード前にデータを取得できるので、コンポーネントが完成された状態でページを表示することができる

Usage

async asyncData(context) {
  const res = await context.$axios.$get(`/api/offices/${context.params.id}`);
  return {
    office: res,
    office_name: res.name,
    office_introduction: res.introduction,
  };
 },

データ初期化前の処理のため、thisや$メソッドを使うことができない

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactのhooksのESLint対応

公式 見るべし。

// Your ESLint configuration の部分は .eslintrc.js 内の設定をかえる

一応、yarn版

yarn add eslint-plugin-react-hooks --dev
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ゲームを作りながら学ぶ!JavaScript レベルアップ講座 part4

こんにちは、Yuiです。

今回はcanvasを使って背景やモンスターを描画する方法について書いていきます。
JavaScript道場はこちら

今回の目標物はこれです。
スクリーンショット 2020-10-19 17.11.44.png

canvasとは?

canvasとは、HTML5に標準装備されている図形を描くための技術仕様で、 HTMLの<canvas>要素とJavaScriptを組み合わせて図形を描画することができます。

公式ドキュメントはこちら。=>https://developer.mozilla.org/ja/docs/Web/HTML/Element/canvas

ざっくりというと、canvasを使うことで、画像の描画やアニメーション、ゲームのグラフィック、データの可視化、写真加工などを簡単に描画することができます。

canvasは基本的には2Dでの画像表示を対象としています。

使い方

まずはcanvasをbodyタグに組み込む必要があります。

<canvas id="canvas" width="640" height="480" style="background-color: black;"></canvas>

そして上記で指定したidを使って、<script>内に組み込みます。

<script>
  const canvas = document.getElementById("canvas")
  const ctx = canvas.getContext("2d")
  const img = new Image()
  img.src = "画像のURL"
  img.onload = () => ctx.drawImage(img, 0, 0, 100, 100)
</script>

順番に解説します。

まず、今回canvasのidはcanvasで指定しました。
なので、document.getElementByIdを使ってcanvasというidの部分にJavaScriptを結びつける必要があります。

そして次のconst ctx = canvas.getContext("2d")はcanvasを2dの画像描画に適用させるということです。ここでctxと変数に格納したので、それ以降はctx.hogehogeという形でcanvasの機能を使うことができます。
canvasという設計図の中に入っているhogehogeという機能にアクセスをすることができるわけです。

canvasでは画像を描画する際にはdrawImage(描画させる画像, x軸, y軸, 横幅, 縦幅)で表すことができるので、ここではctx.drawImage(img, 0, 0, 100, 100)と書くことで、横幅100、縦幅100のimgという画像を(0,0)の位置に表示することができます。

実際にスライムを表示させてみる

実際にスライムを表示させてみましょう。

スライムの画像は以下のURLから用います。
https://cache-www.dragonquest.jp/hoshidora/assets_190310/narikirislime/pc/slime.png

まずは基本となるHTMLの雛形

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

</body>
</html>

body内にcanvas部分を記載

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <canvas id="canvas" width="640" height="480" style="background-color: black;"></canvas>
  <script>
    const canvas = document.getElementById("canvas")
    const ctx = canvas.getContext("2d")
    const img = new Image()
    img.src = "https://cache-www.dragonquest.jp/hoshidora/assets_190310/narikirislime/pc/slime.png"
    img.onload = () => ctx.drawImage(img, 0, 0, 100, 100)
  </script>
</body>
</html>

これで無事スライムが表示されたかと思います!

もしもっと学びたいという方はぜひJavaScript道場にご参加ください。
次回はこれを使ってスライムを動かしていきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DOMと仮想DOMについて

はじめに

DOMとは何かを初学者でもわかることを目的としてまとめて見ました。

DOM(Document Object Model)

DOMは、HTMLをJavascriptから参照・更新を行うためのインターフェイスです。
DOMはHTML文章をオブジェクト型式に解析したもので、Javascriptではそれら全てのオブジェクトへアクセスすることができます。

※DOMはJavascriptがHTMLを認識して操作できるように解析したものだと思っていただけるといいかもしれませんね。

例えば、下記のようなHTMLはDOMで表現すると

<html>
<head>
  <title>Document</title>
</head>
<body>
  <h1>Hello</h1>
  <h2>World</h2>
</body>
</html>

スクリーンショット 2020-10-19 15.46.25.png
上記のように解析されます。この樹形図のような状態をDOMツリーといいます。

仮想DOMとは

Vue.jsやReactでは、DOMとは別にHTMLの状態管理を行う際に仮想DOMを使います。
仮想DOMは、Javascriptエンジンに確保されているDOMと同じ構造でVue.jsとReactの内部に保持されます。
仮想DOMに変更された際に、変更をフレームワークが検知して実際のDOMを更新します。
スクリーンショット 2020-10-19 16.20.36.png

まとめ

JavascriptではDOMを操作することで、ビューに変化をもたらしています。また、JavascriptフレームワークやライブラリであるVueやReactでは仮想DOMを採用しているが最終的にはDOMに対して直接操作してます。

参考サイト

現代のJavascript(DOMツリー)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】3次元のベクトルクラス【HTML5】

はじめに

自分用の3次元のベクトルクラスです。
ES6で書いています。静的クラスとして書いています。

ソース

// 3Dベクトルクラス(ベクトルの計算に使用)
class Vector3d {
    // 足し算
    static add(v0, v1) {
        return {
            x: v0.x + v1.x,
            y: v0.y + v1.y,
            z: v0.z + v1.z,
        };
    }
    // 引き算
    static subtract(v0, v1) {
        return {
            x: v0.x - v1.x,
            y: v0.y - v1.y,
            z: v0.z - v1.z,
        };
    }
    // スカラー倍
    static scale(v0, s) {
        return {
            x: v0.x * s,
            y: v0.y * s,
            z: v0.z * s,
        };
    }
    // ベクトルの長さを返す
    static length(v) {
        return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
    }
    // ベクトルの距離を求める
    static distance(v0, v1) {
        const v = Vector3d.subtract(v0, v1);
        return Vector3d.length(v);
    }

    // 単位ベクトルを返す(非破壊的)
    static unit(v) {
        const len = Vector3d.length(v);
        return {
            x: v.x / len,
            y: v.y / len,
            z: v.z / len
        };
    }
    // 内積
    static innerProduct(v0, v1) {
        return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z;
    }
    // 外積
    static outerProduct(v0, v1) {
        return {
            x: v0.y * v1.z - v0.z * v1.y,
            y: v0.z * v1.x - v0.x * v1.z,
            z: v0.x * v1.y - v0.y * v1.x,
        };
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Native + Firebase で超簡単なチャットアプリを作ってみた‼️ (データ書き込み編)

はじめに

前回の記事までで環境構築とFirebaseの設定をしたので今回から実際にコードを書いていきたいと思います!!
環境構築については第1回の記事を、フォルダの構成やFirebaseプロジェクトの作成については第2回の記事を参考にしてください。

  1. 環境構築編
  2. Firebaseプロジェクト設定編
  3. データ書き込み編 ← 本記事
  4. データ読み取り編

全4回をかけて、簡単なチャットアプリを開発します!
↓↓完成イメージです:bangbang:(再掲)

メインの画面作成

React Nativeのプロジェクトでは、まずApp.tsxを実行するのですが、このApp.tsxに直接長々とコードを書いていくのはスマートじゃないので、メインの画面をscreens/ChatScreen.tsxで記述していきます。
スクリーンショット 2020-10-14 16.14.11.png

後ほど、react-nativeStatusBarも使う予定なので、名前が重複しないようにexpo-status-barStatusBarExpoStatusBarとしてインポートしています。

//screens/ChatScreen.tsx

import React from 'react';
import { StyleSheet, Text, SafeAreaView } from 'react-native';
import { StatusBar as ExpoStatusBar } from 'expo-status-bar';

export const ChatScreen = () => {
    return (
        <SafeAreaView style={styles.container}>
            <ExpoStatusBar style="light" />
            <Text>ChatScreen</Text>
        </SafeAreaView>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#333',
        alignItems: 'center',
        justifyContent: 'center'
    }
});

次に、App.tsx側から、上記のChatScreenをインポートする必要があるので、App.tsxも修正を行います。
スクリーンショット 2020-10-14 16.10.10.png

//App.tsx

import React from 'react';
import { ChatScreen } from './src/screens/ChatScreen';

export default function App() {
    return <ChatScreen />;
}

現在画面はこのようになっているはずです。

テキスト入力範囲の作成

screens/ChatScreen.tsxをこのように修正します。

React Nativeにはstateという概念があり、stateを更新すると画面の再レンダリングが走るという仕組みになっています。

useStateを使うと、stateとstateを更新する関数を作成することができます。
const [text, setText] = useState<string>('')を記述することで、textというstateと、setTextというtextを更新する関数が作られました。

InputTextonChangeTextというプロパティで、テキストの変更の際に実行する関数を指定できます。
onChangeTextに先ほど定義したsetTextを渡すことで、テキストの変更と同時にstateを更新し、画面の再レンダリングを行い入力内容が画面に即時反映される仕組みです。
よくわからないという方は、onChangeText={() => {}}と書き換えてみてください。文字の入力をしても画面に文字が表示されないはずです。

import React, { useState } from 'react';
import {
    StyleSheet,
    TextInput,
    SafeAreaView,
    View,
    Button
} from 'react-native';
import { StatusBar as ExpoStatusBar } from 'expo-status-bar';

export const ChatScreen = () => {
    const [text, setText] = useState<string>('');

    return (
        <SafeAreaView style={styles.container}>
            <ExpoStatusBar style="light" />
            <View style={styles.inputTextContainer}>
                <TextInput
                    style={styles.inputText}
                    onChangeText={(value) => {
                        setText(value);
                    }}
                    value={text}
                    placeholder="メッセージを入力してください"
                    placeholderTextColor="#777"
                    autoCapitalize="none"
                    autoCorrect={false}
                    returnKeyType="done"
                />
                <Button title="send" onPress={() => {}} />
            </View>
        </SafeAreaView>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#333',
        alignItems: 'center',
        justifyContent: 'center'
    },
    inputTextContainer: {
        width: '100%',
        flexDirection: 'row',
        alignItems: 'center'
    },
    inputText: {
        color: '#fff',
        borderWidth: 1,
        borderColor: '#999',
        height: 32,
        flex: 1,
        borderRadius: 5,
        paddingHorizontal: 10
    }
});

このような画面になっているはずです。
まだsendボタンのアクションを定義していいないので、sendボタンを押してもなんも起こりませんが、文字の入力ができると思います。

Firestore にメッセージを保存する

次にsendボタンを押したときのアクションを作ります。
最終的には、sendボタンを押すとFirestoreにメッセージの内容や、送信者の情報などを保存し、Firestoreが更新されると、他の端末でも再度データ取得を行い画面を更新する形にしようと思っています。

ここではまず、メッセージの内容をFirestoreに保存するようにしたいと思います。

lib/firebase.tsというファイルを作成します。

スクリーンショット_2020-10-14_16_58_15.png

//lib/firebase.ts

import * as firebase from 'firebase';
import 'firebase/firestore';

const firebaseConfig = {
    apiKey: 'YOUR_API_KEY',
    authDomain: 'YOUR_AUTH_DOMAIN',
    databaseURL: 'YOUR_DATABASE_URL',
    projectId: 'YOUR_PROJECT_ID',
    storageBucket: 'YOUR_STORAGE_BUCKET',
    messagingSenderId: 'YOUR_MESSAGING_SENDER_ID',
    appId: 'YOUR_APP_ID'
};

if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig);
}

firebaseConfigの調べ方ですが、Firebaseの「プロジェクトの概要」の右側の設定アイコンから、「プロジェクトの設定」に遷移し、ページ下部に記載してあるものをコピペしてください。
スクリーンショット_2020-10-14_16_56_25.png

これで、アプリ側からFirestoreの操作が行なえます。
Firestoreは少し特殊なデータ構造をしており、コレクション>ドキュメント>データという構造をしています。
参照:Cloud Firestore データモデル  |  Firebase

image.png

まず、lib/firebase.tsで、messagesというコレクションの新規ドキュメントの参照を得る関数を定義します。

//lib/firebase.ts

import * as firebase from 'firebase';
import 'firebase/firestore';

const firebaseConfig = {
    apiKey: 'YOUR_API_KEY',
    authDomain: 'YOUR_AUTH_DOMAIN',
    databaseURL: 'YOUR_DATABASE_URL',
    projectId: 'YOUR_PROJECT_ID',
    storageBucket: 'YOUR_STORAGE_BUCKET',
    messagingSenderId: 'YOUR_MESSAGING_SENDER_ID',
    appId: 'YOUR_APP_ID'
};

if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig);
}

export const getMessageDocRef = async () => {
    return await firebase.firestore().collection('messages').doc();
};

Firestoreにメッセージを保存する前準備として、メッセージの型を作成しておきます。
types/message.tsというファイルを作成し、Messageという型を定義します。

メッセージの内容と、送信時間、送信者を管理できるように、このようにMessageの型を定義します。

スクリーンショット 2020-10-14 18.18.22.png

//types/message.ts

import firebase from 'firebase';

export type Message = {
    text: string;
    createdAt: firebase.firestore.Timestamp;
    userId: string;
};

次に、lib/firebase.tsで定義したgetMessageDocRefを用いて、screens/ChatScreen.tsxから新しいメッセージをFirestoreに保存するようにします。

//screens/ChatScreen.tsx

import React, { useState } from 'react';
import {
    StyleSheet,
    TextInput,
    SafeAreaView,
    View,
    Button,
    Alert
} from 'react-native';
import { StatusBar as ExpoStatusBar } from 'expo-status-bar';
import firebase from 'firebase';
import { getMessageDocRef } from '../lib/firebase';
import { Message } from '../types/message';

export const ChatScreen = () => {
    const [text, setText] = useState<string>('');

    const sendMessage = async (value: string) => {
        if (value != '') {
            const docRef = await getMessageDocRef();
            const newMessage = {
                text: value,
                createdAt: firebase.firestore.Timestamp.now(),
                userId: ''
            } as Message;
            await docRef.set(newMessage);
            setText('');
        } else {
            Alert.alert('エラー', 'メッセージを入力してください!')
        }
    };

    return (
        <SafeAreaView style={styles.container}>
            <ExpoStatusBar style="light" />
            <View style={styles.inputTextContainer}>
                <TextInput
                    style={styles.inputText}
                    onChangeText={(value) => {
                        setText(value);
                    }}
                    value={text}
                    placeholder="メッセージを入力してください"
                    placeholderTextColor="#777"
                    autoCapitalize="none"
                    autoCorrect={false}
                    returnKeyType="done"
                />
                <Button
                    title="send"
                    onPress={() => {
                        sendMessage(text);
                    }}
                />
            </View>
        </SafeAreaView>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#333',
        alignItems: 'center',
        justifyContent: 'center'
    },
    inputTextContainer: {
        width: '100%',
        flexDirection: 'row',
        alignItems: 'center'
    },
    inputText: {
        color: '#fff',
        borderWidth: 1,
        borderColor: '#999',
        height: 32,
        flex: 1,
        borderRadius: 5,
        paddingHorizontal: 10
    }
});

一旦userIdは空のままですが、これでsendボタンでFirestoreにメッセージを保存することができます。
「うほっ」と入力し、sendボタンを押すと、、、

このようにドキュメントが追加されているはずです!!

スクリーンショット 2020-10-14 18.24.44.png

次回はFirestoreに書き込んだデータを読み込んで、画面に表示できるようにします!!
チャットアプリは次回で完成します!!:iphone::speech_balloon:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマホのウインドウの高さぴったりにする(アドレスバー分をちゃんと考慮)

100vhがうまく行かないというのがよく知られている部分ですが

area.js
setInterval(function(){
        $(".area").css({"height":window.innerHeight,"min-height":$(window).innerHeight()});
},100)

横向きにした時など回転直後など、window.innerHeightの値が異様に小さくなる。
スクロールをしてアドレスバーのエリアが小さくなった時には、$(window).innerHeight()が伸びた分を検知してくれない。

でこれをした時にposition:fixed;をしているときにtop:0だと、隠れたりしてしまうので、オススメはbottom:0;

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む