20200730のJavaScriptに関する記事は28件です。

herokuでNode.jsを立ち上げるまでの手順

色々と理解できていないことは多いですが、とりあえずherokuで立ち上げられたので手順を記載。
エラーでまくったしいろんなサイト見すぎてもはや何を参考にしたのかわからなくなってます。

前提

① Node.js、herokuはインストール済み
② パッケージマネージャーはyarnを仕様
下記の記事を見てyarnにしました
https://qiita.com/qulylean/items/afa2acff6ed963c88798

yarnのインストール

上記の記事を参考にyarnをインストールします。

yarnでhttpモジュールのインストール

コマンドプロンプトを立ち上げて今回作成したいフォルダまでディレクトリの移動をします。

フォルダの移動
$ cd フォルダ名

前に戻る
$ cd ..

フォルダの作成
$ mkdir フォルダ名

フォルダを作成して移動
$ mkdir フォルダ名 && cd フォルダ名

ファイルの作成
$ type nul > ファイル名

ディレクトリの移動が完了したら下記のコマンドを実行します。

$ yarn add http

すると必要なフォルダが用意されて下記の状態になります。

フォルダ
フォルダ
 ├ node_modules
 ├ package.json
 └ yarn.lock

yarnでexpressモジュールのインストール

次のコマンドを実行してexpressのインストールを行います。

expressのインストール
$ yarn add express

実行完了すると下記の表示がでるので、package.jsonに記載するためにバージョンだけ覚えておいてください。

yarn add v1.22.4
[1/5] Validating package.json...
[2/5] Resolving packages...
[3/5] Fetching packages...
[4/5] Linking dependencies...
[5/5] Building fresh packages...
success Saved 1 new dependency.
info Direct dependencies
└─ express@4.17.1
info All dependencies
└─ express@4.17.1
Done in 3.23s.

package.jsonの変更

初期状態だと下記のようになっていると思います。

pakage.jsonデフォルト
{
  "dependencies": {
    "http": "^0.0.1-security"
  }
}

それを次のように変更します。

pakage.json変更後
{
  "name": "hello_world",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "jade": "*"
  },
  "engines": {
    "node": "12.18.1"
  }
}

現在使用しているnodeのバージョンは下記のコマンドで確認が可能です。

nodeのバージョン確認
$ node --version

JavaScriptファイルの作成

ファイル名は先程、pakage.jsonで指定したapp.jsにして下記を書きます。

app.js
const express = require('express');
const app = express();

const port = process.env.PORT || 5000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(port, () => {
  console.log('listen: ' + port);
});

ここまでできたらまずローカルで動くかを確かめてみましょう。

コマンドプロンプトで下記を実行してください。

$ node app.js

listen: 5000 と表示されたらブラウザで下記を開いてください。

localhost:5000

無事「Hello World!」が表示されていたらOKです。
閉じる場合コマンドプロンプト上でCtrl + C を押すと終了できます。

Procfileの作成

コマンドプロンプトで下記を実行

Procifleの作成
type nul > Procifle

作成したら開いて下記を入力

web: node app.js

最終的には下記のフォルダ構造になります。

フォルダ
フォルダ
 ├ node_modules
 ├ package.json
 ├ yarn.lock
 ├ app.js
 └ Procfile

herokuにデプロイ

デプロイするのに書きの順番で実行していきます。

herokuへのデプロイ
$ git init
$ git add .
$ git commit -m 'init'
$ heroku login
$ heroku create
もしくは
$ heroku create ファイル名
$ git heroku push master

次回から更新する際は一度にまとめても記載が可能です

heroku
$ git add . && git commit -m 'init' && git heroku push master

完了したらherokuを開きます

herokuを開く
$ heroku open

もしくはデプロイ時に出てきた
https://{アプリ名}.herokuapp.com/

をブラウザで開いても大丈夫です。
Hello World!が表示されていたら完了です。

herokuへのデプロイ時にエラーがでたら

下記のエラーが出た場合はyarnのバージョンが古いためのエラーです。

error Your lockfile needs to be updated, but yarn was run with `--frozen-lockfile`.

コマンドプロンプトで下記を実行してyarnをアップデートして再度実行してみてください。

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

【GAS】大量の予定をGoogleスプレッドシートから簡単にGoogleカレンダーに登録しよう

この記事を読んで習得できること

・カレンダーに追加したい大量の予定をスプレッドシートから一括登録出来るようになる

この記事を読んだあなた!

まずはこの画面のどこかにあるLGTM押してください!

(最後まで読んで、「そうでもねえな」って思ったらLGTM消していいです笑)

背景

プロフィールにも書いてあるように、自分は競馬が好きだ。
日本の競馬のみならず、海外の競馬も見たい。
が、海外の競馬は基本的に夜行われているので、結構忘れてしまっている。

なので、忘れないように、googleカレンダーに予め記録しておいて、
毎日確認出来るようにしたかった。

また、海外の競馬といっても、世界各地で行われていて、
一つずつ地道に登録していくのもなかなか骨が折れる。
なので、googleスプレッドシートに全て記載して、
Google Apps Script使って一気にカレンダーに登録してやろうと思った。

そのカレンダーがこちら

https://calendar.google.com/calendar/embed?src=7pv3mcieh1iqcnl9klgc2ght8g%40group.calendar.google.com&ctz=Asia%2FTokyo

現在73レース登録している。これからもまだまだ追加予定。
手入力なんてしてられないよ…

とにかく実装

何はともあれ、まずは実装方法見たいよね。

まず、今回使うスプレッドシートはこちら。
スクリーンショット 2020-07-30 21.46.05.png

時間のところは空いてるけど、今回は使用しないので気にせず。
(将来的に使用するから書いておいた)

今回は、このデータをこんな感じで使用することにした。

項目 使用値(シート列)
タイトル レース名(A列)
日付 日付(B列)
場所 国名(E列) + 競馬場(D列)
説明 グレード(F列)

また、G列がDoneまみれになっているが、これは後で説明します。

早速コードを書いてみる。

(コードに色つけたかったので
コード.gs→コード.js
としています)

コード.js
// スプレッドシートのIDは、スプレッドシートのURLから確認できます
// https://docs.google.com/spreadsheets/d/"{スプレッドシートのID}"/edit#gid=0

// カレンダーIDは「カレンダー」→「設定」→「マイカレンダーの設定」から追加したいカレンダーを選択
// →「カレンダー ID」があるのでそれを使用する

const spreadSheet = SpreadsheetApp.openById("{スプレッドシートのID}")
const calendar = CalendarApp.getCalendarById('xxxxxxxxxxxxxx@group.calendar.google.com')
const sheet = spreadSheet.getSheetByName('シート名')

function createEvents() {
  // スプレッドシートの記載されている最終行を取得
  const listLastRow = sheet.getLastRow();

  for(var i = 1 ; i < listLastRow; i++) {
    // タイトル
    const title = sheet.getRange(i + 1, 1).getValue()
    // カレンダーに追加されているかを判定するために使う
    const isAdd = sheet.getRange(i + 1, 7).getValue()

    // isAddが'Done'(追加済という意味)もしくはtitleが登録されていない場合、この行をパスする
    if(isAdd == 'Done' || title == '') {
      continue
    }

    // timeだけど今回は日付
    const time = new Date(sheet.getRange(i + 1, 2).getValue())
    // 説明
    const description = sheet.getRange(i + 1, 6).getValue()
    // 国名
    const country = sheet.getRange(i + 1, 4).getValue()
    // 競馬場
    const raceCourse = sheet.getRange(i + 1, 5).getValue()

    // オプション(説明と場所はここに定義する)
    const option = {
      description: description,
      location: country + " " + raceCourse,
    }

    // カレンダーに追加する(全日追加する)
    calendar.createAllDayEvent(title, time, option)

    // 追加したことを記録しておくためにG列に'Done'を記載
    sheet.getRange(i + 1, 7).setValue('Done')
  }
}

calendar.createAllDayEvent(title, time, option)で、
タイトルと時間、オプションをカレンダーに記録する。
ちなみに、optionは使用しなくてもいい。

今回は全日で登録するが、もし、時間指定したかったら、
時間指定でやりたかったが、
calendar.createEvent(title, startTime, endTime)
を使おう。
(option使いたかったら、
calendar.createEvent(title, startTime, endTime,option)
みたいに追加する。)

詳しいことはドキュメントに書いてあるからそれを見てくれればいいと思います。

コードが書けたら

ここまで作ったら、createEvents()を実行する。
初めてなら、いろいろ連携についての確認Viewが出ると思うが、OKOKしていけばいい。

これで実行完了されるはず。
カレンダーに追加されたことが確認できると思う。

もう一度、createEvents()を実行しても、カレンダーには新たに追加されないと思う。
これは、G列のDoneが効いているから。

新規で追加したかったら、今まで記載しているものはそのままにして、その下から追加していこう。

終わりに

GASってプログラミングをあまりやったことない人が最初に触ったりする印象がある。
(自分もそうだった)
とりあえずコードコピペして、いろいろいじってみて、試行錯誤しながら正解を見つけていけばいいと思います。

分かりにくかったらコメントください。
読んでいただきありがとうございます。

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

Node.jsとJavascriptの違い、関係について

はじめに

はじめてNode.jsを学んだ方や使用した方はこう思いませんでした?

「なんで同じJavaScriptなのに名前が違うの?サーバーサイドで動くJavaScript?使ってるけどいまいちよくわからない…」

そんな疑問を持った方たちのために簡単に解説していきます。

言語・処理系について

そもそもプログラミング言語とは、コンピュータに情報や命令を伝えるための「文字・記号の集まり及びそのルール」です。そして、プログラミング言語によって書かれた文字列を処理系によって処理することで、コンピュータに情報を伝達することができます。

日本人は日本語を理解することができますよね?逆に、日本語を知らない外国人は日本語を理解することができません。これは、日本人が頭の中に日本語の処理系を持っているから言語を理解できるのです!

このように言語は、その言語の意味やルールを処理・理解してくれる処理系がないと全く機能しなくなってしまいます。

プログラミング言語における処理系とは?

日本語や英語などを理解するための処理系はそれぞれの人間の頭脳でした。じゃあ、プログラミング言語における処理系は何でしょう?
それは、プログラミングをする環境を構築する際にインストールしたやつです!

言語 処理系
Python CPython
C GCC
PHP PHP

など

基本的にプログラミング言語と処理系はセットなのです。

では、フロントでJavaScriptを動かしている方は、何もインストールしてないのに動くじゃん!と思うかもしれないですが、これはブラウザに処理系が搭載されているためです。

じゃあNode.jsって?

そうです、

Node.jsとはJavaScriptの処理系です!

プログラミング言語と処理系の関係について理解できれば、名前が違う!といった疑問も解消できるはずです。

JavaScriptはブラウザで動くフロントサイドの言語と昔は言われていましたが、このNode.jsという処理系によってPythonやPHPといったサーバーサイドの言語と同じようなことができるのです!

おわりに

自分でも曖昧な部分があったので、記事として書き起こすことで整理してみました。
自分の成長のためにも、間違った部分やご指摘があればどんどんコメントお願いします!

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

Javascript 実行環境の構築

概要

ターミナルと Visual Studio Code を利用して、JavaScript で Hello World するための実行環境の手順について記載する。

前提知識

  • Unix コマンドの基礎知識
  • Progate の JavascriptI

使用するソフト

  • Visual Studio Code : js のスクリプト記述のために利用
  • ターミナル : macOS High Sierra ver 10.13.6 に標準搭載されている実行環境
  • Google Chrome Console 画面 : 表示画面 (Google Chrome 右クリック → [検証] で起動)

手順

作業ディレクトリ・ファイルの作成

ターミナルで、任意のフォルダー(ここでは「workspace」とする)を作成し、index.html を作成

ターミナル
mkdir workspace
cd workspace
touch index.html

HTMLの編集

Visual studio Code で index.html を開く
htmlと入力すると html:5 が予測変換で出てくるので選択すると以下の記述が自動的に表示される

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

</body>
</html>

<body>の中に<p>Hello World</p>を入力し、上書き保存

index.html
<body>
    <script type="text/javascript" src="main.js"></script>
    <p>Hello World</p>
</body>

記述したHTMLの確認

ターミナルでindex.htmlを開く

ターミナル
open index.html

index.html がブラウザーで開かれる

HTML に Javascript ファイルを読み込み、コンソール画面に表示する

1.ターミナルで main.jsを作成
2.index.htmlmain.jsを参照させる記述の追加
3.Hello Worldの出力

ターミナル
touch main.js
index.html
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
   #src=""に任意のファイル名を記載する。ここでは「main.js」
    <script type="text/javascript" src="main.js"></script>
    <title>Document</title>
</head>
main.js
console.log("Hello world!!!");

Google Chrome の Console 画面で表示されていることを確認

参考ページ

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

JavaScript 実行環境の構築

概要

Progate で演習した際には、実行環境があらかじめ用意されていた。
本記事では、ローカルで JavaScript を実行するための環境を構築の仕方を記載する。

成果物

Google Chrome のコンソール画面に「Hello World」という文字列を表示する。
スクリーンショット 2020-07-30 22.41.57.png

前提知識

使用するソフト

Visual Studio Code : js のスクリプト記述のために利用
ターミナル : macOS High Sierra ver 10.13.6 に標準搭載されている実行環境
Google Chrome (Google Chrome 右クリック → [検証] で [Console] 画面を表示)

全体像

JavaScript は HTML から呼び出して実行する
スクリーンショット 2020-07-30 23.02.45.png

手順

作業ディレクトリ・ファイルの作成

ターミナルで、任意のフォルダー(ここでは「jssample」とする)を作成し、index.html を作成

ターミナル
mkdir jssample
cd jssample
touch index.html

HTMLの編集

Visual Studio Code で index.html を開く
htmlと入力すると html:5 が予測変換で出てくるので選択すると以下の記述が自動的に入力される

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

</body>
</html>

<body>の中に<p>Hello World</p>を入力し、上書き保存

index.html
<body>
    <script type="text/javascript" src="main.js"></script>
    <p>Hello World</p>
</body>

記述したHTMLの確認

ブラウザでindex.htmlを表示するために、以下のコマンドをターミナルに入力する

ターミナル
open index.html

ブラウザで「Hello World」が表示される。

HTML に Javascript ファイルを読み込み、コンソール画面に表示する

流れは以下の通り

  1. ターミナルで main.jsを作成し、「console.log("Hello World");」と入力
  2. index.htmlmain.jsを呼び出す<script>タグを記述
  3. コンソール画面の確認
ターミナル
touch main.js

main.jsの作成

main.js
console.log("Hello world!!!");

コンソールに文字列を出力するコードを書く

ここで、コンソール画面を確認すると、何も表示されない。
JavaScript を実行するためには、HTML からmain.jsを呼び出すためのコードを追加する必要がある

以下ように<script>タグを追加する

index.html
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="./main.js"></script>
    <title>Document</title>
</head>

<script> タグの使い方

index.html
<script type="text/javascript" src="./ファイル名.js"></script>

Google Chrome の Console 画面で表示されていることを確認
スクリーンショット 2020-07-30 22.41.57.png

参考ページ

HTML, Javascript の違いについて

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

JavaScript で「Hello World」する方法

概要

Progate で演習した際には、実行環境があらかじめ用意されていた。
本記事では、ローカルで JavaScript を実行するための環境を構築の仕方を記載する。

成果物

Google Chrome のコンソール画面に「Hello World」という文字列を表示する。
スクリーンショット 2020-07-30 22.41.57.png

前提知識

使用するソフト

Visual Studio Code : js のスクリプト記述のために利用
ターミナル : macOS High Sierra ver 10.13.6 に標準搭載されている実行環境
Google Chrome (Google Chrome 右クリック → [検証] で [Console] 画面を表示)

全体像

JavaScript は HTML から呼び出して実行する
スクリーンショット 2020-07-30 23.02.45.png

手順

作業ディレクトリ・ファイルの作成

ターミナルで、任意のフォルダー(ここでは「jssample」とする)を作成し、index.html を作成

ターミナル
mkdir jssample
cd jssample
touch index.html

HTMLの編集

Visual Studio Code で index.html を開く
htmlと入力すると html:5 が予測変換で出てくるので選択すると以下の記述が自動的に入力される

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

</body>
</html>

<body>の中に<p>Hello World</p>を入力し、上書き保存

index.html
<body>
    <script type="text/javascript" src="main.js"></script>
    <p>Hello World</p>
</body>

記述したHTMLの確認

ブラウザでindex.htmlを表示するために、以下のコマンドをターミナルに入力する

ターミナル
open index.html

ブラウザで「Hello World」が表示される。

HTML に Javascript ファイルを読み込み、コンソール画面に表示する

流れは以下の通り

  1. ターミナルで main.jsを作成し、「console.log("Hello World");」と入力
  2. index.htmlmain.jsを呼び出す<script>タグを記述
  3. コンソール画面の確認
ターミナル
touch main.js

main.jsの作成

main.js
console.log("Hello world!!!");

コンソールに文字列を出力するコードを書く

ここで、コンソール画面を確認すると、何も表示されない。
JavaScript を実行するためには、HTML からmain.jsを呼び出すためのコードを追加する必要がある

以下ように<script>タグを追加する

index.html
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="./main.js"></script>
    <title>Document</title>
</head>

<script> タグの使い方

index.html
<script type="text/javascript" src="./ファイル名.js"></script>

Google Chrome の Console 画面で表示されていることを確認
スクリーンショット 2020-07-30 22.41.57.png

参考ページ

HTML, Javascript の違いについて

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

ニートワイ「クラウドソーシングの案件やってみた」

ある日の我が家

ワイ 「クラウドソーシングって初めてやわ」
娘 「クラウドソーシングはランサーズクラウドワークスココナラが有名だね」

ワイ 「誰やおまえ急に」
ワイ 「ニートのワイには娘なんておらんやろ」
娘 「まあ最初だし過去の案件を見てみるといいんじゃない」
ワイ 「そうやな」

~過去の案件を見る~

ワイ 「3つのサイトをざっと見たが」
ワイ 「全体的に割に合わない案件が多い気がする」
ワイ 「あくまでもワイの感想ね。実際には知らん。」
娘 「そうだね」
ワイ 「まぁせっかく見たしQiitaに何か投稿するネタないかな」
ワイ 「せや 過去の案件をやってみてドヤ顔したろ」

~過去の案件を探すワイ~

ワイ 「よしこれにするわ」

案件内容

URLのパラメータを引き継いだままページ遷移する方法を教えてください

ワイ 「JavaScriptで楽勝」

成果物

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>URLのパラメータを引き継いだままページ遷移する</title>
  </head>
  <body>
    <a id="link" href="http://localhost/a.html">link</a>
    <script>
      document.getElementById('link').onclick = function (e) {
        // イベントキャンセル
        e.preventDefault();

        // ページ遷移
        location.href = this.href + window.location.search;
      };
    </script>
  </body>
</html>

ワイ 「5分もかからず終わったわ」
ワイ 「この案件はこれで3000円くらい貰えたのかな?」

〜おしまい〜

動画

IMAGE ALT TEXT HERE

ワイのGitHubとか

GitHub: https://github.com/yuzuru2
YouTube: https://www.youtube.com/channel/UCuRrjmWcjASMgl5TqHS02AQ
Qiita: https://qiita.com/yuzuru2
LINE: https://line.me/ti/p/-GXpQkyXAm
Twitter: https://twitter.com/yuzuru_program
成果物まとめ: https://qiita.com/yuzuru2/items/b5a34ad07d38ab1e7378

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

heroku×node.jsでcorsエラーがなかなか解決しないときに気をつけること

node.js(express)でサーバーサイドを記述し、herokuに上げていたのですが、今まではすんなりと解消できていたcorsのエラーに嵌ってしまったので、記事を書くことにしました。

環境

  • heroku: 7.42.5
  • node.js: 12.16.2
  • express: 4.17.1
  • cors: 2.8.5

基本的な対応方法

今回はサーバーサイドとフロントエンドを異なるレポジトリに書いているため、フロントエンドからfetch等でサーバーサイドのAPIを呼ぶ際には、corsのエラーが生じてしまいます。

Access to fetch at 'https://xxx.com' from origin 'https://yyy.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

基本的にはnpm install corsをして、

const express = require('express')
const cors = require('cors')
const app = express()
app.use(cors())

と書けば解決します。

注意点① - credentialsをtrueにする場合

ただ、credentialsをtrueとしたい場合には、originを具体的に指定する必要があります。

const express = require('express')
const cors = require("cors");
const app = express();
const corsOption = {
  origin: [
    "http://xxx.com",
    "https://yyy.com",
  ],
  credentials: true,
};
app.use(cors(corsOption));

というようにします。
originは、1つの場合は配列なしの文字列でも大丈夫で、1つにしろ複数にしろ正規表現を用いることもできます。
ここまでは、ぐぐると色々な解説が出てくると思うのですが、今回はさらに二重に嵌ってしまっていました。

注意点② - 最後の"/"

1つめは、originの最後に"/"を入れてしまっていたことです。
本来は"http://xxx.com"としなければいけないところを、アドレスバーからコピペするなどして、"http://xxx.com/"としてしまうと、エラーが生じます。

注意点③ - herokuのconfig varsが登録されていない

2つめは、herokuのconfig varsが登録されていなかったということです。
かなり初歩的なミスなのですが、例えば、process.env.TEST_KEYみたいなコードを書いていたとして、ローカルでは.envの変数を参照していたとします。
当然、herokuではconfig varsにTEST_KEYを登録しなければいけないのですが、必要なキーなどを参照できなかった場合も、corsのエラー同様のメッセージが表示されてしまうため、数時間を無駄にしてしまいました。
皆様もお気をつけください。

参考

Express cors middleware
Nodejs express, Heroku CORS

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

jQueryでDOMを操作するメソッドについて

前提(jQueryとは?)

Javascriptのライブラリの一種で、Javascriptを使いやすくする為に上から被せるようなもの。
Javascriptで書くと長くなるスクリプトを短くしたり、clickした後のアクションやイベントを作れる。
当然だが、jQueryを使うためには、ライブラリを読み込む必要がある。

CDN(Content Delivery NetWork)の読み込みについて

インターネット経由でファイルを配信する仕組み。URLを指定することによってそのまま参照できるので
ファイルをダウンロードする必要がなくなる。
また、ユーザー側でも複数のサイトで同じjQueryのバージョンを使っていたらキャッシュの読み込みで
ページの読み込み時間を省略できる事もメリット。

定義するときは以下のように定義する。

index.html
<body>
....
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<script>
 ... // jQuery本文
...
</script>

メソッド一覧(順次追加予定)

No メソッド 意味
1 append() 指定した子要素にテキストやHTML要素を追加
2 prepend() 指定の要素内に文字列やHTML要素を追加
2 prepend() 複数の要素を先頭に追加
3 before() 指定した要素の前に追加する
4 replaceWith() 別の要素を指定要素に置換する
5 remove() 指定した要素を削除する
6 find() 指定された要素を検索する
7 text() HTML要素内にあるテキスト情報を取得、変更する
8 empty() 要素の中身を削除する
9 after() 指定した要素の後に追加する
10 attr() 属性を取得、変更、追加する

サンプルコード

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Title</title>
    <style>
        .replace {
            color: red;
        }
        .replaceNot {
            color: black;
        }
        .replacetarget {
            color: black;
        }
        .empty {
            background-color: skyblue;
        }
    </style>

</head>
<body>
    <ul class="category">
        <li class="name">項目1</li>
        <li>項目2</li>
    </ul>

    <div class="text">
    </div>

    <div id="target">
        <p>一つ前に追加</p>
    </div>

    <button class="click">クリック</button>
    <div class="replacetarget">divタグが置換</div>
    <div class="replaceNot">divタグ置換不可</div>
    <div class="remove">このdivタグはボタンを押したら削除</div>
    <div class="empty" style="padding: 10px">
        <span>ここは消えますが色は残ります</span>
    </div><br>

    <button class="addclass">更新</button>
    <div id="find">
        <input type="text" size="50" value="更新ボタンを押してください">
    </div>

    <script src="https://code.jquery.com/jquery-3.5.1.js"></script>

<script>
    $('ul.class').append('<li>項目3</li>'); // li要素を追加する
    $('li.name').prepend('<strong>名前</strong>'); // li要素のテキストを強調する
    $('div.text').prepend('<p>タイトル</p>','<p>テキスト</p>'); // 指定要素の中にp要素を追加する
    $('div#target').before('<div class="before"><span>新しく追加</span></div>'); // 指定要素の前に追加する
    $('button.click').click(function() { // クリックボタンを押したらテキストを更新する
        let replaceTo = '<div class="replace">置換完了</div>';
        $('div.replacetarget').replaceWith(replaceTo); // 不要部分を書き換える
        $('div.remove').remove(); // タグと要素を全て削除
        $('div.empty').empty(); // 要素だけ削除
    });
    $('button.addclass').click(function() { // ク更新ボタンを押したらテキスト内容を書き換える
        $('div#find').find('input[type="text"]').attr('value','テキストを更新しました');

    });

</script>

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

GithubActions を作って動かそうとしたら "ReferenceError: define is not defined" と言われた

やりたかったこと

typescript で 書いた GitHub Actions のコードをトランスパイルして実行する。
しかし、GitHub Actions で呼び出した際にエラーが発生する。

エラー文

エラーが発生した箇所
https://github.com/tkt-actions/add-issue-links/runs/925347571?check_suite_focus=true

Run tkt-actions/add-issue-links@v1.0.2
/home/runner/work/_actions/tkt-actions/add-issue-links/v1.0.2/lib/main.js:1
define("domain/error/BaseError", ["require", "exports"], function (require, exports) {
^

ReferenceError: define is not defined
    at Object.<anonymous> (/home/runner/work/_actions/tkt-actions/add-issue-links/v1.0.2/lib/main.js:1:1)
    at Module._compile (internal/modules/cjs/loader.js:959:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
    at Module.load (internal/modules/cjs/loader.js:815:32)
    at Function.Module._load (internal/modules/cjs/loader.js:727:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)
    at internal/main/run_main_module.js:17:11

どうやって直したか

差分全体: https://github.com/tkt-actions/add-issue-links/compare/v1.0.1...v1.0.5

絶対パスで指定していた import を相対パスで書き直した。(tsc は絶対パスの解決を行ってくれない)
(ここがエラー文の原因なのかはいまいちあやしい)

過程

ReferenceError: define is not defined の解決

差分全体: https://github.com/tkt-actions/add-issue-links/compare/v1.0.2...v1.0.4

tsconfig でビルドのターゲットを変更することで、ReferenceError: define is not defined は解決したが、新たに MODULE_NOT_FOUND のエラーに遭遇した。

エラー: https://github.com/tkt-actions/add-issue-links/runs/925458465?check_suite_focus=true

tsconfig.json

     "strict": true,
     "noImplicitAny": true,
     "strictNullChecks": true,
-    "target": "es2019",
+    "target": "es6",
     "sourceMap": true,
     "outDir": "./lib",
     "baseUrl": "./",

MODULE_NOT_FOUND の解決

差分全体: https://github.com/tkt-actions/add-issue-links/compare/v1.0.4...v1.0.5

絶対パスで指定していた import を相対パスで書き直した。
すると、 target の指定を戻しても、動くようになった。

まとめ

ビルドは、よーわからん

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

セミコロンをつけ忘れただけなのに...【JavaScript】

はじめに

某小説っぽいタイトルにしちゃいましたが、
JavaScriptでコードを書いていた後輩が二つのコードの挙動の差に困っていたので、
原因究明をしようとしたら、全然わからなかったのですが、どうやらいろんなJavaScriptの仕様が絡み合ってできた罠だったぽいので、
せっかくなので、今回でわかったことを記していこうと思います。

原因のコードのサンプル

エラーになるコード
function foo() {
  let point = {}
  try {
    const lat = 35
    const lng = 132
    [point.lat, point.lng] = [lat, lng]
  } catch (e) {
    console.error(e)
  }
}
foo()

みた感じ、tryブロックの中で、latとlngにセットされた値をpointオブジェクトに追加しているように見えると思います。
一見おかしなところがないように思えるのですが、このコードを実行すると以下のエラーになります。

VM50:8 ReferenceError: Cannot access 'lng' before initialization
    at foo (<anonymous>:6:36)
    at <anonymous>:11:1

しかし、以下のようにconstで初期化する処理をtryブロックの外に移動させるようにコードを変更すると実行することが可能になります。

宣言場所を変えただけのエラーにならないコード
function foo() {
  let point = {}
  const lat = 35
  const lng = 132
  try {
    [point.lat, point.lng] = [lat, lng]
  } catch (e) {
    console.error(e)
  }
}
foo()

なぜでしょう? というのが課題提起です。

解決策

上記エラーになるコードの方でconst lng = 132の後ろにセミコロンをつけるとエラーなく動きます。

なんで?

まずはエラーの内容を読み解く

このコードを読む前に表示されたエラーについて確認しましょう。

VM50:8 ReferenceError: Cannot access 'lng' before initialization
    at foo (<anonymous>:6:36)
    at <anonymous>:11:1

このエラーの内容はかなり明確で、constで定義しているlngに対して、初期化する前にアクセスしちゃっているからやめてねということを伝えようとしています。
でもソースコードをみてみると、明らかにlngを定義すると同時に初期化してからlngへのアクセスを行なっていますよね。

しかも、動く方のコードとの差分はtryブロックの外で定義するか中で定義するかの違いでしかありません。

const,letだからスコープの違い?

結論から言うとスコープの違いによることが理由ではありません。

動かないコードと動くコードの比較をしてみても、定義場所の違い以外に差があるようには思えないので、
一見ブロックスコープが影響しているように思う人もいるかもしれません。
たしかにconstとletはブロックでスコープを作りますが、それはブロック内で宣言されたconst,letの値に対してブロックの外では参照することができないというもので、ブロックの外で宣言されたものはブロックの中で参照することが可能です。

// ブロック内で定義されたものへの外からアクセス
if (true) { const hoge = 'fuga'}
console.log(hoge);
//VM196:1 Uncaught ReferenceError: hoge is not defined
//    at <anonymous>:1:13

// ブロック外で定義されたものへの内からアクセス
const hoge2 = 'fugafuga';
if (true) { console.log(hoge2) }
// fugafuga

つまり今回のエラーはスコープのエラーによるものではないですね。

考えること1: JavaScriptはなぜセミコロンがなくても動くのか?

私たちがJavaScriptでコードを記載している際に、セミコロンをつけずとも正しく構文を解釈してスクリプトの実行をすることができることはみなさんご存知のことだと思います。

let a = 1
console.log(a)
// => 1

これは裏側ではどのようなことが行われているのでしょうか?

ECMAScriptの仕様を見るとそのことについて記載されています。
11.9 Automatic Semicolon Insertion

Most ECMAScript statements and declarations must be terminated with a semicolon. Such semicolons may always appear explicitly in the source text. For convenience, however, such semicolons may be omitted from the source text in certain situations. These situations are described by saying that semicolons are automatically inserted into the source code token stream in those situations.

ざっくり内容を読んでみると、全てのステートメントと宣言はセミコロンで終わるべきであり、セミコロンがないときには特定条件下では自動で挿入すると記載されています。

上記11.9の下にある11.9.1も合わせて読んで見ると、
セミコロン挿入には3つの基本的なルールがあるという記載がありまして、
それらを読むともう少し具体的にルールがわかると思います。

ざっくりまとめると以下のような場合にセミコロンが自動で追加されます。
(和訳に自信がないので間違ってたら優しく教えてください:bow:)
コードを左から右に読み込んだときに
1. 文法の生成で許可されていないトークンが発見されたとき。
2. パーサーが入力トークンストリームをゴール非終端の単一インスタンスとして解析できない場合。
3. 文法の生成によって許可されているトークンが検出されたが、生成が制限された生成であり、トークンが直後のターミナルまたは非ターミナルの最初のトークンの時。

つまり、JavaScriptは上記のコードを一旦一行のコードとして解釈をしていて、文法上おかしかったり、宣言ステートメントが途中にあったりした場合には、ルールに乗っ取った上で自動でセミコロンを自動挿入した上で実行しているわけですね。

例えば以下のようなコードだったとしても、

let a
=
1
console.log(a)

以下のように解釈されて

let a = 1;console.log(a);

として実行されているわけですね。
だから私たちはセミコロンをつけずともJavaScriptを記載することができるのです。

ただ、今回のソースコードに落とし込むと、正しく解釈されず以下のように解釈されてしまったようです。

エラーになるコードにセミコロンが挿入された場合
function foo() {
  let point = {};
  try {
    const lat = 35;
    const lng = 132[point.lat, point.lng] = [lat, lng];
  } catch (e) {
    console.error(e);
  }
}
foo();

なぜでしょう?

考えること2: なぜ上記コードはセミコロンを自動でつけずに実行してくれなかったのか?

結論から述べると文法上おかしな場所がなかったために、セミコロンが挿入されなかったことが原因です。
上記考えること1で一定のルールをもとにセミコロンを自動挿入することがわかるのですが、
この部分にはJSの文法上おかしなところがなかったので、jsは同一行が続いてると勘違いしてセミコロンが付与されなかったわけですね。

const lng = 132[point.lat, point.lng] = [lat, lng];

ECMAScriptの仕様ページの11.10.1 Interesting Cases of Automatic Semicolon Insertion in Statement Lists にも、今回のケースの内容が記載されておりました。

An opening square bracket ([). Without a semicolon, the two lines together are treated as property access, rather than an ArrayLiteral or ArrayAssignmentPattern.

tryの外に宣言した方の動くソースコードの方では、try {が宣言と呼び出しの中間にあるおかげで、const lng = 132;のようにセミコロンを挿入したので動いていたということです。

  const lng = 132;
  try {
    [point.lat, point.lng] = [lat, lng]

考えること3: const lng = 132[point.lat, point.lng] = [lat, lng];の挙動について

上記の考えること2の中で、文法上問題がないからセミコロンが入らないという話をしたのですが、

const lng = 132[point.lat, point.lng] = [lat, lng];

に対してPrettierをかけると以下のように変換してくれるようです。

const lng = ((132)[(point.lat, point.lng)] = [lat, lng]);

この時、[(point.lat, point.lng)]のところでは
上記式の場合にはArrayアクセスを行なっているように見えた場所はArrayアクセスではなくBracketアクセスを行なっているようです。
ブラケット内にてカンマ区切りで値をセットすると、末尾の項が返されるという仕様があるらしく、上記コードの場合にはpoint.langが返るようです。

試しに、ディベロッパーツールのconsoleに1,2,3,4,5とだけ記載すると以下のように返ってきます。
スクリーンショット 2020-07-29 15.28.41.png
たしかに末尾の5が返されますね。

したがって、上記コードは文法上問題ないという形になるわけらしいです。

const lng = 132[point.lng] = [lat, lng];

難しい…

結論

僕は絶対セミコロンを忘れずにつけようとおもいます。

参考

https://tc39.es/ecma262/#sec-automatic-semicolon-insertion

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

PUGの書き方

PUGの書き方基礎

https://pughtml.com/ で確認しながら書くと良さそう。
https://pugjs.org/ ←本家サイト

最初の単語がタグになる

p => <p></p>

2番目以降の単語はそのタグの中の内容

p contents => <p>contents</p>

タグの入れ子はインデントで表現

p
  p test

     ↓

<p><p>test</p></p>

属性はタグ名の直後の()の中に書く

a(href="link.html") link => <a href="link.html">link</a>

クラス名はタグ名の後に、「.」をつけて書く。

p.test => <p class="test"></p>

ID名はタグ名の後に、「#」をつけて書く。

p#test01 => <p id="test01"></p>

タグ名を省略してクラス名やID名から始めた場合はdivタグになる

.test => <div class="test"></div>

複数行の内容を書きたい場合

タグの直後「.」で終わらせ、次の行にインデントを下げて書く

script(type='text/javascript').
      let a = "javascript";
      let b = "java";

     ↓

<script type="text/javascript">
    let a = "javascript";
    let b = "java";
</script>

Node.jsから渡された変数を呼び出すとき

例えば <title>変数の値</title> というHTMLを出力したいとき、

変数の値をエスケープ処理する場合

title #{変数名}

  もしくは

title= 変数名

  とする。 title=の「=」の前にスペースを入れてはダメ。

変数の値をエスケープ処理しない場合

title !{変数名}

とする。

インライン要素は3通りの書き方がある。

例えば<p>befor <span class="test">spanタグ</span> after</p>と出力したい場合。

1.そのまま書く

    p befor <span class="test">spanタグ</span> after

2.#[]を使う

    p befor #[span.test spanタグ] after

3.「|」を使う

    p befor 
      span.test spanタグ
      | after

参考サイト

https://www.tam-tam.co.jp/tipsnote/html_css/post12789.html

Next Step

forやif文など https://docs.qranoko.jp/static/pug.html#example

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

ダイエットを続けるために玄関で懸垂しないと家の鍵が開かない仕組みを爆速でつくってみた

こんにちは!わみ(@wamisnet)です。

ダイエットって続かないですよね…

みんな続けられてたらライザップみたいなものはないので同志はいっぱいいると思います(思いたい

そんなことをふんわりとおもっていたらこんなツイートを見かけました

【懸垂しないと家に入れない】

これだ!!!

お仕事や買い物などで少なくとも家の外に出かけるので、その帰宅したときに懸垂しないと入れないようにしたら、面倒でサボるとはならないはずです

スマートフォンとかでやるのも手ですけど、偽装しやすいので、専用のハードウエアをつくってやってみます

そんなおかしなアイデアをテクノロジーで実現させちゃったお話です。

この記事は技術編として、どんな風にこれができているのか紹介していきます。プログラムの説明記事もありますのでご覧ください。

【おうちハック】筋トレしないとお家に入れないようにしてみた【プログラム編】

作ったもの

仲間集め

さっそくさっきのツイートを今住んでいるシェアハウスのSlackで話題にしました。

slack.png

面白そうって言ってくれたのでプロトタイピングでモノを作ってみようと思います!

せっかく4連休があることなので、爆速でモノを作ってみようと思います。

どんな風に動くの???

下準備

まえもって次の写真のように懸垂するところの天井にお手製ハードウエアを取り付けておきます。

あとはカギを開けるために必要な懸垂の回数を設定しておきます。(何度も取り直しているので最後のほうヘロヘロになりながらドアをあけていますw

懸垂のカウント

懸垂をすると、天井に取り付けられたお手製ハードウエアに内蔵されている距離センサーが反応します。

懸垂によって体と天井の距離が離れたり、近づいたりすると懸垂をしたとしてカウントアップされます。

カウントの表示

デバイスから懸垂が何度目かわからないとやる気でませんよね??

天井にある都合上表示したところで確認するのは大変ですし、しゃべらすのも技術的にちょっと難しいです。

というわけでスマートフォンから確認できるWebページを用意しました!

Webページには、懸垂の回数を確認できるのはもちろん、鍵を開けるために必要な懸垂の回数を設定できるようにしたり、懸垂の回数をクリアできるようなボタンも用意しておきました。

ちなみに鍵を開けるために必要な懸垂の回数は最大255回まで設定できます。(これぐらいあればだいぶ疲れるよね…?

もっとできるバケモノがいたら改善しないといけないですね!w

カウント達成

懸垂を指定された回数を達成するとSESAMEというスマートロックによって家の鍵を解除してくれます。

これが全体の流れになります。

構成技術

ちょっと難しいワードが出るかもしれませんが、そんなに気にしないで雰囲気ですすめていきましょう。

全体の構成図を作ってみました。

system.jpg

あ、使用したプログラミング言語は次の通りです

  • ハードウエア
    • C++(Arduino)
  • Webページ
    • HTML
    • CSS
    • JavaScript

お手製ハードウエア

もう少し詳しい説明をしていきます。

むかーし作った基板が落ちていたので、それでサクッとはんだ付けしちゃます。

緑色の基板が3.5cmの正方形で500円玉が2.6cmなので500円玉と比較するとちょっと大きめではありますが、小さいのが伝わるでしょうか?

その上に米粒より小さい1.6mmしかない部品やはんだ付けする間隔が0.65mmしかない部品をはんだ付けしていきます。

これでもスマートフォンなどで使われている部品からしたらとても巨大で、なれば簡単にはんだ付けできるようになるんだから慣れって怖いものですね。

完成したのハードウエアはこんな感じです。できたのでケースに収めます。

device.jpg

プログラムの作成

ハードウエアができたところでプログラムを作成する必要があります。

今回はつい最近リリースされた「obniz plug-in」を活用して作成しました。

これを活用すると次のようなメリットがあります。

  • 簡単にネットワークに接続できる
  • 高頻度のデータでもロスせずに扱える
  • obnizでサポートしていないセンサーを活用できる
  • Webページと簡単に接続ができる

といった一からつくるより簡単にオリジナルのIoTデバイスを開発できます。

それを活用して短いコード数でプログラムを作成しました。

懸垂回数の表示

これも先ほど話にでた「obniz」を活用して作成しています。

普通のIoTであれば、Webとハードウエアを繋げるのには大変な苦労があります。

それは、異なるモノとモノをつなげようとしているからなのですが、obnizではその差を吸収してくれます。

あとは、簡単にHTML、CSS、JavaScriptを書くだけで、Webページが完成してしまいます。

実際に2時間ぐらいでサクッと作ることができています。

カギの制御

鍵の制御は、SESAMEというスマートロックを購入します。

その子をこんな感じにドアに取り付けて、鍵を開けてもらいます。

SESAMEのいいところとして、APIとよばれるプログラムから指示を送ると鍵を開けてくれる機能があるのでそれに指示を飛ばすだけです!

いまのシェアハウスでは、Suicaや指紋認証で入れるようになっており、このAPIを活用しています。

詳しくはこちらの記事にまとまっていますので興味のある方はぜひご覧ください!

まとめ

こんな感じで筋トレしないと鍵が開かない仕組みを作ることができました。

別記事で、プログラムの解説をした記事も用意しましたので興味のある方はご覧ください!

【おうちハック】筋トレしないとお家に入れないようにしてみた【プログラム編】

今回のこのような面白デバイスのような開発やはたまた真面目な農業用IoTといったものまで受託開発しておりますので、予算とこんなアイデア作ってみたいといったものがあればぜひ

お問合せしていただければと思います。

いいねやコメント、SNSでの共有等をしてくださると、今後の励みになります。よろしくお願いします。

よかったらTwitter(@wamisnet)もフォローしてね!

別のハック記事

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

Vue.js、Svelte.js、jQueryのToDoアプリを作って構文を比較した

Vue.js、Svelte.js、jQueryの構文比較

Vue.jsとSvelte.jsでそれぞれTodoアプリをつくって違いを見比べてみました。
作ったものがシンプルすぎるせいもありますが、ぱっと見では違いが分からないくらいには書き方が似ているので、Vueに慣れていたらSvelteの習得は楽だと思います。

tasllist.PNG

Vue.jsでの実装

まずはVueでつくったToDoアプリのソースコードです。

【Vue.js】
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Task list</title>
<style>
[v-cloak] {
    display:none;
}
</style>
</head>
<body>
<div id="app">
    <h2>Task list</h2>
    <input v-model="newTaskName">
    <button v-on:click="add">add</button>
    <ul>
        <li v-for="task in tasks">{{ task.text }}</li>
    </ul>
</div>
</body>
<script src="https://unpkg.com/vue"></script>
<script>
let app = new Vue({
    el: '#app',
    data: {
    tasks: [],
    newTaskName: '',
    },
    methods: {
        add: function(){
            if(this.newTaskName == "") return;
            this.tasks.push({text: this.newTaskName});
            this.newTaskName= '';
        }
    }
});
</script>
</html>

Svelte.jsでの実装

つぎにSvelteでつくったソースコードです。
ループの書き方がVueとは異なりますね。またデフォルトのデリミタがVueは{{と中括弧2つなのに対して、Svelteの方は{と中括弧1つとなっています。

【Svelte.js】src/App.svelte
<div id="">
    <h2>Task list</h2>
    <input type="text" bind:value={newTaskName}>
    <button on:click={() => add()}>add</button>
    <ul>
        {#each tasks as task}
        <li>{task.newTaskName}</li>
        {/each}
    </ul>
</div>
<script>
let tasks = [];
let newTaskName = '';

function add(){
    if(newTaskName == '') return;
    tasks = [...tasks,{newTaskName}]; //スプレッド構文を使用
    newTaskName = '';
};
</script>
【Svelte.js】src/main.js
import App from './App.svelte';

const app = new App({
    target: document.body
});

export default app;

上記2つを見比べると構文の雰囲気がよく似ている事が分かります。
現在Vueの方が日本語のドキュメントも充実しているし書籍も数多く出ているので学習環境は整っていますが、一旦Vueを習得してしまえばSvelteへの移行は非常に楽なんじゃないかと思います。

スプレッド構文の参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax

jQueryでの実装(参考)

参考までにjQueryでも同じ挙動のToDoアプリを実装してみました。
この程度のアプリだったらむしろjQueryの方が分かりやすいかもしれません。
VueもSvelteもJavaScriptのソースコードにクリックイベントを記述する必要がないですが、jQueryの場合はclassやID等をトリガーとしてクリックイベントを設定する事が必須となります。

<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='UTF-8'>
<title>Task list</title>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js'></script>
</head>
<body>
<h2>Task list</h2>
<input type='text' value='' id='text'>
<button id='add'>add</button>
<ul id='todo'></ul>
<script>
const App = class {
  add() {
    $('#add').on('click', () => {
      let text = $('#text').prop('value');
      let html = `<li>${text}</li>`;
      $('#todo').append(html);
      $('#text').prop('value', '');
      });
    }
  }
let todo = new App();
todo.add();
</script>
</body>
</html>

所感

ちょっとSvelte.jsにもちょっかい出してみましたが、現状は日本語ドキュメントが充実しているVueの方が取っつきやすく、構文も見やすいなぁと感じました。
また、Svelteのうり?となっている実行速度の速さはある程度大きなアプリでないと体感し難いかもしれません。とりあえずフロントのフレームワークはjQuery以外では引き続きVueを勉強していこうって思いました。

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

FragmentShaderを表示するreactコンポーネントの作り方

fragmentshader.jpeg

概要

CRAでプロジェクトを作成、プロジェクトにPackageを入れて、↑を表示するシンプルなReactコンポーネントの作成を行います。

環境

  • Mac
  • yarn/npx (CRAのグローバルインストールは非推奨のため)
  • TypeScript 3.5+ (オプション)

CRAでベースアプリを作成

# npxの場合
$ npx create-react-app myshader

# yarnの場合
$ yarn create react-app myshader


# npx & typescriptを利用する場合
$ npx create-react-app myshader --template typescript

# yarn & typescriptを利用する場合
$ yarn create react-app myshader --template typescript

大抵の場合は必要ないですが、一応CRAがちゃんとコードを生成している事を確認しておきます。

$ cd myshader

# yarn
$ yarn start

# npm
$ npm start

[TypeScriptの場合]
cra.png

react-vfxをインストール

react-vfxをインストールします。
併せて、styled-componentsも入れておきます。CSSやSCSSまたはSASSが好きな方はインストールしなくても全く問題ないです。
ちなみ、react-vfxはtsで書かれているので、@typesファイルのインストールは不要です。

# yarn
$ yarn add react-vfx styled-components
$ yarn add -D @types/styled-components <-- if you use typescript

# npm
$ npm install react-vfx styled-components
$ npm install -D @types/styled-components <-- if you use typescript

コンポーネントを作成

一番最初の画像を表示するためのシンプルなコンポーネントを作成していきます。
名前はなんでも良いのですが、今回は分かりやすくMyShader.tsxとしました。
本当はフォルダを作るべきなんですが、目的はreact-vfxを試す事なので、src直下に作りました。

import React from "react";
import * as VFX from "react-vfx";
import styled from "styled-components";

const Content = styled.div`
  width: 100vw;
  height: 100vh;
`;

const metal = `
uniform vec2 resolution;
uniform float time;
void main()
{
    vec2 coord = gl_FragCoord.xy / resolution.xy;
    vec2 st = coord;
    vec3 line = vec3(0.0);

    coord *= 4.;

    float len;

    for (int i = 0; i < 15; i++) {
        len = length(vec2(coord.x, coord.y));
        coord.x += cos(coord.y + sin(len)) + cos(time * .07) * 0.2;
        coord.y += sin(coord.x + cos(len)) + sin(time * 0.1);
    }

    len *= cos(len * 0.4);

    len -= 10.;

    for (float i = 0.0; i < 5.0; i++) {
        len += 0.11 / abs(mod(st.x, 1.09 * i) * 200.) * 1.;
    }

    vec3 color = vec3(cos(len + 0.2) * 1.15, cos(len + 0.1), cos(len - 0.05));

    gl_FragColor = vec4(color, 1.0);
}
`;

const MyShader: React.FC = () => {
  return (
    <>
      <VFX.VFXProvider>
        <VFX.VFXSpan shader={metal}>
          <Content></Content>
        </VFX.VFXSpan>
      </VFX.VFXProvider>
    </>
  );
}

export default MyShader;

コード自体は非常にシンプルで、react-vfxをインポート
import * as VFX from "react-vfx";
FragmentShaderをリテラル内に記述して、metalという変数を定義
インポートしたvfxのコンポーネントを使って、metal(FragmentShader)をロード

Contentは表示するためだけのものなので、普通にCSSを書いて、インポートする方がstyled-componentsを使うより簡単かつ楽だと思います。

あとはこれをApp.tsxもしくはApp.jsにインポートして、を記述したのちに、アプリを起動すれば、FragmentShaderで記述されたアニメーションが表示されます。

ちなみに、metalの部分にShaderToyのコードを移植して、変数名等を修正すれば、簡単に色々なモノを試せます。ただし、公開する場合はライセンスに気をつける必要があります。

react-vfxサンプルページ

react-vfx自体もエフェクトを持っているので、Shaderを書かなくても面白いエフェクトをページに簡単に加える事ができます。
https://amagi.dev/react-vfx/

[text]
Screen Shot 2020-07-30 at 2.06.53 AM.png

repo:https://github.com/koji/typescript/tree/master/shadereact
レポジトリを試す場合は下記を実行すれば、動きます。

$ cd shaderreact
$ yarn
$ yarn start

Dev.toに投稿したポストを日本語にして投稿してみました。
元の記事
https://dev.to/kojikanao/use-fragmentshader-with-reactjs-easily-react-vfx-52lm

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

JavaScript(jQuery)、preventDefault()で既定の動作をキャンセルしてみた

preventDefault()で既定の動作をキャンセルしてみた

やりたいこと
画像をリンク化することで画像の元のサイズを取得できるようにする。
リンク先に飛ばずにモーダルウィンドウ表示にする。

下記の表示でリンク化された画像でリンク先に飛ばなくなる。

  $('img').on('click', function (e) {
    e.preventDefault();
  });

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

【js】更新1週間以内の記事にNEWを付ける

概要

更新日から1週間以内の記事にNEWアイコンを付けたときのメモです。

実装

今回はMTで記事一覧ページを作成して、jsで判定するようにしました。

※HTMLに出てくる<MT:~>はMTタグです。
 今回の実装とは特に関係ありまません。

HTML

HTMLは下記とします。
更新日時の横に「NEW」を表示するようにしたいと思います。

index.html
<div id="article_area">
  <ul class="article_list">
    <MT:PageContents count="10">
      <MT:Entries lastn="0">
        <li>
          <time class="created"><MT:EntryDate format="%Y.%m.%d"></time>
          <span class="category"><MT:EntryTitle></span>
          <!-- ここにNEWアイコンのHTMLを入れる -->
          <a href="<MT:Entrylink>" target="_blank"><MT:EntryTitle></a>
        </li>
      </MT:Entries>
    </MT:PageContents>
  </ul>
</div>

 
アイコン用のHTMLは以下です。

<span class="icon_new">NEW</span>

Javascript

jsで更新日時を判定してアイコン用のタグを挿入します。
ざっと以下のような流れで実装します。

new_icon.js
// 記事一覧のliタグを全て取得
var list = document.querySelectorAll('#article_area li');

// liをループで処理
list.forEach(element => {
    // <span class="category"></span>の下にアイコンを入れたいので親要素として取得
    var parent = element.querySelector('span.category');

    // 更新日時(2020.7.1)を取得して、.で年月日に分ける
    var time = element.querySelector('time.created').innerText.split('.');

    // 公開日の日付オブジェクト生成
    var created = new Date();
    created.setFullYear(time[0]);
    created.setMonth(time[1] - 1); // setMonth()は0~11なので、1引く
    created.setDate(time[2]);
    created.setHours(0, 0, 0, 0); // 時間を00:00:00に指定

    // 今日の日時と1週間後の日時を生成
    var today = new Date(new Date().setHours(0, 0, 0, 0));
    created.setDate(created.getDate() + 7);

    // 公開から1週間以内ならNEWアイコンを付与
    if (created >= today) {
        // NEWアイコン用のHTMLを生成
        var icon = document.createElement('span');
        icon.className = 'icon_new';
        icon.innerText = 'NEW';
        parent.after(icon); // 親要素の後ろにNEWアイコンを挿入
    }
});

NEWアイコンを付けるため、タイトル一覧のliタグを取得して1つずつ日時を判定しています。
setMonth()は0~11の値(0が1月)なので、HTMLから取得した月から-1しています。

ソースコード 

解説無しのソースは以下になります。

new_icon.js
var list = document.querySelectorAll('#article_area li');

list.forEach(element => {
    var parent = element.querySelector('span.category');
    var time = element.querySelector('time.created').innerText.split('.');

    // 公開日の日付オブジェクト生成
    var created = new Date();
    created.setFullYear(time[0]);
    created.setMonth(time[1] - 1);
    created.setDate(time[2]);
    created.setHours(0, 0, 0, 0);

    // 今日の日時と1週間後の日時を生成
    var today = new Date(new Date().setHours(0, 0, 0, 0));
    created.setDate(created.getDate() + 7);

    // 公開から1週間以内ならNEWアイコンを付与
    if (created >= today) {
        var icon = document.createElement('span');
        icon.className = 'icon_new';
        icon.innerText = 'NEW';
        parent.after(icon);
    }
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptで、配列・連想配列から要素を削除する時に使える文法!

はじめに

こんにちは!プログラミング初心者なのですが、今日は、初めてQiitaに記事を投稿します。
現在、ReactでToDoListアプリケーションの作成に挑戦しているのですが、今日はその途中で、配列の要素の削除に関してプチ「アハ体験」をしたので、備忘録も兼ねて投稿します!

一般的な配列から要素を削除する方法

JavaScriptの入門書等によく書いてある、要素の削除方法は次の2つ。

  1. pop()
    配列の末尾の要素を取得し、削除する。

  2. shift()
    配列の先頭の要素を取得し、削除する。

  1. pop()
  2. sample.js
    let arry = ["a", "b", "c", "d", "e"];
    let poppedArry = arry.pop();
    
    console.log(arry); // ["a", "b", "c", "d"]
    console.log(poppedArry); // e
    

    上記のように、配列の末尾にある要素、今回の例では「e」を取得してきて、それを変数poppedArryに格納し、元の配列arryからは、「e」が削除されています。

  3. shift()
  4. sample.js
    let arry2 = ["", "", "",  "", ""];
    let shiftedArry2 = arry2.shift();
    
    console.log(arry2); // ["い", "う", "え", "お"]
    console.log(shiftedArry2); // あ
    

    上記のように、配列の先頭にある要素、今回の例では「あ」を取得してきて、それを変数shiftedArry2に格納し、元の配列arry2からは、「あ」が削除されています。

    本来は削除のための文法ではないけど削除できる文法その1!

    sample.js
    let arry3 = [1, 2, 3, 4, 5];
    let splicedArry3 = arry3.splice(1,3);
    
    console.log(arry3); // [1, 5]
    console.log(splicedArry3); // [2, 3, 4]
    

    上記の例では、spliceメソッドを使っています。このspliceメソッドとは、
    splice(開始インデックス番号, 開始からn番目, 置き換え1, ...置き換えn);
    のように、要素を置き換える働きを主にします。
    しかし、上記の例にあるように、開始インデックス番号と開始からn番目の2つのみを指定し、置き換える要素を指定しなければ、それらの指定された区間(開始~n番)の要素が削除されるのです!

    本来は削除のための文法ではないけど削除できる文法その2!

    sample.js
    let arry4 = [
    { id: 1, title: "絵本"},
    { id: 2, title: "小説"},
    { id: 3, title: "参考書" }
    ]
    let filteredArry4 = arry4.filter(arry4 =>
        arry4.id !== 2);
    
    console.log(filteredArry4); 
    /* 
    {id: 1, title: "絵本"}
    {id: 3, title: "参考書"}
    */
    console.log(arry4); 
    /* 
    {id: 1, title: "絵本"}
    {id: 2, title: "小説"}
    {id: 3, title: "参考書"}*/
    

    上記の例では、filterメソッドを使っています。このfilterメソッドとは、
    filter(function(value, index, array){
    return 条件
    });
    のように、条件に合った要素で新たに配列を形成する働きを主にします。
    しかし、上記の例にあるように、「連想配列のidが2ではない」を条件として、その条件に
    合う要素を、変数filteredArry4に条件に合う要素のみを集めた新しい配列として格納します。元の配列arry4を見てみると、こちらでは、削除は実行されず、元のままです。

    まとめ

    JavaScriptで、配列・連想配列から要素を削除する時に使える文法には、

  • pop()

  • shift()

  • spliceメソッド

  • filterメソッド

の4つがあります!

個人開発などにおいて、配列や連想配列から要素を削除する機能を付けたいということが多々あると思います。そんな時に、通常のpop()やshift()だけでなく、spliceメソッドやfilterメソッドを選択肢に入れてみてください!
拙い投稿ですが、最後まで読んでくださり、ありがとうございます!

参考文献

山田祥寛著 『改訂新版 JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで』(2018)
※第1刷は、2016年。

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

VercelにデプロイしたNextアプリからSendGridを使ってメールを送信する

はじめに

JavaScript界隈ではすっかり地位を確立したJAMSTACK。

僕も大好きです。

最近の休日は、ほぼ全てJAMSTACK開発に費やしています。
静的ホスティングサービスはコストパフォーマンスに優れたものも多く、ブログや簡単なショッピングサイトなどをJAMSTACKでサクッと作ってしまうことで得られるメリットも多いと思います。

ブログやショッピングサイトにはメールが必須ですよね。

とはいえ、まだまだ参考文献が少ないのが現状です。。
今回は、NextプロジェクトをVercelにデプロイした際に、どのようにSendGridと連携してメールを送信するかを書きたいと思います。

前提条件

既に構築済みのNextプロジェクトに組み込むことを想定しているので、以下の環境構築は既に完了済みと想定して話を進めていきます。

  • SendGridのアカウント作成(APiキーの作成も完了済み)
  • Next.jsの基本的な構築

Vercelへのデプロイは各自に任せます。
メールの送信テストは npm run dev でも検証可能です。

SendGridモジュールのインストール

プロジェクト配下で以下のコマンドを実行して必要なモジュールをインストールします。

$ npm i @sendgrid/mail

サーバーレス関数の作成

静的ホスティングサービスってPOST出来ないんでしょ?

という意見を耳にすることがありますが、可能です。

Vercelでは任意の関数をサーバレス機能として展開することができ、バックエンドの言語のHTTPリクエストを取得し、応答を提供することができます。

サーバーレス関数を使うことで、ユーザー認証、フォーム送信、データベースクエリ、カスタムSlackコマンドなどを処理することが可能です。

まず /pages/api 配下に send.js というファイルを作成します。

send.js
const sgMail = require('@sendgrid/mail')

export default async function(req, res) {
  sgMail.setApiKey('SendGridで作成したAPIキー')

  const { email, message } = req.body

  const content = {
    to: email,
    from: '任意のメールアドレス(実現するもの)',
    subject: 'メールのタイトルです',
    text: message,
    html: `<p>${message}</p>`
  }

  try {
    await sgMail.send(content)
    res.status(200).send('Message sent successfully.')
  } catch (error) {
    console.log('ERROR', error)
    res.status(400).send('Message not sent.')
  }
}

これでメールの送信処理の作成が完了しました。

メールを送信する

あとは任意のファイルの中で作成したサーバーレス関数にPOSTするだけでメールが送信できます。
必要に応じて async/await などの非同期処理を行ってください。

index.js
import fetch from 'node-fetch';

fetch('/api/send', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        email: 'test@exsample.com'
        message: 'Test mail.'
      })
    })

最後に

まだまだ開発が活発に行われており、変化に柔軟に対応していかなければならないNext.jsですが、本当に面白い技術なので、皆さんも是非触ってみてください。

この記事が、不安や懸念を振り払うお手伝いになれたら幸いです。

参考文献

Create and Deploy a SendGrid-Powered Next.js and Node.js Contact Form with ZEIT Now

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

Javascript知らない男のGAS 1

経緯

LINEの機能でトークに投げた内容をLINE keepに保存してくれる機能があります。
これのGoogle Keep版を作りたかった学生の話です。
結果として、Google KeepはGASに対応しておらず、公式APIがないので
Google ドキュメントに保存することになりました。
文字と画像の保存に対応しています。
(動画も画像同様対応可能ですが書いてません。需要があれば解説します。)
コードだけ参照したい方は....
https://github.com/hinawakasonoko/desktop-tutorial/blob/master/Code.gs
コード解説はその2にまとめたので記事下のリンクをどうぞ。

LINE側の準備

まずはLINEのAPIを使わないとどうしようもないので、LINE Business IDで公式アカウントを作成します。
https://account.line.biz/signup
作成は流れで入力なりボタン押すなりするだけなので割愛。

LINE Developersのサイトで
- messaging APIを有効にする
- Webhook送信をオンにする
- 自動応答メッセージをオフにする
- アクセストークン(ロングターム)を発行する
これらを行えばOKです。取り扱いmessaging APIのタブ探せば間違いないです
アクセストークンはGAS内に入れるのでメモをお忘れなく。

Google側の準備

https://drive.google.com/drive/u/0/my-drive
一方Google側ではDriveにアクセスし、左上の新規ファイルの作成ボタンを押し、
その他にある、アプリを追加を選びます。
Google Apps Scriptを検索してインストールし、流れのままセットアップすると、先ほどのその他の中にGoogle Apps Scriptが追加されます。
同時に、メモを保存するためのドキュメントも作成しておいてください。
その際、ドキュメントを開いているときのURLの
docs.google.com/document/d/文字列/edit の文字列部分もメモしておいてください。GASで使います。

その2ではコードと、LINEで使うための手順を解説します。(qiitaって連続投稿できないんですね....)
その2 https://qiita.com/nuyuyuri/items/3e1e76980c1d8f5c6812

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

three.jsでカードゲーム盤面構築

概要

ドグマブレード作ったはいいけど、如何にも素人丸出しの貧相なビジュアルやなぁ…ということで
three.jsで奥行ある盤面描写に挑戦していきます。
バージョンは執筆時点で最新の[r119]を使用。
typescriptで書いてますがjsでもほぼ変わりません。

下準備

①レンダリングを乗せるcanvasを用意。

index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <meta name="3dboard" content="width=device-width, initial-scale=1"/>
  <script src="main.js"></script>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas"></canvas>
</body>
</html>

npm install threeでインストール。

③WebGL処理を書くmain.tsを用意、threejsをインポート。

main.ts
import * as THREE from 'three';

window.onload = function() {
  // ここにWebGL処理を書く
}

基本となる5つのクラス

Scene Light Scene Camera Mesh Rendererについて

Scene

3D空間を定義するクラス。
Sceneインスタンスにカメラ、ライト、オブジェクトをaddしていきます。

scene.ts
    // シーンを作成
    const scene = new THREE.Scene();
    scene.background = new THREE.Color("#BBFFFF");

Camera

3D空間を撮影する視点を定義するクラス。
遠近感が適用されるTHREE.PerspectiveCamera を使用。

camera.ts
    // カメラを作成
    // (角度,アスペクト比)
    const camera = new THREE.PerspectiveCamera(70, width / height);
    camera.position.set( 0, 150, 180 ); // カメラを置く座標
    camera.lookAt(new THREE.Vector3(0, 0, 0));  // 原点座標を向かせる
    scene.add(camera); // sceneに加える

Light

光源を定義するクラス。

light.ts
    // 光源を作成
    // (光色,光量,照射距離,照射角,ぼかし,減衰率)
    const light = new THREE.SpotLight(0xFFFFFF, 1, 1000, Math.PI / 4, 10, 0.1);
    light.castShadow = true; // 光源側で影を有効に
    light.position.set( 0, 150, 150 ); // 光源を置く座標
    light.lookAt(0,0,0);  // 原点座標を向かせる
    light.shadow.mapSize.width = 2048;  // 影の解像度、デフォルトの512では粗い
    light.shadow.mapSize.height = 2048; // 〃
    light.shadow.radius = 10; // 影の拡張半径
    scene.add(light); // sceneに加える

Mesh

3D空間に浮かべるオブジェクトを定義するクラス。
形状THREE.Geometry、質感THREE.Materialを設定する必要があります。

mesh.ts
    // キューブを作成
    const cubeGeometry = new THREE.BoxGeometry(10, 10, 10); // 形状
    const cubeMaterial = new THREE.MeshBasicMaterial({color:"red"}); // 質感
    const cube = new THREE.Mesh(cubeGeometry, cubeMaterial); // 形状、質感設定
    cube.position.set( 0, 20, 0 ); // キューブを置く座標
    scene.add(cube); // sceneに加える

Renderer

カメラ視点から撮影した3D空間をcanvasに描写するクラス。

renderer.ts
    // サイズを指定
    const width = window.innerWidth;
    const height = window.innerHeight;

    // レンダラーを作成
    const mainCanv = <HTMLCanvasElement>document.querySelector('#myCanvas');
    const renderer = new THREE.WebGLRenderer({
      canvas: mainCanv,
      antialias: true //描画を滑らかにするやつ
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(width, height);
    // レンダラー側で影を有効に
    renderer.shadowMap.enabled = true;

    tick();
    // 毎フレーム時に実行されるループイベント
    function tick() {
      // レンダリング
      renderer.render(scene, camera);
      requestAnimationFrame(tick);
      controls.update();
    };

実践

いよいよ盤面を作っていきます。

ボード

カードを置く平面。
THREE.PlaneGeometryのMeshを寝かせるだけ。

genBoardBack.ts
const boardBack =  new THREE.Mesh(                                      
    new THREE.PlaneGeometry(200, 200),
    new THREE.MeshStandardMaterial({ 
    color: "palegreen",
    })
);
boardBack.name = "boardBack";
boardBack.rotation.set(-Math.PI/2, 0, 0); // x軸に対して回転
boardBack.receiveShadow = true;
boardBack.castShadow = true;

次にボードのゾーン枠を描写します。
THREE.Lineでシコシコ書いてますが、画像を用意してBoardBackに貼り付けてもOK。

genZone.ts
const genZone = (mode:"MONSTER"|"OTHER",x:number)=>{
    const points = [
        new THREE.Vector3( -7.5, 0, -10 ),
        new THREE.Vector3( 7.5, 0, -10 ),
        new THREE.Vector3( 7.5, 0, 10 ),
        new THREE.Vector3( -7.5, 0, 10 ),
        new THREE.Vector3( -7.5, 0, -10 )
    ];
    const zoneFrame = new THREE.Line(
        new THREE.BufferGeometry().setFromPoints(points),
        new THREE.LineBasicMaterial({color: "red"})
    );
    zoneFrame.receiveShadow = true;
    const zoneMaterial = new THREE.Mesh(
        new THREE.PlaneBufferGeometry(15, 20),
        new THREE.MeshBasicMaterial({color:"pink"})
    );
    zoneMaterial.receiveShadow = true;
    zoneMaterial.rotation.set(-Math.PI/2, 0, 0);

    const result = new THREE.Group()
    if(mode=="MONSTER"){
        const atk = new THREE.Group().add(zoneFrame,zoneMaterial);
        const def = atk.clone();
        def.rotation.set(0, -Math.PI/2, 0);
        result.add(atk,def);  
    }else if(mode=="OTHER"){
        result.add(zoneFrame,zoneMaterial);  
    };
    result.position.set(x, 0, 0);
    result.receiveShadow = true;
    return result
};
const ZoneGroup = ()=>{
    const ZoneHorizon = (vertical:"front" | "back")=>{
        const zoneHorizonLine = new THREE.Group();
        if(vertical=="back"){
            for (let H = 0; H < 7; H++) {
                zoneHorizonLine.add(
                    genZone("OTHER",-75 + H*25)
                )
            };
            zoneHorizonLine.position.set(0, 0, -12.5)
        }else if(vertical=="front"){
            for (let H = 0; H < 7; H++) {
                if( 1<=H && H<=5){
                    zoneHorizonLine.add(genZone("MONSTER",-75 + H*25))
                }else{
                    zoneHorizonLine.add(
                        genZone("OTHER",-75 + H*25)
                    )
                };
            };
            zoneHorizonLine.position.set(0, 0, 12.5)
        };
        zoneHorizonLine.receiveShadow = true;
        return zoneHorizonLine;
    };
    const playerGroup = new THREE.Group().add(ZoneHorizon("front"),ZoneHorizon("back"));
    playerGroup.position.set(0, 0, -37.5);
    playerGroup.receiveShadow = true;
    const enemyGroup = new THREE.Group().add(ZoneHorizon("front"),ZoneHorizon("back"));
    enemyGroup.position.set(0, 0, 37.5);
    enemyGroup.rotation.set(0, Math.PI, 0);
    enemyGroup.receiveShadow = true;

    const result = new THREE.Group().add(playerGroup,enemyGroup);
    result.position.set(0, 0.1, 0);
    result.receiveShadow = true;
    return result;
};

const boardGroup : THREE.Group = new THREE.Group().add(boardBack,ZoneGroup())
boardGroup.receiveShadow = true;
scene.add(boardGroup);

カード

カードも平面だからTHREE.PlaneGeometryで…と行きたいところですが

  • 表裏に別のテクスチャを貼ることができない
  • ボードに影を落とせない

のでTHREE.BoxGeometryで極薄直方体として描写します。

genCard.ts
    const genCardImgObj = (openImg:string)=>{
        const loader = new THREE.TextureLoader();
        //6面それぞれにmaterialを設定。表裏以外は黒
        const materialArray = [
            new THREE.MeshBasicMaterial({color:"black"}),
            new THREE.MeshBasicMaterial({color:"black"}),
            new THREE.MeshBasicMaterial({color:"black"}),
            new THREE.MeshBasicMaterial({color:"black"}),
            new THREE.MeshBasicMaterial({map: loader.load(openImg)}),
            new THREE.MeshBasicMaterial({map: loader.load("cardback.jpg" )}),
        ];
        const cardGeometry = new THREE.BoxGeometry(13.5, 18, 0.01); // 厚さ0.01
        const cardMesh = new THREE.Mesh(cardGeometry,materialArray);

        cardMesh.rotation.x = -Math.PI/2;
        cardMesh.castShadow = true;
        cardMesh.receiveShadow = true;
        return cardMesh;
    };

    // 1枚配置
    const AirmanA = genCardImgObj("Airman.jpg");
    AirmanA.position.set(25,1,25);
    scene.add(AirmanA);
    // 40枚配置してデッキに
    for (let i = 0; i < 40; i++) {
        const card = AirmanA.clone();
        card.position.set(75, 1+0.2*(i), 50);
        card.rotation.y = Math.PI;
        scene.add(card);
    };

カメラコントロール

盤面はできましたが、折角作ったオブジェクトを定点カメラで眺めるだけでは寂しいので
ドラッグで視点をグリグリ動かして色んな角度から鑑賞できるようにします。
OrbitControlsをインポートして呼び出すだけ!

OrbitControls.ts
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
const controls = new OrbitControls( camera, renderer.domElement );
controls.enableDamping = true; // 動きを滑らかにするやつ

完成!

3dss1.png
3dss2.png

以上

最近触り始めたところなので有識者からのツッコミ、及び便利なライブラリ・ヘルパー情報お待ちしております。
次回はマウスイベントとアニメーションについて書きます。

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

[AWS] ReactアプリでCognitoユーザープール認証を行ってみる

Cognitoの基本

[AWS] Cognitoの基本まとめ」をご覧ください。

前提

今回はReactを使用するので、事前に

  • Node.js
  • npm

をインストールしておいてください。
あと、AWSアカウントの用意もお忘れなく。

準備

ユーザープールの作成

まず、マネジメントコンソールにログインして、サービス「Cognito」の画面を表示します。
まだユーザープール、IDプールともに未作成のはずなので、下記のような画面になると思います。

cognito1.png

ここで「ユーザープールの管理」ボタンを押します。

cognito2.png

そして「ユーザープールを作成する」ボタンを押します。
すると、プール名の入力を促されるので、適当なプール名を入力してください。

cognito3.png

今回は、ここで「デフォルトを確認する」ボタンを押します。

cognito4.png

一応内容を確認して、一番下にある「プールの作成」ボタンを押します。
すぐに作成完了の画面が出るので、プールIDを控えておきましょう(控え忘れてもあとで確認はできます)。

cognito5.png

アプリクライアントの作成

今回は、外部プロバイダではなくユーザープールで認証を行うので、作成したユーザープールにアプリクライアントを作成します。
画面右側にある「アプリクライアント」を選択してください。

cognito6.png

ここで「アプリクライアントの追加」リンクをクリックします。
アプリクライアント名に適当な名称を入力します。
あと、今回は簡単なサンプルにしたいので「クライアントシークレットを生成」のチェックだけ外してください。
それ以外はデフォルトのままとしておきます。

cognito7.png

「アプリクライアントの作成」ボタンを押します。
すると、画面が切り替わるので「アプリクライアントID」を控えておきましょう(これも後で確認することはできます)。

cognito8.png

IDプールの作成

ユーザープールが作成できたので、今度は、アカウントを管理するIDプールを作成します。
画面上部にある「フェデレーテッドアイデンティティ」のリンクをクリックします。

cognito9.png

まずはIDプール名に適当な名称を指定しましょう。

cognito10.png

そして、画面の下の方に移動すると「認証プロバイダー」というリンクをがあるので、これをクリックして入力領域を展開します。
ここで、ここまでに控えておいた、ユーザープールID、アプリクライアントIDを入力します。

cognito11.png

そして「プールの作成」ボタンを押します。
しばらくすると、IAMロールを作成する画面が出てきます。

cognito12.png

特に何も変更せず、そのまま「許可」ボタンを押してください。

クライアントアプリの作成

Reactアプリの作成

まず最初に、create-react-appをインストールします。

$ npm install -g create-react-app

続いて、下記コマンドでアプリケーションのプロジェクトを作成します。

$ create-react-app cognito-test

完了したら、一応正しくセットアップできているか確認してみます。

$ cd cognito-test
$ npm start

http://localhost:3000/にアクセスして、下記のような画面が表示されればOKです。
「Ctrl+C」で一度終了させてください。

react1.png

Apmplifyのインストール

今回はクライアントにJavaScriptを使うため、Amplifyを使用したいと思います。
プロジェクトのディレクトリ配下で、以下のコマンドを実行してください。

$ npm install aws-amplify aws-amplify-react @aws-amplify/ui-react

Cognitoのコードを書く

package.json

まず、上でインストールしたライブラリが追加されているか確認しましょう。
dependenciesに、以下の2行を追記してください。

    "aws-amplify": "^3.0.22",
    "aws-amplify-react": "^4.1.21",
    "@aws-amplify/ui-react": "^0.2.13"

src/index.js

続いて、index.jsを以下のように修正します。

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import Amplify from 'aws-amplify';

Amplify.configure({
  Auth: {
      identityPoolId: 'IDプールのID',
      region: 'ap-northeast-1',
      userPoolId: 'ユーザープールID',
      userPoolWebClientId: 'アプリクライアントID'
  }
});

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

とします。
IDプールのIDは、IDプールの画面で「IDプールの編集」から確認できます。

cognito13.png

src/App.js

続いて、App.jsも、下記のように書き換えてしまってください。

import React from 'react';
import { withAuthenticator } from '@aws-amplify/ui-react';

function App() {
  return (
    <div className="App">
    </div>
  );
}

export default withAuthenticator(App);

以上。
これだけです、たったこれだけ。

動作確認

いざ起動

まずはアプリケーションを起動します。

$ npm start

そしてhttp://localhost:3000/にアクセスすると、画面が先ほどとは違い、以下のような画面が出てきます。
Cognitoのデフォルトの認証画面です。

React_App.png

ユーザ作成

この時点では、まだユーザを1つも登録していないので「Create account」のリンクから、ユーザを登録してみましょう。
この時、標準の設定で、メールアドレス認証を有効にしているので、メールアドレスはメールを受信できる有効なものを指定してください。

userreg.png

「CREATE ACCOUNT」ボタンを押すと、確認画面が表示されます。

なお、この時点でマネジメントコンソール画面でユーザープールの状態をみてみると、

cognito16.png

と、ユーザが作成され、Eメール確認済みが「false」になっていることが確認できます。

cognito17.png

では、話を戻して、Confirmation Codeに、受信したメールに記載されているコードを入力して「CONFIRM」を押してみましょう。
すると、画面が真っ白くなりますが(認証後に表示するページを指定していないため)、マネジメントコンソールの画面で確認してみると、

cognito19.png

Eメール確認済みが「true」にかわっているかと思います。

まとめ

認証・認可の機能を追加する、ってなかなか敷居が高いイメージでしたが、さくっとできてしまいました。
次は、FacebookなどからSSOする方法について解説したいと思います。

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

Javascript知らない男のGAS 2

さて書くべきコードは大まかに、GASにLINEからデータが来た時の処理、返信内容、返信をするために必要な認証情報の付与 といったところです。実際にコードを見ていきましょう。割と最低限のコードです。コメント無しをGithubにも載せておきます。
https://github.com/hinawakasonoko/desktop-tutorial/blob/master/Code.gs

コード

//具体的な値は事前に指定しています 説明はコード後に
var CHANNEL_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty("token");
var id = PropertiesService.getScriptProperties().getProperty("doc");
//LINEから来たデータから使うものを取り出す部分 
function doPost(post) {
  var obj = JSON.parse(post.postData.contents);//HTTPSに来たPOSTリクエストのJSONをパース
  var events = obj["events"];JSON内、リクエストボディのeventsを取り出す
      reply_message(events[0]);//ここは1つのメッセージごとに返信を返す仕様に甘えたコードかも?
}

//ラインのサーバから画像データを取得する部分
function getImg(messageId) {   
  var url = "https://api-data.line.me/v2/bot/message/" + messageId + "/content"; 
 //このURLは変数に入れといたほうが今後機能追加するうえで便利
  var options = { 
    "method" : "get",
    "headers" : {
    "Content-Type" : "application/json", 
    'Authorization': "Bearer " + CHANNEL_ACCESS_TOKEN
    }
  }; 
  return UrlFetchApp.fetch(url, options);   
 }

//ドキュメントへの動作と、LINEへの返信内容のコード 
function reply_message(data) 
{
  var return_text;
  if(data.message.type=="text") //文字
  {
    var input_text = data.message.text;
      //指定したIDを参考にドキュメントを開き、新しい行に追記
    var document = DocumentApp.openById(id).appendParagraph(input_text);
    return_text = "追加しました";   
  }
  else if(data.message.type=="image")//画像
  {
    var input_img = getImg(data.message.id);//画像データを得ます
    var document = DocumentApp.openById(id).appendImage(input_img);
    return_text = "追加しました";
  }
  else//スタンプなど
  {
    return_text = "非対応の形式です"
  }

//以下、返信データ用のコード いわばおまじない 
  var postData = {
    "replyToken" : data.replyToken,
    "messages" : [
      {
        "type" : "text",
        "text" : return_text
      }
    ]
  };

  var options = {
    "method" : "post",
    "headers" : {
      "Content-Type" : "application/json",//一応ここで送信データの文字コードも指定できます
      "Authorization" : "Bearer " + CHANNEL_ACCESS_TOKEN
    },
    "payload" : JSON.stringify(postData)
  };
  UrlFetchApp.fetch("https://api.line.me/v2/bot/message/reply", options);//ここにしか使わないのでURL直書き
}

アクセストークンとドキュメントのidはプロパティとして事前に登録しているので、コード内には記載していません。
ファイル>プロジェクトのプロパティ>スクリプトのプロパティ に登録します。
プロパティの名前はコード内で使用するものと一致していればなんでもOK
値にアクセストークンやドキュメントidを入れて、保存します。
コード内に書いた場合と処理速度は同じか、誤差レベルと思われます。

恐らく解説が一番必要なのは画像データを取得する部分かと思います。
文字はdata.message.textとして簡単に取り出せていますが、
それ以外の、画像や動画は同じようにはいかないのです。
画像を送ったとて、画像を送ったという事実はGASに送られてきますが画像データそのものは送られてきていません。
そのため、送られてきたメッセージのIDを用いて、該当画像をLINEのサーバーから直に拾ってきています。
ちなみに、文字をsetText(input_text)で書き込むことはできますが、前のメモを上書きされかねないので、新しい行に書き込んでくれるappendメソッドを使っています。
新しい行に書き込むという性質上か、ドキュメントの内容を取得するコードは省略できていますね

アプリとして公開

さて、コードがOKなら、上の公開からウェブアプリケーションとして導入を押します
公開範囲を匿名含む全員に指定し、公開します。
初めはGoogleのセキュリティ確認が入ると思います。
公開するとURLが発行されるので、これをLINE developのWebhook URLに貼り付けます。
これで、作成した公式アカウントに送ったメッセージが自動的にドキュメントに追記されます。完成!

これは、他のサービスのAPIを用いれば他のメッセンジャーにも応用できますし(トークンの使い方など違いがあるので流用はできません)、Evernoteなどは投稿用アドレスにメールを送れば追加してくれるサービスがあります。

セキュリティ面の懸念

さて、こうして作った公式アカウントは他人に公開しない事を強く勧めます。
というのも、他人が入力したデータが簡単に見れるからです。
今回のケースは誰が入力しようが自分のドキュメントにメモされるので公開されることはないと思いますが、問題はEvernoteと組み合わせた場合です。
例えば、ユーザーにEvernoteの投稿用アドレスを登録してもらい、そこへメモを投げるBOTを私が作ったとします。
当然logに流せば投稿用アドレスも私に見えますし、メモの内容も覗けます。
テキストを直接取得するAPIがないのは、APIを使うまでもないからです。
私はWebhookどころかJSONすら知らなかった初学者なので、なにかやり方があるのかなと、BOTを公開していた方に興味本位でデータのセキュリティについて質問したところ、見事に記事全消し&Twitterに鍵をかけるという感じで答えももらえず逃げられましたので.....
その方はデバッグ用にスプレッドシートにログを残すコードを入れていましたので、気付いていながら運用していた可能性が高いです。

感想

このコードの課題は、画像サイズがそのままなのでドキュメント上で大きすぎることです。
とはいえリサイズする関数を書けば解決します。どのサイズにリサイズするか画像投稿時に一緒に指定することも可能ですね。
まだ試せていませんが、最終的には非公式のGoogle keep APIを活用してKeepにメモを残せるようにするつもりです。
普段コードを書くことがないので新鮮でしたが、英語が分かれば分かるもんですね
LINEに関しては日本語での説明が豊富ですし、初学者にとって易しかったです。

参考資料

LINE Messaging APIリファレンス LINE Developersより
GASに来るデータの中身
https://developers.line.biz/ja/reference/messaging-api/#message-event
画像データの取得
https://developers.line.biz/ja/reference/messaging-api/#get-content

Google GAS Developersより
ドキュメントに対する処理記述
https://developers.google.com/apps-script/reference/document/body#methods

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

ウソ穴 Ver 5 Type B / 時雨堂 Sora

はじめに

個人開発ウソ穴の作り方を紹介します。

ウソ穴とは

ウソ穴は、ライブ映像 or 動画とARを組み合わせて、壁に穴が空いた錯覚を作り出します。Webサイトなので、ユーザーはアプリのインストール無くウソ穴を使用できます。

ウソ穴 Ver 5 Type B / 時雨堂 Sora

  • 時雨堂さんのSoraをつかったウソ穴の作り方を紹介
  • 時雨堂さんのSoraは遅延がほとんどないストリーミング
  • 時雨堂さんのSoraのおかげでウソ穴の"穴感"がよりリアルになる

時雨堂さんの技術力すげー

デモ映像

向かって左がウソ穴、右がホントの穴です。

参考

構成図

  • ストリーミング機能に、時雨堂さんのSoraを使わせて頂く
  • 2つのWebサイトがあります
    • 映像配信用のWebサイト upstream.html
    • ウソ穴用のWebサイト usoanaVer5TypeB.html

image.png

ソース

ソースはgithubで公開しています。

usoana-public/usoana5TypeB-shiguredo-sora

ウソ穴の構築方法

ここから、ウソ穴 Ver 5 Type Bの構築方法を紹介します。

構築概要

  • 1. ラズパイのセットアップ
  • 2. Nodejsで、HTTPS対応のWebサーバーを構築
  • 3. 時雨堂 sora を実装
    • upstream.html完成
  • 4. A-Frameでウソ穴用のWebサイトを実装
    • usoanaVer5TypeB.html完成

1. Nodejsで、HTTPS対応のWebサーバーを構築

ラズパイにOSをインストールして、初期セットアップをします。

2. Nodejsで、HTTPS対応のWebサーバーを構築

HTTPS対応のWebサイトを構築します。
具体的には、Nodejsのインストールと、オレオレ証明書を作ってHTTPS対応のWebサイトを構築します。

3. 時雨堂 Sora を実装

Sora JavaScript SDKを使ってみたを参考に、Soraを実装します。ここで作成したupstream.htmlは、ウソ穴の映像配信用のWebサイトとして利用します。

4. A-Frameでウソ穴用のWebサイトを実装

githubにあるウソ穴のソースファイル を作成したHTTPS対応のWebサーバーに配置します。

配置したusoanaVer5TypeB.htmlの以下を時雨堂さんのサイト( https://sora-labo.shiguredo.jp/ )の情報に更新します。

const channelId = 'xxxxxxxxx@sora-labo';
const signalingUrl = 'wss://xxxxxxxxxxxxxxxx';
const metadata = {"signaling_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"};

image.png

Webサイト(ウソ穴 Ver 5 Type B / 時雨堂 Sora)の準備

githubにあるウソ穴のソースファイルは、画像データが不足しているので、追加します。

parts\img\tex001.jpg ※穴の側面を表現する画像ファイル
parts\img\invisible.png ※透過率100%の完全透明な画像ファイル

tex001.jpgのダウンロード
invisible.pngのダウンロード
invisible.png

これで、ウソ穴 Ver 5 Type B / 時雨堂 Soraの準備ができました。
ファイル構造はこのようになります。

$ tree
.
├── parts
│   ├── img
│   │   ├── (dummy)invisible.png.txt
│   │   ├── (dummy)tex001.jpg.txt
│   │   ├── invisible.png
│   │   └── tex001.jpg
│   └── js
│       └── sora.min.js
├── upstream.html
└── usoanaVer5TypeB.html

ウソ穴 Ver 5 Type B / 時雨堂 Sora を動かす

Webサービスを開始する

以下コマンドでWebサービスを開始します。ポート3000はhttp通信(非暗号化通信)で、ポート3001はhttps通信(暗号化通信)です。ウソ穴は、https通信が必須なので3001番ポートを使います。

$ pwd
/home/pi/nodejs/01
$ node app.js
        サーバがポート3000で起動しました。モード:development
        サーバがポート3001で起動しました。モード:development

図4.png

ブラウザで受信用のWebサイトを開く

ブラウザで以下のURLを開き、ストリーミングを開始します。

https://{ラズパイのIPアドレス}:3001/.../upstream.html

図2.png

ブラウザでウソ穴用のWebサイトを開く

ブラウザで以下のURLを開くと、ウソ穴が動きます。(Win10のFirefox,Chrome と iPhone iOS13.x の Safari で動作実績あり)

https://{ラズパイのIPアドレス}:3001/.../usoanaVer5TypeB.html

図3.png

line.png

今回は、これでおわり

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

JavaScriptのfetch()にcgiで返事するときは改行コードを取ろう

概要

  • Javascriptでfetch()からサーバに問い合わせした時なにかの文字列を返えすとします。その文字列を読み込んだのに条件判定に失敗しました。
  • わかってしまえば簡単なこと。文字列に改行コードが入っていたのでした。

コード

cgiで何かの処理とか条件判定した時に文字列をvJavaScript/fetch()に返事をするとします。JavaScriptでは例えばこんな問い合わせをサーバにするとします。

...
fetch('/cgi/hoge.cgi', {
  headers : {"Content-Type": "text/html"},
  body    : hoge
}).then(response => {
  sonsole.log(response.text);
  if (response.text === "FILE_EXIST" ){
    ...
    //ファイルがあった場合の処理
    ...
  }
}).catch(error => {...})

サーバ側のhoge.cgiでは問い合わせに結果を返す時に、例えばファイルが存在するのかを文字列で返したとします。その例です。

$ cat hoge.cgi
# なんかの条件判定を設定。
...
# 例えばファイルが存在するか?
[ -f $hoge ] && result="FILE_EXIST" || result="NONE"
#結果を返す
echo "Content-type: text/html"
echo ""
echo -n $result
exit 0

ポイントはecho -nと改行コードをつけないようにすることです。最初echo $hogeで返してうまく行かなたったので一晩悩みました。console.log(response.text)ではリターンが表示されないので気が付きませんでした。XRLのレスポンス(応答ペイロード)を見て気が付きました。

どなたかのお役に立てれば嬉しいです。

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

Kinx アルゴリズム - ハノイの塔

Kinx アルゴリズム - ハノイの塔

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。「プログラム=アルゴリズム+データ構造」。アルゴリズムの実装例をご紹介。

元ネタは「C言語による(30年経っても)最新アルゴリズム事典」。今回はライフゲームです。

最新アルゴリズム事典にはこういうのも結構載ってる。パズル的な。

Kinx では最初のころにこのサンプルコードを書いて、再帰関数の確認に使った。良いテストになった。

ハノイの塔

Wikipedia より

以下のルールに従ってすべての円盤を右端の杭に移動させられれば完成。

  • 3本の杭と、中央に穴の開いた大きさの異なる複数の円盤から構成される。
  • 最初はすべての円盤が左端の杭に小さいものが上になるように順に積み重ねられている。
  • 円盤を一回に一枚ずつどれかの杭に移動させることができるが、小さな円盤の上に大きな円盤を乗せることはできない。

ソースコード

function movedisk(n, a, b) {
    if (n > 1) movedisk(n - 1, a, 6 - a - b);
    System.println("円盤 %d を %d から %d に移す" % n % a % b);
    if (n > 1) movedisk(n - 1, 6 - a - b, b);
}

const N = 4;
System.println("円盤 %d 枚を柱 1 から柱 2 に移す方法は"
        "次の %u 手です." % N % ((1 << N) - 1));
movedisk(N, 1, 2);

結果

円盤 4 枚を柱 1 から柱 2 に移す方法は次の 15 手です.
円盤 1 を 1 から 3 に移す
円盤 2 を 1 から 2 に移す
円盤 1 を 3 から 2 に移す
円盤 3 を 1 から 3 に移す
円盤 1 を 2 から 1 に移す
円盤 2 を 2 から 3 に移す
円盤 1 を 1 から 3 に移す
円盤 4 を 1 から 2 に移す
円盤 1 を 3 から 2 に移す
円盤 2 を 3 から 1 に移す
円盤 1 を 2 から 1 に移す
円盤 3 を 3 から 2 に移す
円盤 1 を 1 から 3 に移す
円盤 2 を 1 から 2 に移す
円盤 1 を 3 から 2 に移す

おわりに

こちらも C 言語版ほぼそのままです。GitHub は英語基準なのでこれまで文言を英語に直していましたが、一応日本語でも動くので、今回の上記のソースコードは日本語のままにしました。ただし GitHub に例としてコミットしているものは英語にしてあります。

ではまた、次回。

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

【React Hooks】useState()でcannot assign to read only propertyが出た時の対処法

はじめに

useState()でオブジェクトを管理していましたが、タイトルのようなエラーが発生したので、その解決法を記してあります。

エラー時のコード

test.js
import React, { useState, useEffect } from "react";
import { db } from "../firebase.js";

const Test = () => {
  const [imgStyle, setImgStyle] = useState({backgroundImage: ""});

  useEffect(() => {

   db.collection("test").get().then((querySnapshot) => {
      querySnapshot.forEach((doc) => {
        const docVal = doc.data();
        imgStyle["backgroundImage"] = `url(${docVal.imgUrl})`;
        setImgStyle(Object.assign({}, imgStyle));
      });
    }).catch(function(error) {
      console.log("Error getting document:", error);
    });

  }, []);

  return (
    <div>
      <div className="reportBefore" style={imgStyle}></div>
    </div>
  );
}

export default Test;

解決後のコード

test.js
import React, { useState, useEffect } from "react";
import { db } from "../firebase.js";

const Test = () => {
  const [imgStyle, setImgStyle] = useState([]);

  useEffect(() => {

   db.collection("test").get().then((querySnapshot) => {
      querySnapshot.forEach((doc) => {
        const docVal = doc.data();
        setImgStyle(oldForm => [...oldForm,
          {backgroundImage: `url(${docVal.imgUrl})`}
        ]);
      });
    }).catch(function(error) {
      console.log("Error getting document:", error);
    });

  }, []);

  return (
    <div>
      <div className="reportBefore" style={imgStyle[0]}></div>
    </div>
  );
}

export default Test;

まとめ

読みのみで変更はできないので、useStateでオブジェクトを扱う際は追加のイメージを忘れずに持っておくことが必要です。

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

fetchでクエリパラメータを渡す方法

ajaxをfetch apiで書き変えた時に、getリクエストにクエリパラメータをつける方法がわからず詰まってしまったのでメモ。

fetchにはgetリクエストにクエリパラメータをつける方法が用意されていないようです。
この点、ajaxメソッドはわかりやすくクエリパラメータを付与できたのはメリットだったと思います。

ただ、以下の方法でクエリパラメータを付与してfetchを書くことができました。
悩んでいる方は是非参考にしてみてください。またもっとい書き方があれば、是非教えてください!

    const params = { // 渡したいパラメータをJSON形式で書く
      a: xxxx,
      b: xxxx,
      c: xxxx,
    };

    const query_params = new URLSearchParams(params); 
    fetch('${リクエストを送りたいURL}?' + query_params)
      .then(response => response.json())
      .then(response => {
          // 実行したい処理を記述
      });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む