20200907のJavaScriptに関する記事は30件です。

Node.jsにおける入力パラメータのチェック(バリデーション)

AWSLambda(Node.js)のREST APIにおいて入力パラメータのチェックがしたい
そうだ、value-schemaを使おう

value-schemaができること

1.必須パラメータの有無、
2.型、
3.想定範囲内に存在するか(e.g.limit < input < limit)、
上記3点における入力パラメータの検証と修正ができる

使い方

大きく2つの書き方がある....と勝手に思ってる
基本のフォーマットは vs.型名( {オプション} )

//====その1. オブジェクトの中身をまとめてチェックする方法====
var vs = require("value-schema");

// 入力パラメータのオブジェクト
let requestbody = {
    testId: "test",
    testNum: 1,
    testlist: [1, 2, 3],
    NoCheckid:"no",
};

// 入力パラメータの満たすべき条件
const schemaObject = {
    testId: vs.string({minLength: 3}), //eventIdが文字列かつ5文字以上
    testNum: vs.number({minValue: 1,}), //testNumが数字かつ1以上
    testlist: vs.array({minLength: 2}), //testlistがarrayかつ2要素以上
                                        //noNoCheckidは条件を書いてないのでチェックされない
};

// 全ての条件を満たさない場合エラーを投げる,満たす場合は変数にいれる
const checkedRequestbody = vs.applySchemaObject(schemaObject,requestbody,(err)=> {
    throw new Error("入力パラメータがおかしい")
});
//====その2. パラメータ単体でチェックする方法====
var vs = require("value-schema");
const flag = true;
const checkedName = vs.boolean().applyTo(flag,(err)=> { //flagがbool値である
    throw new Error("入力flagがおかしい") //条件を見たさない場合err投げる
});

ほぼ全ての型で使える便利なオプション

{strictType: true} : 厳密なタイプチェックができる、デフォルトはオフ
{ifUndefined: somevalue} : パラメータがundefinedの場合に任意の値を入れておく
{ifNull: somevalue} : パラメータがnullの場合に任意の値を入れておく
{ifEmptyString: somevalue} : パラメータが空文字""の場合に任意の値を入れておく

*その他オプションなどは公式を参照

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

AWSLambda(Node.js)の入力パラメータチェック(バリデーション)

AWSLambda(Node.js)のREST APIにおいて入力パラメータのチェックがしたい
そうだ、value-schemaを使おう

value-schemaができること

1.必須パラメータの有無、
2.型、
3.想定範囲内に存在するか(e.g.limit < input < limit)、
上記3点における入力パラメータの検証と修正ができる

使い方

大きく2つの書き方がある....と勝手に思ってる
基本のフォーマットは vs.型名( {オプション} )

//====その1. オブジェクトの中身をまとめてチェックする方法====
var vs = require("value-schema");

// 入力パラメータのオブジェクト
let requestbody = {
    testId: "test",
    testNum: 1,
    testlist: [1, 2, 3],
    NoCheckid:"no",
};

// 入力パラメータの満たすべき条件
const schemaObject = {
    testId: vs.string({minLength: 3}), //eventIdが文字列かつ5文字以上
    testNum: vs.number({minValue: 1,}), //testNumが数字かつ1以上
    testlist: vs.array({minLength: 2}), //testlistがarrayかつ2要素以上
                                        //noNoCheckidは条件を書いてないのでチェックされない
};

// 全ての条件を満たさない場合エラーを投げる,満たす場合は変数にいれる
const checkedRequestbody = vs.applySchemaObject(schemaObject,requestbody,(err)=> {
    throw new Error("入力パラメータがおかしい")
});
//====その2. パラメータ単体でチェックする方法====
var vs = require("value-schema");
const flag = true;
const checkedName = vs.boolean().applyTo(flag,(err)=> { //flagがbool値である
    throw new Error("入力flagがおかしい") //条件を見たさない場合err投げる
});

ほぼ全ての型で使える便利なオプション

{strictType: true} : 厳密なタイプチェックができる、デフォルトはオフ
{ifUndefined: somevalue} : パラメータがundefinedの場合に任意の値を入れておく
{ifNull: somevalue} : パラメータがnullの場合に任意の値を入れておく
{ifEmptyString: somevalue} : パラメータが空文字""の場合に任意の値を入れておく

*その他オプションなどは公式を参照

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

AWSLambda(Node.js)の入力パラメータチェック

AWSLambda(Node.js)のREST APIにおいて入力パラメータのチェックがしたい
そうだ、value-schemaを使おう

value-schemaができること

1.必須パラメータの有無、
2.型、
3.想定範囲内に存在するか(e.g.limit < input < limit)、
上記3点における入力パラメータの検証と修正ができる

使い方

大きく2つの書き方がある....と勝手に思ってる
基本のフォーマットは vs.型名( {オプション} )

//====その1. オブジェクトの中身をまとめてチェックする方法====
var vs = require("value-schema");

// 入力パラメータのオブジェクト
let requestbody = {
    testId: "test",
    testNum: 1,
    testlist: [1, 2, 3],
    NoCheckid:"no",
};

// 入力パラメータの満たすべき条件
const schemaObject = {
    testId: vs.string({minLength: 3}), //eventIdが文字列かつ5文字以上
    testNum: vs.number({minValue: 1,}), //testNumが数字かつ1以上
    testlist: vs.array({minLength: 2}), //testlistがarrayかつ2要素以上
                                        //noNoCheckidは条件を書いてないのでチェックされない
};

// 全ての条件を満たさない場合エラーを投げる,満たす場合は変数にいれる
const checkedRequestbody = vs.applySchemaObject(schemaObject,requestbody,(err)=> {
    throw new Error("入力パラメータがおかしい")
});
//====その2. パラメータ単体でチェックする方法====
var vs = require("value-schema");
const flag = true;
const checkedName = vs.boolean().applyTo(flag,(err)=> { //flagがbool値である
    throw new Error("入力flagがおかしい") //条件を見たさない場合err投げる
});

ほぼ全ての型で使える便利なオプション

{strictType: true} : 厳密なタイプチェックができる、デフォルトはオフ
{ifUndefined: somevalue} : パラメータがundefinedの場合に任意の値を入れておく
{ifNull: somevalue} : パラメータがnullの場合に任意の値を入れておく
{ifEmptyString: somevalue} : パラメータが空文字""の場合に任意の値を入れておく

*その他オプションなどは公式を参照

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

Reactでnpm startができなくなった話。

React.js Node.js環境でnpm start実行時にエラー発生。

解決策だけ見たい人は最後に載せてます。(予定)

問題編

【React】いまどきのJSプログラマーのためのNode.jsとReactアプリケーション開発テクニックという参考書

の写本・実践をやっていると、

React.js Node.js環境でnpm startできなくなった。

経緯編

create-react-appでreactのアプリを作った。

app.jsを編集し、いざ起動する。

npm start 

そうすると、以下のようなエラーが発生。

> cycle@0.1.0 start /Users/user/Documents/ubuntu/cycle
> react-scripts start


There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

The react-scripts package provided by Create React App requires a dependency:

  "webpack": "4.42.0"

Don't try to install it manually: your package manager does it automatically.
However, a different version of webpack was detected higher up in the tree:

  /Users/user/Documents/ubuntu/node_modules/webpack (version: 4.44.1) 

Manually installing incompatible versions is known to cause hard-to-debug issues.

If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That will permanently disable this message but you might encounter other issues.

To fix the dependency tree, try following the steps below in the exact order:

  1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.
  2. Delete node_modules in your project folder.
  3. Remove "webpack" from dependencies and/or devDependencies in the package.json file in your project folder.
  4. Run npm install or yarn, depending on the package manager you use.

In most cases, this should be enough to fix the problem.
If this has not helped, there are a few other things you can try:

  5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.
     This may help because npm has known issues with package hoisting which may get resolved in future versions.

  6. Check if /Users/user/Documents/ubuntu/node_modules/webpack is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  7. Try running npm ls webpack in your project folder.
     This will tell you which other package (apart from the expected react-scripts) installed webpack.

If nothing else helps, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That would permanently disable this preflight check in case you want to proceed anyway.

P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cycle@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cycle@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-09-07T13_28_49_449Z-debug.log
(base) user-no-MacBook-Air:cycle user$ SKIP_PREFLIGHT_CHECK=true npm start

> cycle@0.1.0 start /Users/user/Documents/ubuntu/cycle
> react-scripts start

Attempting to bind to HOST environment variable: x86_64-apple-darwin13.4.0
If this was unintentional, check that you haven't mistakenly set it in your shell.
Learn more here: https://bit.ly/CRA-advanced-config

dyld: lazy symbol binding failed: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

dyld: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cycle@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cycle@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-09-07T13_29_11_414Z-debug.log
(base) user-no-MacBook-Air:cycle user$ eact-scripts start
-bash: eact-scripts: command not found
(base) user-no-MacBook-Air:cycle user$ react-scripts start

There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

The react-scripts package provided by Create React App requires a dependency:

  "webpack": "4.42.0"

Don't try to install it manually: your package manager does it automatically.
However, a different version of webpack was detected higher up in the tree:

  /Users/user/Documents/ubuntu/node_modules/webpack (version: 4.44.1) 

Manually installing incompatible versions is known to cause hard-to-debug issues.

If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That will permanently disable this message but you might encounter other issues.

To fix the dependency tree, try following the steps below in the exact order:

  1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.
  2. Delete node_modules in your project folder.
  3. Remove "webpack" from dependencies and/or devDependencies in the package.json file in your project folder.
  4. Run npm install or yarn, depending on the package manager you use.

In most cases, this should be enough to fix the problem.
If this has not helped, there are a few other things you can try:

  5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.
     This may help because npm has known issues with package hoisting which may get resolved in future versions.

  6. Check if /Users/user/Documents/ubuntu/node_modules/webpack is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  7. Try running npm ls webpack in your project folder.
     This will tell you which other package (apart from the expected react-scripts) installed webpack.

If nothing else helps, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That would permanently disable this preflight check in case you want to proceed anyway.

P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!

検索すると、

SKIP_PREFLIGHT_CHECK=true

を.envファイルに記載すると良いと出てきた。(勘違いかも。。)

だが、まずcreate-react-appで作ったアプリのフォルダ内に.envファイルが見つからない。。。

どうしようと悩み、調べ続けるとnpm startの後に、直接打てば良いのでは? との記事を発見。。

試してみる。

SKIP_PREFLIGHT_CHECK=true npm start

結果。

> cycle@0.1.0 start /Users/user/Documents/ubuntu/cycle
> react-scripts start

Attempting to bind to HOST environment variable: x86_64-apple-darwin13.4.0
If this was unintentional, check that you haven't mistakenly set it in your shell.
Learn more here: https://bit.ly/CRA-advanced-config

dyld: lazy symbol binding failed: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

dyld: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cycle@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cycle@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-09-07T13_29_11_414Z-debug.log
(base) user-no-MacBook-Air:cycle user$ eact-scripts start
-bash: eact-scripts: command not found
(base) user-no-MacBook-Air:cycle user$ react-scripts start

There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

The react-scripts package provided by Create React App requires a dependency:

  "webpack": "4.42.0"

Don't try to install it manually: your package manager does it automatically.
However, a different version of webpack was detected higher up in the tree:

  /Users/user/Documents/ubuntu/node_modules/webpack (version: 4.44.1) 

Manually installing incompatible versions is known to cause hard-to-debug issues.

If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That will permanently disable this message but you might encounter other issues.

To fix the dependency tree, try following the steps below in the exact order:

  1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.
  2. Delete node_modules in your project folder.
  3. Remove "webpack" from dependencies and/or devDependencies in the package.json file in your project folder.
  4. Run npm install or yarn, depending on the package manager you use.

In most cases, this should be enough to fix the problem.
If this has not helped, there are a few other things you can try:

  5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.
     This may help because npm has known issues with package hoisting which may get resolved in future versions.

  6. Check if /Users/user/Documents/ubuntu/node_modules/webpack is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  7. Try running npm ls webpack in your project folder.
     This will tell you which other package (apart from the expected react-scripts) installed webpack.

If nothing else helps, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That would permanently disable this preflight check in case you want to proceed anyway.

P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!

んーうまくいかない。。。。

さらに調べる。

そうすると、今度はホームディレクトリのnode_modulesを全削除すればうまくいくとの記事を発見。

試してみる。

create-react-appで作った該当node_modulesのみ、一時名前を変えて退避。

そして、それ以外のnode_modulesをホームに戻って全削除。

以下全削除のcommand

find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +

そして、create-react-appで作ったアプリのディレクトリに戻って、npm startを再度実行。

またエラーになった。

でもエラー文は変わった。

> cycle@0.1.0 start /Users/user/Documents/ubuntu/cycle
> react-scripts start

internal/modules/cjs/loader.js:895
  throw err;
  ^

Error: Cannot find module 'entities/maps/entities.json'
Require stack:
- /Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Tokenizer.js
- /Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Parser.js
- /Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/index.js
- /Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/tools.js
- /Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/renderKid/styleApplier/inline.js
- /Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/RenderKid.js
- /Users/user/Documents/ubuntu/cycle/node_modules/pretty-error/lib/PrettyError.js
- /Users/user/Documents/ubuntu/cycle/node_modules/html-webpack-plugin/lib/errors.js
- /Users/user/Documents/ubuntu/cycle/node_modules/html-webpack-plugin/index.js
- /Users/user/Documents/ubuntu/cycle/node_modules/react-scripts/config/webpack.config.js
- /Users/user/Documents/ubuntu/cycle/node_modules/react-scripts/scripts/start.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:892:15)
    at Function.Module._load (internal/modules/cjs/loader.js:742:27)
    at Module.require (internal/modules/cjs/loader.js:964:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (/Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Tokenizer.js:4:17)
    at Module._compile (internal/modules/cjs/loader.js:1075:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1096:10)
    at Module.load (internal/modules/cjs/loader.js:940:32)
    at Function.Module._load (internal/modules/cjs/loader.js:781:14)
    at Module.require (internal/modules/cjs/loader.js:964:19) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Tokenizer.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Parser.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/index.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/tools.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/renderKid/styleApplier/inline.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/RenderKid.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/pretty-error/lib/PrettyError.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/html-webpack-plugin/lib/errors.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/html-webpack-plugin/index.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/react-scripts/config/webpack.config.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/react-scripts/scripts/start.js'
  ]
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cycle@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cycle@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-09-07T13_38_20_165Z-debug.log

エラー文のいかに注目。。。

Error: Cannot find module 'entities/maps/entities.json'

このエラー文で検索をかけると、

install entities: npm i entities
create a new index.js file with: require("entities/maps/entities.json")
run it

とすれば良いのでは? との記事を発見。

実行してみる。

結果。

> cycle@0.1.0 start /Users/user/Documents/ubuntu/cycle
> react-scripts start

Attempting to bind to HOST environment variable: x86_64-apple-darwin13.4.0
If this was unintentional, check that you haven't mistakenly set it in your shell.
Learn more here: https://bit.ly/CRA-advanced-config

dyld: lazy symbol binding failed: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

dyld: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cycle@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cycle@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-09-07T22_44_27_537Z-debug.log

また別のエラー。。。

はぁ。。。

エラー文のなかで、

Attempting to bind to HOST environment variable: x86_64-apple-darwin13.4.0
If this was unintentional, check that you haven't mistakenly set it in your shell.

に注目し、このエラー文で検索をかける。

そうすると、原因が

「HOST環境変数にバインドしようとしています:x86_64-apple-darwin13.4.0
これが意図的でない場合は、誤ってシェルに設定していないことを確認してください。」

らしい。

HostをBindから解放するには

unset HOST

をすれば良いらしい。

実行。

エラー消えた!!!!

解決編

以下僕の場合の解決手順

  1. ホームディレクトリのnode_modulesを全削除(create-react-appで作ったディレクトリのnode_modulesを除く。)

  2. entitiesのinstall

  3. HostのBindからの解放

以下実際のコマンド

find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +
install entities: npm i entities
create a new index.js file with: require("entities/maps/entities.json")
run it
unset HOST

参考文献

https://teratail.com/questions/177560

https://qiita.com/yuta0801/items/118d9478ad536a443f3f

https://mebee.info/2020/03/03/post-6791/

https://github.com/davideicardi/live-plugin-manager/issues/14

https://qiita.com/hppRC/items/4c09c1f33135a40870bd

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

クジラとネコの親子プログラミング - Docker for Windowsを使ってScratch3.0のオリジナル拡張機能を試してみよう。

親子プログラミングはGitとNode.jsが使えることが前提になっている?

小学校でのプログラミング教育の必須化に伴って、子供たちがScratch 3.0に触れる機会も増えるかと思います。そんな中、ブロックをくっつけるだけのお遊びツールだとあなどっていると、次のような連載が始まりました。

「親子でできる!Scratch と AWS を使った "ものづくり" 体験 - 1. 準備 〜 疎通確認編」
2020-09-01
How to be a Developer
金澤 圭

この連載の「2. 作業の前提について」で、衝撃的な次の一文があります。

PC に Git と Node.js がインストールされている必要があります。

保護者の方も、GitやNode.jsを当たり前のようにインストールしなければいけない時代になりましたね。でも、安心してください。Dockerという便利なツールがありますので、簡単に開発環境を整えることができます。

本記事では、Webベースの統合開発環境であるcode-serverとScratch 3.0 拡張機能の開発と実行に必要なソースコード(scratch-vm/scratch-gui)を詰め合わせたDockerコンテナ(jprad/s3coder)を使って、Windowsパソコン上で、Scratch 3.0 GUIサーバーを起動できるようにします。
後半では、「Scratch 3.0の拡張機能を作ってみよう」を参考にして、拡張機能を追加します。

拡張機能を追加して、Scratch 3.0 GUIでコーディング
image.png

必要なもの

Windows 10 パソコンにDockerをインストールしてください。Dockerのインストール方法は、リンク先や各種記事を参照してください。

セットアップと起動

Dockerイメージ(jprad/s3coder)を元にDockerコンテナを起動し、Scratch 3.0 GUIサーバーをWindowパソコンで実行できるようにします。

1. 共有フォルダの作成とDockerコンテナの初回起動

Windows 10 で、コマンドプロンプトを起動し、次の一連のコマンドを入力します。

コマンドプロンプト.cmd
cd c:\
mkdir scratch-ws
docker run --name s3coder -d -p 8080:8080 -p 8601:8601 ^
-v C:/scratch-ws:/home/coder/s3/scratch-ws -e PASSWORD=password ^
jprad/s3coder /home/coder/s3

REM docker run ... のコマンドが長いので「^(キャレット)」で改行しています。

C\scratch-wsフォルダは、Dockerコンテナにマウントされる共有フォルダです(/home/coder/s3/scratch-ws)。
docker runコマンドを開始すると、必要なDockerイメージ(合計:844.17 MB)のダウンロードが始まり、Dockerコンテナが起動されます(コンテナ名:s3coder)。

ダウンロード中のコマンドプロンプト
image.png

ダウンロード完了後、コンテナ起動済みのコマンドプロンプト
image.png

Dockerコンテナの初回起動時に、共有フォルダへのアクセスを許可するかどうかの確認通知が表示されますので、[Share it]で許可します。

共有を許可(Share it
image.png

2.code-severへのログインとScratch 3.0 GUIの起動

Dockerコンテナの起動完了後、code-server ( http://localhost:8080 ) へアクセスし、ログインします(パスワードは、passwordです)。統合開発環境が開きますので、Dockerイメージに同梱されている scratch-guiを起動します。起動方法が構成ファイル(./s3/.vscode/launch.json)に設定されていますので、クリック操作のみで起動できます。scratch-guiの起動完了後、Scratch 3.0 GUI ( http://localhost:8601 ) へアクセスすると、公式ページとは少しばかり異なっていますが、使い慣れたScratch 3.0でのコーディングが行えます。

手順
1. code-server ( http://localhost:8080 ) へアクセスする。
2. パスワードにpasswordと入力し、[SUBMIT]ボタンをクリックし、ログインする。
3. [Run (Ctrl + Shift + D)]アイコンをクリックする。
4. ドロップダウンリストで'Run Scratch 3.0 GUI'(既定)を選択する。
5. [Start Debuging]アイコンをクリックする。
6. [TERMINAL]タブをクリックし、ビルド完了後の'Compiled successfully' が表示されるのを待つ。
7. Scratch 3.0 GUI ( http://localhost:8601 ) へアクセスする。

scratch-guiの起動
image.png

Scratch 3.0 GUI
image.png

3.停止・再開

scratch-guiの再開・停止
scratch-guiを停止せずに再開するには、coder-serverの[Restart (Ctrl+Shift+F5)]アイコンをクリックします。
scratch-guiを停止するには、code-serverの[Stop (Shift+F5)]アイコンをクリックします。

scratch-guiの開始

  1. [Run (Ctrl + Shift + D)]アイコンをクリックする。
  2. ドロップダウンリストで'Run Scratch 3.0 GUI'(既定)を選択する。
  3. [Start Debuging]アイコンをクリックする。
  4. [TERMINAL]タブをクリックし、ビルド完了後の'Compiled successfully' が表示されるのを待つ。

Dockerコンテナの停止
コマンドプロンプトで次のコマンドを実行するとDockerコンテナ(コンテナ名:s3coder)が停止します。

コマンドプロンプト.cmd
docker stop s3coder

Dockerコンテナの再開
コマンドプロンプトで次のコマンドを実行するとDockerコンテナ(コンテナ名:s3coder)が再開します。

コマンドプロンプト.cmd
docker start s3coder

拡張機能の開発

Scratch 3.0 Extensions の開発に関する情報には、次のようなものがあります。

ここでは、「Scratch 3.0の拡張機能を作ってみよう」を参考にログを出力する拡張機能(ブロック)を実装(コピペ)してみます。

拡張機能に必要なファイル

拡張機能(s3coder_newblocks)を実装するには、次のファイルを追加・編集します。

scratch-vm

# フォルダ ファイル 追加 編集 説明
1 scratch-vm/src/extensions/s3coder_newblocks index.js 拡張機能(newblocks)の本体です。
2 scratch-vm/src/extension-support extension-manager.js 追加した拡張機能(s3coder_newblocks)をbuiltinExtensionsに追加登録します。

scratch-gui

# フォルダ ファイル 追加 編集 説明
1 scratch-gui/src/lib/libraries/extensions/s3coder_newblocks newblocks.png 600x372ピクセルのPNG形式ファイルです。拡張機能の選択時にバナーとして表示されます。
2 scratch-gui/src/lib/libraries/extensions/s3coder_newblocks newblocks-small.png 80x80ピクセルのPNG形式ファイルです。ブロックのアイコンとして表示されます。
3 scratch-gui/src/lib/libraries/extensions index.js PNG形式ファイルの読込定義と拡張ブロックの定義を設定します。

scratch-vm での実装

scratch-vmで、拡張機能そのものを実装します。

フォルダ作成

拡張機能用のフォルダを次の手順で作成します。

  1. code-serverのEXPLORERで、s3/scratch-vm/src/extensionsフォルダを選択します。
  2. 右クリックで、コンテキストメニューからNew Folderを選択し、's3coder_newblocks'フォルダを作成します。

index.jsファイルの実装

次の手順で、index.jsファイルを実装します。

  1. 作成した's3coder_newblocks'フォルダで右クリックし、New Fileを選択し、ファイル名をindex.jsとします。
  2. 「Scratch 3.0の拡張機能を作ってみよう/基本の書式 - 拡張機能の追加」からindex.jsのソースコードをコピー&ペーストします。

index.js
index.js
const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type');
const Cast = require('../../util/cast');
const log = require('../../util/log');

/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len
const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZyBpZD0iSUQwLjA4NjgyNDQzOTAwMDMzODMyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjQ5MTU0NjY2MDY2MTY5NzQsIDAsIDAsIDAuNDkxNTQ2NjYwNjYxNjk3NCwgLTY0LjUsIC03Ny4yNSkiPjxwYXRoIGlkPSJJRDAuNTcyMTQ2MjMwMzc3MjU2OSIgZmlsbD0iI0ZGOTQwMCIgc3Ryb2tlPSJub25lIiBkPSJNIDE4OCAxNDEgTCAyNTAgMTQxIEwgMjUwIDIwMyBMIDE4OCAyMDMgTCAxODggMTQxIFogIiB0cmFuc2Zvcm09Im1hdHJpeCgxLjI4NzkwMzMwODg2ODQwODIsIDAsIDAsIDEuMjg3OTAzMzA4ODY4NDA4MiwgLTExMC45LCAtMjQuNCkiLz48cGF0aCBpZD0iSUQwLjYzODMzNjEzNTA3NDQ5NjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTk2IDIwNCBDIDE5NiAyMDQgMTkyLjcwNiAxOTAuMDU4IDE5MyAxODMgQyAxOTMuMDc0IDE4MS4yMzYgMTk1Ljg4NiAxNzguNDU4IDE5NyAxODAgQyAyMDEuNDU1IDE4Ni4xNjggMjAzLjQ0MyAyMDMuNzU0IDIwNiAyMDEgQyAyMDkuMjExIDE5Ny41NDIgMjEwIDE2NiAyMTAgMTY2ICIgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgLTU3LCAxNS44KSIvPjxwYXRoIGlkPSJJRDAuNzU4NzMwMzU2NTgxNTA5MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgZD0iTSAyMTUgMTY5IEMgMjE1IDE2OSAyMTguMzY3IDE2OS41MzQgMjIwIDE3MCBDIDIyMC43MTYgMTcwLjIwNSAyMjEuMjc4IDE3MC44MTkgMjIyIDE3MSBDIDIyMi42NDYgMTcxLjE2MiAyMjMuMzY4IDE3MC43ODkgMjI0IDE3MSBDIDIyNC40NDcgMTcxLjE0OSAyMjUgMTcyIDIyNSAxNzIgIiB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLCAtNTcsIDE1LjgpIi8+PHBhdGggaWQ9IklEMC4yNDM2NzMwNzMxMjc4NjU4IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBkPSJNIDIyNyAxNTQgQyAyMjcgMTU0IDIxOC41NTUgMTQ3Ljg5MCAyMTcgMTUxIEMgMjEyLjM0NSAxNjAuMzEwIDIxMS4yODkgMTcxLjczMyAyMTMgMTgyIEMgMjEzLjYxMiAxODUuNjcyIDIyMyAxODcgMjIzIDE4NyAiIHRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIDEsIC01NywgMTUuOCkiLz48cGF0aCBpZD0iSUQwLjc5MzkzOTQ4MTk1NTAyMTYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTc1IDIwMC41MDAgQyAxNzUgMjAwLjUwMCAxNjkuODA1IDIyMS45MTMgMTcxIDIyMi43NTAgQyAxNzIuMTk1IDIyMy41ODcgMTc4Ljc5NSAyMDUuMjk1IDE4Mi41MDAgMjA1Ljc1MCBDIDE4NS45MjAgMjA2LjE3MCAxODEuODU5IDIyNC41MDAgMTg1LjI1MCAyMjQuNTAwIEMgMTg5LjIxMyAyMjQuNTAwIDE5Ny4yNTAgMjA1Ljc1MCAxOTcuMjUwIDIwNS43NTAgIi8+PC9nPjwvc3ZnPg==';

/**
 * Icon svg to be displayed in the category menu, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len
const menuIconURI = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZyBpZD0iSUQwLjA4NjgyNDQzOTAwMDMzODMyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjQ5MTU0NjY2MDY2MTY5NzQsIDAsIDAsIDAuNDkxNTQ2NjYwNjYxNjk3NCwgLTY0LjUsIC03Ny4yNSkiPjxwYXRoIGlkPSJJRDAuNTcyMTQ2MjMwMzc3MjU2OSIgZmlsbD0iI0ZGOTQwMCIgc3Ryb2tlPSJub25lIiBkPSJNIDE4OCAxNDEgTCAyNTAgMTQxIEwgMjUwIDIwMyBMIDE4OCAyMDMgTCAxODggMTQxIFogIiB0cmFuc2Zvcm09Im1hdHJpeCgxLjI4NzkwMzMwODg2ODQwODIsIDAsIDAsIDEuMjg3OTAzMzA4ODY4NDA4MiwgLTExMC45LCAtMjQuNCkiLz48cGF0aCBpZD0iSUQwLjYzODMzNjEzNTA3NDQ5NjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTk2IDIwNCBDIDE5NiAyMDQgMTkyLjcwNiAxOTAuMDU4IDE5MyAxODMgQyAxOTMuMDc0IDE4MS4yMzYgMTk1Ljg4NiAxNzguNDU4IDE5NyAxODAgQyAyMDEuNDU1IDE4Ni4xNjggMjAzLjQ0MyAyMDMuNzU0IDIwNiAyMDEgQyAyMDkuMjExIDE5Ny41NDIgMjEwIDE2NiAyMTAgMTY2ICIgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgLTU3LCAxNS44KSIvPjxwYXRoIGlkPSJJRDAuNzU4NzMwMzU2NTgxNTA5MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgZD0iTSAyMTUgMTY5IEMgMjE1IDE2OSAyMTguMzY3IDE2OS41MzQgMjIwIDE3MCBDIDIyMC43MTYgMTcwLjIwNSAyMjEuMjc4IDE3MC44MTkgMjIyIDE3MSBDIDIyMi42NDYgMTcxLjE2MiAyMjMuMzY4IDE3MC43ODkgMjI0IDE3MSBDIDIyNC40NDcgMTcxLjE0OSAyMjUgMTcyIDIyNSAxNzIgIiB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLCAtNTcsIDE1LjgpIi8+PHBhdGggaWQ9IklEMC4yNDM2NzMwNzMxMjc4NjU4IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBkPSJNIDIyNyAxNTQgQyAyMjcgMTU0IDIxOC41NTUgMTQ3Ljg5MCAyMTcgMTUxIEMgMjEyLjM0NSAxNjAuMzEwIDIxMS4yODkgMTcxLjczMyAyMTMgMTgyIEMgMjEzLjYxMiAxODUuNjcyIDIyMyAxODcgMjIzIDE4NyAiIHRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIDEsIC01NywgMTUuOCkiLz48cGF0aCBpZD0iSUQwLjc5MzkzOTQ4MTk1NTAyMTYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTc1IDIwMC41MDAgQyAxNzUgMjAwLjUwMCAxNjkuODA1IDIyMS45MTMgMTcxIDIyMi43NTAgQyAxNzIuMTk1IDIyMy41ODcgMTc4Ljc5NSAyMDUuMjk1IDE4Mi41MDAgMjA1Ljc1MCBDIDE4NS45MjAgMjA2LjE3MCAxODEuODU5IDIyNC41MDAgMTg1LjI1MCAyMjQuNTAwIEMgMTg5LjIxMyAyMjQuNTAwIDE5Ny4yNTAgMjA1Ljc1MCAxOTcuMjUwIDIwNS43NTAgIi8+PC9nPjwvc3ZnPg==';


/**
 * Class for the new blocks in Scratch 3.0
 * @param {Runtime} runtime - the runtime instantiating this block package.
 * @constructor
 */
class Scratch3NewBlocks {
    constructor (runtime) {
        /**
         * The runtime instantiating this block package.
         * @type {Runtime}
         */
        this.runtime = runtime;

        //this._onTargetCreated = this._onTargetCreated.bind(this);
        //this.runtime.on('targetWasCreated', this._onTargetCreated);
    }


    /**
     * @returns {object} metadata for this extension and its blocks.
     */
    getInfo () {
        return {
            id: 'newblocks',
            name: 'New Blocks',
            menuIconURI: menuIconURI,
            blockIconURI: blockIconURI,
            blocks: [
                {
                    opcode: 'writeLog',
                    blockType: BlockType.COMMAND,
                    text: 'log [TEXT]',
                    arguments: {
                        TEXT: {
                            type: ArgumentType.STRING,
                            defaultValue: "hello"
                        }
                    }
                },
                {
                    opcode: 'getBrowser',
                    text: 'browser',
                    blockType: BlockType.REPORTER
                }
            ],
            menus: {
            }
        };
    }

    /**
     * Write log.
     * @param {object} args - the block arguments.
     * @property {number} TEXT - the text.
     */
    writeLog (args) {
        const text = Cast.toString(args.TEXT);
        log.log(text);
    }

    /**
     * Get the browser.
     * @return {number} - the user agent.
     */
    getBrowser () {
        return navigator.userAgent;
    }
}

module.exports = Scratch3NewBlocks;

image.png

extension-manager.jsファイルの変更

scratch-vm/src/extension-supportフォルダのextension-manager.jsファイルを開き、builtinExtensionsに、次のように拡張機能(s3coder_newblocks)を追加登録します。
※カンマで区切ることを忘れないでください。

extension-manager.js
const builtinExtensions = {
    ...
    gdxfor: () => require('../extensions/scratch3_gdx_for'),
    newblocks: () => require('../extensions/s3coder_newblocks'),
};

image.png

scratch-gui での設定

scratch-guiで、拡張機能を選択できるように設定します。

フォルダ作成

画像保存用のフォルダを次の手順で作成します。

  1. code-serverのEXPLORERで、s3/scratch-gui/src/lib/libraries/extensionsフォルダを選択します。
  2. 右クリックで、コンテキストメニューからNew Folderを選択し、's3coder_newblocks'フォルダを作成します。

画像ファイル

画像ファイルをダウンロードし、作成したフォルダへ保存します。

2つの画像ファイル(クリックして展開)

newblocks.png
image.png

newblocks-small.png
image.png

  1. 2つの画像ファイルをダウンロードし、C:\scratch-wsフォルダへ保存します。
  2. code-serverのEXPLORERで、s3/scratch-wsフォルダを開き、2つの画像ファイルを選択し、右クリックでコンテキストメニューからCopyします。
  3. s3/scratch-gui/src/lib/libraries/extensions/s3coder_newblocksフォルダを選択し、右クリックでコンテキストメニューからPasteします。

image.png

index.jsファイルの設定

s3/scratch-gui/src/lib/libraries/extensionsフォルダのindex.jsファイルを開き、次のように拡張機能の設定を行います。これにより、Scratch 3.0 GUIの拡張機能の追加で、拡張機能を選択できるようになります。

index.js
...
import gdxforConnectionSmallIconURL from './gdxfor/gdxfor-small.svg';

import newblocksImage from './s3coder_newblocks/newblocks.png';
import newblocksInsetImage from './s3coder_newblocks/newblocks-small.png';

export default [
...
        ),
        helpLink: 'https://scratch.mit.edu/vernier'
    },
    {
        name: (
            <FormattedMessage
                defaultMessage="New Blocks"
                description="Name for the 'New Blocks' extension"
                id="gui.extension.newblocks.name"
            />
        ),
        extensionId: 'newblocks',
        iconURL: newblocksImage,
        insetIconURL: newblocksInsetImage,
        description: (
            <FormattedMessage
                defaultMessage="New extension"
                description="Description for the 'New Blocks' extension"
                id="gui.extension.newblocks.description"
            />
        ),
        featured: true
    },
];

image.png

動作確認

scratch-guiを開始し、Scratch 3.0 GUIを開きなおすと、「拡張機能を追加」で、新しい拡張機能を選択できます。
image.png

logブロックを実行すると、ブラウザのConsoleにhelloと出力されます。

image.png

おわりに

【メモ】Dockerイメージの作成
dos.cmd
cd c:\
mkdir scratch-ws
docker run -it -p 8080:8080 -p 8601:8601 -v C:/scratch-ws:/home/coder/s3/scratch-ws -e PASSWORD=password codercom/code-server /home/coder/s3

http://localhost:8080/ へアクセス

sudo apt-get update -y
sudo apt-get install -y nodejs npm
sudo npm install -g npm
sudo chown -R coder:coder /home/coder/s3
git clone --depth 1 https://github.com/llk/scratch-vm.git
git clone --depth 1 https://github.com/llk/scratch-gui.git

cd scratch-vm
npm ci
sudo npm link

cd ../scratch-gui
npm ci
sudo npm link scratch-vm

npm start
--->> [Ctrl]+[C]で中断

http://localhost:8601/ へアクセス

  • scratch-guiのポート
    ENV環境変数で設定可能

Dockerfile の記述

docker build -t test-image .

docker run --name test-container -it -p 8080:8080 -p 8601:8601 -v C:/scratch-ws:/home/coder/s3/scratch-ws -e PASSWORD=password test-image /home/coder/s3

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

stdout is not a ttyに叱られる

完全に自分用の健忘録です

Git Bash(ver2.28)で実行しようとした際に、

$ node hogehuga.js 
stdout is not a tty

と叱られてしまった。悲しい。
原因はMinTTYのアレです。大体winptyを先頭につければ解決すると思ってたけど、今回は解決しなかった。
Gitを再インストールしてもダメ。

5分くらい悩んで、拡張子をつけたら動いた。

$ node.exe hogehuga.js

以下参考
https://github.com/mintty/mintty/wiki/Tips#inputoutput-interaction-with-alien-programs

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

Twitterでかっこよくソースコードを表示するCarbonを君は知っているか。

こんにちは、現在フリーのweb制作者として活動していますしょーごと申します。

さてみなさん、ソースコードのハイライトは見ていて気持ちが良いですよね。

VSCodeのコードハイライトだと、class名が緑色になるのが好きだったりします。

スクリーンショット 2020-09-07 20.47.45.jpg

これに慣れてしまうと、もう質素なメモ帳など使えなくなりますよね。

しかし、Twitterにはエディター機能はないので、ツイートしたくてもそのままソースコードを貼り付けると、すごく味気なくなります(文字数制限もきつい)

スクリーンショット 2020-09-07 20.51.48.jpg

私はTwitterを初めて8年、エンジニアを初めて2年立ちますが、2020年からイカしたサービスを活用しています。

コードハイライト大好き教のあなた、今日からCarbonを使うのです。

Carbonとは

Twitterできれいにソースコードを共有できるサービスです。

Carbon

スクショで貼るより綺麗です。

スクリーンショット 2020-09-07 20.10.48.jpg

様々な言語、テーマに対応しているのが写真でわかるかと思います。

上記の写真でのテーマはVSCodeですが、私はsetiというテーマが好きなので、setiで表示していきます。

Ruby

スクリーンショット 2020-09-07 20.16.29.jpg

JavaScript(Vue)

スクリーンショット 2020-09-07 20.19.38.jpg

Sass

スクリーンショット 2020-09-07 20.23.08.jpg

PHP

スクリーンショット 2020-09-07 20.40.51.jpg

簡単投稿、画像保存も可能

Twitterでサインインすればすぐツイートできますし、画像としてpngなどでエクスポートすることも可能です。

スクリーンショット 2020-09-07 20.34.36.jpg

投稿時にはCreated with @carbon_appは消しても問題ありません。
pic.twitter.com以下があれば問題ありません。

スクリーンショット 2020-09-07 20.23.34.jpg

スクリーンショット 2020-09-07 20.32.28.jpg

うーん、おしゃれ!!!

Twitterにソースコードを共有する心理としては、

「私のこのスマートなコードを見てくれ!これ便利でおすすめだぜ!!」

pose_makasenasai_boy.png

という心理かと思いますので(独断と偏見)

訴求力で見ても、とてもスマートなソースコードの共有方法だと思います。

Carbon

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

【Hash関数】できるだけ衝突率を上げずにIDを短くしたい

背景

  • もともと一意のIDを別DBでも使いたかったが、該当のカラムの文字数に制限があり、そのまま使うことができなかった。

やりたいこと

  • 英数字でできたIDををなるべく一意性を保ったまま短くしたい
    • 例: abc123DEF456fgh7890(19桁) => 123abc456DEF(12桁)

手順

  1. Hash関数を用いて文字列から数値に変換する
  2. その数値を62進数(a-z + A-Z + 0-9)に変換する

こうすることで、一意性をギリギリまで保ったまま短くできると考えた

使用環境・言語

  • macOS Mojave 10.14.5
  • node 10.19.0
  • TypeScript 3.9.7

実装

1. ハッシュ関数の選定

ハッシュ関数と言っても、アルゴリズムや出力サイズ等、様々な種類が存在している。
=> 参考: hashアルゴリズムとハッシュ値の長さ一覧(+ハッシュ関数の基本と応用)

今回、選定の肝となるのは「出力サイズ」。つまり、何bitの出力が得られるか。
bit数は最終的な桁数に直結するため、ちょうどいいサイズを探す必要がある。

先駆者の方が公開してくださっていたので、任意の長さの短いハッシュ関数を考える(短縮 URLなど) で実際に試しながら良さげなものを探してみる。

結果、今回は64bit出力のfnv1a64を使うことにした。
64bitを62進数にすると11桁になる)

// 色々ライブラリがあったが、DL数が多いのを選んだ
import fnv1a from '@sindresorhus/fnv1a';

const inputStr = 'abc123DEF456fgh7890';
// jsのNumber型は2^53-1までしか表現できないのでBiGInt型の出力
const hash = fnv1a.bigInt(inputStr, { size: 64 });
console.log(hash); // 9465884287178377307n

2. 10進数を62進数に変換

10進数を2進数に変換するのと同様の方法で62進数に変換する。
62進数の一桁を0~9,a~z,A~Zにすることで、10進数を文字列に変換することができる。

const D_62 = BigInt(62);
const DIGIT_62 =
  '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

const decTo62 = (num: bigint): string => {
  const converted = [];
  while (num >= D_62) {
    const quotient = num / D_62;
    const remainder = Number(num % D_62);
    num = quotient;
    // インデックスはNumber型のみ
    // 64bitなら一度1/62にすれば2^53-1以下になる想定
    converted.unshift(DIGIT_62[remainder]);
  }
  converted.unshift(DIGIT_62[Number(num)]);
  return converted.join('');
};

const inputBigInt = BigInt(9465884287178377307);
console.log(decTo62(inputBigInt)); // INdk5tWOAhb

3. 完成したコード

import fnv1a from '@sindresorhus/fnv1a';

const D_62 = BigInt(62);
const DIGIT_62 =
  '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

const decTo62 = (num: bigint): string => {
  const converted = [];
  while (num >= D_62) {
    const quotient = num / D_62;
    const remainder = Number(num % D_62);
    num = quotient;
    converted.unshift(DIGIT_62[remainder]);
  }
  converted.unshift(DIGIT_62[Number(num)]);
  return converted.join('');
};

export const hash11 = (v: string): string => {
  /**
  * 文字列から一意の文字列11桁を生成する
  * 使用しているhashアルゴリズムはfnv1a、サイズは64bit
  * 衝突確率は 2^(bit数/2) 程度と言われているため
  * 2^(64/2) = 4294967296
  * 約42億分の1
  */

  const hash = fnv1a.bigInt(v, { size: 64 });
  return decTo62(hash);
}

// 使用例
const v = 'testRequestUID12345'
console.log(v, hash11(v)); // lhu2C5XktMT

4. まとめ

  • 同じ入力に対しては毎回同じ文字列が返せる(短くなった分衝突率は上がってしまうが)
    • 「最初のxx桁を切り抜く」よりかはマシ
    • インクリメントで一意のIDを生成するほうが素直
  • 要件にあったHash関数ライブラリを見つけるのに苦労した
    • 文字列を引数に取れるものが見つからない・・・

最後まで読んでいただき、ありがとうございました:sob:

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

svelte/transitionでさくっと使うことができる5つのアニメーション

はじめに

Svelte.jsではsvelte/transitionモジュールを取り込むことによって、アニメーションを使うことができる。
今回はさくっと使うことができる5つに絞ってそれぞれどのような動きになるのか、簡単に見てみる。

svelte/transition一覧

fade

名前の通りフェードアニメーションです。
svelte/transitionをインポートする際にfadeを指定することで使うことができる。
そしてfadeしたい部分にtransition:fadeを追加する。※後に続くアニメーション紹介では省略します。

<script>
  import { fade } from 'svelte/transition';
  ...

<p transition:fade>Hi Fade!</p>

fade

さらにfadedelayパラメータを渡すことによって、fadeを遅延させることが可能です。

  <p transition:fade="{{delay: 250}}">Hi Fade!</p> 

※デフォルトdelay値は0

blur

blurはfadeと少し似ているがぼかしながら消えていくアニメーション。

<script>
  import { blur } from 'svelte/transition';
</script>

...

<p transition:blur>Hi Blur!</p>

blur

また、delay意外にもdurationopacityamountといったパラーメーターがあります。

試しにopacityを500にしたとき

<p transition:blur="{{opacity: 500}}">Hi Blur! </p>

blur_no_check_opacity (1).gif

先ほどよりも不透明度が強く感じる。長く見てると気持ち悪くなりそう、、

fly

flyはパラメーターx,yのいずれかに数値を設定することでその方向に飛ばすことができる。
ちなみにパラメーターを指定しないとfadeと同じような動きになった。

xを200で指定したとき

<script>
  import { fly } from 'svelte/transition';
</script>

...

<p transition:fly="{{x: 200}}">Hi Fly!</p>

fly_no_check (1).gif

flyのパラメーターなしのとき

ちなみに先ほど言ったxyも指定しないとき

fly_no_xy

やはりfadeと同じなような気がするのでflyを使うときは注意が必要そう。

slide

slideは指定した要素をスライドさせるアニメーション

<script>
  import { slide } from 'svelte/transition';
</script>

...

<p transition:slider>Hi Slide!</p>

slide

scale

scaleは横方向にサッと消えていくようなアニメーション

<script>
  import { scale } from 'svelte/transition';
</script>

...

<p transition:scaler>Hi Scale!</p>

scale

ちなみにですが、durationパラメーターを使って数値を低く設定すると早く消えます。

durationを100に設定したとき

scale_duration

最後に

その他にもSVGと一緒に使うdrawといったものもあるので、気になる方は公式ドキュメントをみるといいかもしれません。

参考資料

API Docs • Svelte

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

TypeScriptの型上級チートシート

Original article: https://www.ibrahima-ndaw.com/blog/advanced-typescript-cheat-sheet/

以下はIbrahima Ndaw( Twitter / GitHub / LinkedIn / Webサイト )によるTypeScriptの解説、Advanced TypeScript Types cheat sheet (with examples)の日本語訳です。
リンクなどは元記事のままであり、和訳にあたり変更していません。

Advanced TypeScript Types cheat sheet (with examples)

TypeScriptは型付き言語であり、変数、関数の引数および返り値、オブジェクトのプロパティに型を指定することが可能です。

この記事では、TypeScriptの型の高度な使い方を例示付きで紹介します。

Sorry for the interrupt!

TypeScriptを総合的に学びたい人には、こちらのベストセラーコースを強くお勧めします。
Understanding TypeScript - 2020 Edition
これはアフィリエイトリンクなので、よかったら応援してね。

Intersection Types

交差型とは、複数の型をひとつに結合した型です。
すなわち、型Aと型B、もしくはさらに他の型をマージして、それら全てのプロパティを持ったひとつの型を得ることができます。

type LeftType = {
  id: number
  left: string
}

type RightType = {
  id: number
  right: string
}

type IntersectionType = LeftType & RightType

function showType(args: IntersectionType) {
  console.log(args)
}

showType({ id: 1, left: "test", right: "test" })
// Output: {id: 1, left: "test", right: "test"}

見てのとおり、交差型はLeftTypeとRightType両方の要素を持っています。
交差型をつくるには&で結合するだけです。

Union Types

Union型は、与えられた型のうち何れかの型となることができます。

type UnionType = string | number

function showType(arg: UnionType) {
  console.log(arg)
}

showType("test")
// Output: test

showType(7)
// Output: 7

関数showTypeは、string型もしくはnumber型いずれかの値を引数として受け付けることができます。

Generic Types

ジェネリック型とは、与えられた型を再利用する手段です。
引数の型を変数のようにキャプチャすることができます。

function showType<T>(args: T) {
  console.log(args)
}

showType("test")
// Output: "test"

showType(1)
// Output: 1

ジェネリック型を生成するには、関数名に<>で括った型名T ( 実際は任意の名前でよい ) を指定します。
以下に、関数showTypeを異なる型で呼び出す例を示します。

interface GenericType<T> {
  id: number
  name: T
}

function showType(args: GenericType<string>) {
  console.log(args)
}

showType({ id: 1, name: "test" })
// Output: {id: 1, name: "test"}

function showTypeTwo(args: GenericType<number>) {
  console.log(args)
}

showTypeTwo({ id: 1, name: 4 })
// Output: {id: 1, name: 4}

ジェネリック型Tを受け取るインターフェイスGenericTypeを定義しました。
これは再利用可能なので、ひとつめのGenericTypeはstring型の値を受け取り、ふたつめはnumber型を受け取っています。

interface GenericType<T, U> {
  id: T
  name: U
}

function showType(args: GenericType<number, string>) {
  console.log(args)
}

showType({ id: 1, name: "test" })
// Output: {id: 1, name: "test"}

function showTypeTwo(args: GenericType<string, string[]>) {
  console.log(args)
}

showTypeTwo({ id: "001", name: ["This", "is", "a", "Test"] })
// Output: {id: "001", name: Array["This", "is", "a", "Test"]}

ジェネリック型を複数渡すこともできます。
上の例では二つのジェネリック型TとUを渡しています。
interfaceを使用することで、異なる型の引数を渡す関数が提供できるようになりました。

Utility Types

TypeScriptでは、型を容易に操作することができるように便利な組込ユーティリティ型が提供されています。
これらを使うときは、変換したい型を<>に入れて渡します。

Partial

Partial<T>

Partial型は、該当する型の全てのプロパティをオプショナルにすることができます。
これはすなわち、全てのフィールドに?を追加するようなものです。

interface PartialType {
  id: number
  firstName: string
  lastName: string
}

// firstNameがstringからstring?になる
function showType(args: Partial<PartialType>) {
  console.log(args)
}

showType({ id: 1 })
// Output: {id: 1}

showType({ firstName: "John", lastName: "Doe" })
// Output: {firstName: "John", lastName: "Doe"}

関数showType()の引数としてPartialType型を渡していますが、プロパティをオプショナルにするためにPartialユーティリティ型を通しています。
これだけで、PartialType型の全ての値がオプショナルになりました。

Required

Required<T>

Partial型とは「逆に、Required型は全てのプロパティを必須にします。

interface RequiredType {
  id: number
  firstName?: string
  lastName?: string
}

// firstNameがstring?からstringになる
function showType(args: Required<RequiredType>) {
  console.log(args)
}

showType({ id: 1, firstName: "John", lastName: "Doe" })
// Output: { id: 1, firstName: "John", lastName: "Doe" }

showType({ id: 1 })
// Error: Type '{ id: number: }' is missing the following properties from type 'Required<RequiredType>': firstName, lastName

Requiredユーティリティ型を通すことによって、オプショナルであるはずのRequiredType型の全ての値が必須になります。
プロパティを省略した場合、TypeScriptはエラーを発生させます。

Readonly

Readonly<T>

Readonlyユーティリティ型は、全てのプロパティを変更不可能にします。

interface ReadonlyType {
  id: number
  name: string
}

function showType(args: Readonly<ReadonlyType>) {
  args.id = 4
  console.log(args)
}

showType({ id: 1, name: "Doe" })
// Error: Cannot assign to 'id' because it is a read-only property.

Readonlyユーティリティ型によって、ReadonlyType型の全ての値は再割り当て不能になります。
いずれかのフィールドに新しい値を設定しようとすると、エラーになります。

もっと単純に、プロパティの前にreadonlyキーワードを付けて再割り当て不能にすることもできます。

interface ReadonlyType {
  readonly id: number
  name: string
}

Pick

Pick<T, K>

Pickユーティリティ型は、元の型からいくつかのプロパティを選んで新たな型を生成します。

interface PickType {
  id: number
  firstName: string
  lastName: string
}

// PickTypeのうちfirstName,lastNameだけを使った新たな型
function showType(args: Pick<PickType, "firstName" | "lastName">) {
  console.log(args)
}

showType({ firstName: "John", lastName: "Doe" })
// Output: {firstName: "John"}

showType({ id: 3 })
// Error: Object literal may only specify known properties, and 'id' does not exist in type 'Pick<PickType, "firstName" | "lastName">'

これまでに見てきたユーティリティ型とは少々異なる構文で、二つの引数が必要です。
Tは元の型、そしてKは抽出したいプロパティです。
複数のフィールドを|で区切ることによって、複数のフィールドを抽出することも可能です。

Omit

Omit<T, K>

Omitユーティリティ型はPickのちょうど反対で、必要なプロパティを選ぶのではなく不要なプロパティを削除します。

interface PickType {
  id: number
  firstName: string
  lastName: string
}

// PickTypeのうちfirstName,lastNameを使わない新たな型
function showType(args: Omit<PickType, "firstName" | "lastName">) {
  console.log(args)
}

showType({ id: 7 })
// Output: {id: 7}

showType({ firstName: "John" })
// Error: Object literal may only specify known properties, and 'firstName' does not exist in type 'Pick<PickType, "id">'

Pickと同じ使い方で、元となる型から削除するプロパティを指定します。

Extract

Extract<T, U>

Extractユーティリティ型は、T型のプロパティのうち、U型に代入可能なプロパティを抽出します。
2つの型に共通するプロパティを取り出すと考えてよいでしょう。

interface FirstType {
  id: number
  firstName: string
  lastName: string
}

interface SecondType {
  id: number
  address: string
  city: string
}

type ExtractType = Extract<keyof FirstType, keyof SecondType>
// Output: "id"

上の例では、2つの型が同じプロパティidを持っています。
この型にExtractを使用することで、両方に共通するプロパティidを取り出すことができます。
共通するプロパティが複数存在する場合は、その全てが抽出されます。

Exclude

Exclude<T, U>

Excludeユーティリティ型は、T型のプロパティのうち、U型に代入可能なプロパティを除外した型を生成します。

interface FirstType {
  id: number
  firstName: string
  lastName: string
}

interface SecondType {
  id: number
  address: string
  city: string
}

type ExcludeType = Exclude<keyof FirstType, keyof SecondType>
// Output; "firstName" | "lastName"

FirstTypeのプロパティfirstNamelastNameはSecondTypeには存在しないため、Excludeで取り出すことができます。
SecondTypeの値addresscityは出てきません。

Record

Record<K,T>

このユーティリティは、T型の値の集合を作るために役立ちます。
ある型のプロパティを別の型にマッピングする際に、非常に便利です。

interface EmployeeType {
  id: number
  fullname: string
  role: string
}

let employees: Record<number, EmployeeType> = {
  0: { id: 1, fullname: "John Doe", role: "Designer" },
  1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
  2: { id: 3, fullname: "Sara Duckson", role: "Developer" },
}

// 0: { id: 1, fullname: "John Doe", role: "Designer" },
// 1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
// 2: { id: 3, fullname: "Sara Duckson", role: "Developer" }

Recordの動作はシンプルです。
上の例ではキーの型がnumberなので、0,1,2と数値を指定しています。
値はEmployeeType型となっているので、id・fullname・roleを持つオブジェクトが必要です。
文字列を与えたりするとエラーになります。

NonNullable

NonNullable<T>

型Tからnullとundefinedを消し去ります。

type NonNullableType = string | number | null | undefined

function showType(args: NonNullable<NonNullableType>) {
  console.log(args)
}

showType("test")
// Output: "test"

showType(1)
// Output: 1

showType(null)
// Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.

showType(undefined)
// Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.

NonNullableユーティリティは、引数からnullとundefinedを消し去った新たな型を生成します。
その型にNullableな値を渡すと、TypeScriptはエラーを発します。

なお、tsconfigファイルにstrictNullChecksを指定すると、自動的に全ての型にNonNullableが適用されます。

Mapped types

Mapped typesは、既存のモデルを流用しつつ、各プロパティを新しい型に変更することができるようになります。
先に解説したユーティリティ型も、一部の実体はMapped typesです。

type StringMap<T> = {
  [P in keyof T] : string
}

function showType(arg: StringMap<{ id: number; name: string }>) {
  console.log(arg)
}

showType({ id: 1, name: "Test" })
// Error: Type 'number' is not assignable to type 'string'.

showType({ id: "testId", name: "This is a Test" })
// Output: {id: "testId", name: "This is a Test"}

StringMap<>は、渡された型が何であれ、とりあえず文字列型にします。
従って、showTypeに渡す型はnumberではなくstringとなり、number型を渡した場合はエラーが出ることになります。

Type Guards

Type Guardsを使うと、変数やオブジェクトの型を演算子で判定することができます。

typeof

function showType(x: number | string) {
  if (typeof x === "number") {
    return `The result is ${x + x}`
  }
  throw new Error(`This operation can't be done on a ${typeof x}`)
}

showType("I'm not a number")
// Error: This operation can't be done on a string

showType(7)
// Output: The result is 14

上記コードでは、typeofを用いて受け取った引数の型をチェックしています。
条件で型ガードすることができました。

instanceof

class Foo {
  bar() {
    return "Hello World"
  }
}

class Bar {
  baz = "123"
}

function showType(arg: Foo | Bar) {
  if (arg instanceof Foo) {
    console.log(arg.bar())
    return arg.bar()
  }

  throw new Error("The type is not supported")
}

showType(new Foo())
// Output: Hello World

showType(new Bar())
// Error: The type is not supported

typeofの例と同様、こちらでは引数の型がFooクラスであるかをチェックします。

in

interface FirstType {
  x: number
}
interface SecondType {
  y: string
}

function showType(arg: FirstType | SecondType) {
  if ("x" in arg) {
    console.log(`The property ${arg.x} exists`)
    return `The property ${arg.x} exists`
  }
  throw new Error("This type is not expected")
}

showType({ x: 7 })
// Output: The property 7 exists

showType({ y: "ccc" })
// Error: This type is not expected

オブジェクトにプロパティが存在するかどうかはinでチェックすることができます。

Conditional Types

以下では複数の型をテストし、結果に応じてその片方を選択しています。

type NonNullable<T> = T extends null | undefined ? never : T

NonNullableユーティリティ型は、型がnullであるかをチェックし、結果に応じて異なる処理をしています。
この例ではJavaScriptの三項演算子を使用していることに注意してください。

コメント欄

dev.toのコメント欄

「わかりやすくてよい記事。GJ!」
「簡潔によくまとめられてる。」
独自のタイプガードの可能性を追求した。」
「もっとはやくPartialを知りたかった。わざわざ自力で書いてたよ。export type ObjectWithOptionalProps<T> = { [key in keyof T]?: T[key] };
「Maybe型type Maybe<T> = T | null;をよく使ってる。」「普通にオプショナルfunction foo(bar?: string)でよくない?」「Maybe型の場合は値が必須というところが異なるよ。」
「TypeScript複雑になりすぎてきたような」「ほとんどは糖衣構文なので使わなければいいだけだぞ」「せやな」
「行末にセミコロンを忘れるな」「セミコロンは必須ではないし使わない方が好き」「ない方がすっきりしててよい」「Google様に逆らうなら不要だね」

感想

TypeScript Guideシリーズの2番目で、主に型の変換について記述された記事です。
他のシリーズも通して読むことで、TypeScriptへの理解がより深まることでしょう。
参考になったらぜひコーヒーを買ってあげましょう。

実際に手を動かしてみたいという場合は、ちょうど最近日本語解説記事が出た type-challengesなどを試してみるとよいかもしれません。

しかし型に関するいろいろな機能はありますが、実際使うかといったら個人的にはほとんど使っていません。
私はフロントエンドエンジニアではないので本格的にがっつり使ってないからということもありますが、極めてふつーに、型を定義してそれを直接使う程度のことしかやっていませんし、それで不足を感じることもあまりありません。
日頃使ってるのはせいぜいUnion型くらいです。
OmitとかExcludeとか何に使うのか全くわからん。
みんなこういうのバキバキ使いこなしてるんですかね?

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

Vuetify でフィルタリング機能付きセレクトボックスを実装する

Vuetifyのv-autocompleteを利用して絞り込み機能付きのセレクトボックスを実装します。

https://vuetifyjs.com/ja/components/autocompletes/

vueでの記述は下記のようになります

page.vue
<v-autocomplete
    :items="users"
    item-value="id"
    item-text="name"
    placeholder="ユーザーを選択してください"
    v-model="user"
    autocomplete="off"
>
</v-autocomplete>

methodは以下のようになります

page.vue
  data() {
    return {
      users: []
    };
  },
  methods: {
    userListLoad() {
      axios.get('https://example.com/users')
        .then(
          result => {
            Object.entries(result.users).forEach(user =>
              this.users.push({ name: user["name"], id: user["id"] })
            );
          },
          err => {
            console.log(err);
          }
        );
    },
  },

かんたんですね。

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

TSでArray.reduce使ったら第一引数のaccumulatorがnever型になって混乱した話

Array.reduceが一度も回らないとcallbackが無いのでaccumulatorはneverになる

配列の型に[]の空配列を想定していると一度も回らないのでaccumulatorの戻り値無しでnever型になる。
空が想定される場合はそもそも回す処理まで到達出来ないようにしてあげよう。

// const orderArray: string[]|[] = this.orderArray
// ↓
const orderArray: string[] = this.orderArray

arrFunc () {
  return orderArray.reduce((accumulator, id) => {
    const item: ItemDocument|undefined = items.find(item => item.id === id)
    if (item) { accumulator.push(item) }
      return accumulator
    }, [])
}

対策

  1. 型の[]がArray系関数に到達しない処理にする
  2. Arrayを型キャストする
    1. https://stackoverflow.com/questions/54117100/why-does-typescript-infer-the-never-type-when-reducing-an-array-with-concat
const orderArray = this.orderArray as string[]

arrFunc () {
  return orderArray.reduce((accumulator, id) => {
    const item: ItemDocument|undefined = items.find(item => item.id === id)
    if (item) { accumulator.push(item) }
      return accumulator
    }, [])
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webカメラで顔認識させてみた

WebGazer.jsという、Webカメラを使って視線の追跡機能を実装できる面白そうなものを見つけたので試してみました。

ただこの視線の追跡機能、私も試してみたのですがうまく精度が出なかったので途中で断念しました。。

なので顔認識させるところまでのやり方を書いていこうと思います!

手順

まずこちらのURLから、webgazer.jsファイルをダウンロードします。

あとは適当なエディタを開いてプロジェクトを作成し、
index.htmlファイルを作成します。

そしてhead内でscriptタグを使ってwebgazer.jsを読み込みます。

index.html
<script src="webgazer.js" type="text/javascript"></script>

あとはbody内にscriptダグで下記コードを追加します。

index.html
<script>
  webgazer.setGazeListener(function (data, elapsedTime) {
    if (data == null) {
      return;
    }
    var xprediction = data.x;
    var yprediction = data.y;
    console.log(elapsedTime);
  }).begin();
</script>

もうこれだけでいけます。

実際に試してみましょう。
ブラウザからサーバにアクセスするとカメラの使用許可を求められるので、許可を選択しましょう!

するとこんな感じで顔認識してくれます!
すごい!楽しい!

Animated GIF-downsized_large (1).gif

ここからさらに視線追跡機能も試してみて、
動くには動いたのですが、私の環境だといまいち精度が出なかったので断念しました。。

興味のある方は是非上手くいくか試してみてください!

実装方法などについてはこちらをご参照ください。

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

WebGazer.jsを使って、Webカメラで顔認識してみた

WebGazer.jsという、Webカメラを使って視線の追跡機能を実装できる面白そうなものを見つけたので試してみました。

ただこの視線の追跡機能、私も試してみたのですがうまく精度が出なかったので途中で断念しました。。

なので顔認識させるところまでのやり方を書いていこうと思います!

手順

まずこちらのURLから、webgazer.jsファイルをダウンロードします。

あとは適当なエディタを開いてプロジェクトを作成し、
index.htmlファイルを作成します。

そしてhead内でscriptタグを使ってwebgazer.jsを読み込みます。

index.html
<script src="webgazer.js" type="text/javascript"></script>

あとはbody内にscriptダグで下記コードを追加します。

index.html
<script>
  webgazer.setGazeListener(function (data, elapsedTime) {
    if (data == null) {
      return;
    }
    var xprediction = data.x;
    var yprediction = data.y;
    console.log(elapsedTime);
  }).begin();
</script>

もうこれだけでいけます。

実際に試してみましょう。
ブラウザからサーバにアクセスするとカメラの使用許可を求められるので、許可を選択しましょう!

するとこんな感じで顔認識してくれます!
すごい!楽しい!

Animated GIF-downsized_large (1).gif

ここからさらに視線追跡機能も試してみて、
動くには動いたのですが、私の環境だといまいち精度が出なかったので断念しました。。

興味のある方は是非上手くいくか試してみてください!

実装方法などについてはこちらをご参照ください。

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

【JavaScript】fill()で横着して初期化しない

初めに

fill()でずぼらして初期化したら思った挙動にならなかったので備忘録。
実際のコードを引っこ抜いてちょっと変えてるので変な感じになってるかも。

JavaScriptは最近やり始めましたがよくわからんですね。

起こったこと

以下のような変数を作成した

sub.ts
export const FuncA = (): HogeHoge => {
    const [a, setA] = useState("0");
    const [b, setB] = useState("0");
    retunrn {
        parameter: {a,b},
        setter: {setA,setB}
    };
}

fill(funcA)をしたparameters配列のそれぞれの要素を突っ込んだテーブルを以下みたいな感じで作る。

main.tsx
function TableBody(params: Params){
    const parameters = Array(params.lineCount).fill((FuncA));

    return (
        <table>
            <thead>...</thead>
            <tbody> { getLines(params.lineCount,parameters) } </tbody>
        </table>
    );
}
...
function getLines(cnt: number,foo: Array<FuncA>) {
    return ([...Array<number>(cnt)].map((_, i) => {
        return (
            <tr>
                <td>{ i + 1}</td>
                <td>
                    <label>A: <input type="number" value={foo.parameter.a} onChange={(e) => foo.setter.setA(e.target.value)} /></label><br />
                    <label>B: <input type="number" value={foo.parameter.b} onChange={(e) => foo.setter.setB(e.target.value)} /></label>
               </td>
            </tr> 
        )
    }));
}

全てのinputが独立して変更されることを想定していたが、
画面上でinputの値を変更したばあい全ての行の値が変更された。

FuncAの定義をexport const FuncA(){...}にしてfill( ()=> FuncA())とかにもしたけど同じだった。

よくわからないけど直した

fill()をやめてmap()で割り当てたら独立した。
fill(0)をしている場合上記みたいなことにならないので、
非プリミティブ型の場合は参照で引きたわされるのだろうか。
(そもそも関数を変数に入れること自体がこれまでなかったのでそこの挙動の把握も実は微妙)

const parameters = [...Array(params.lineCount)].map(() => FuncA());

おそらくこの部分の記載に基づくもの

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
Polyfillのが同等の実装らしいので少し見たところ
var O = Object(this);のところが多分そうかなとは思った。

Objectのコンストラクタのページは404だったが、Objectクラスの説明に以下の記載があった。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object
Object コンストラクターは、指定された値のオブジェクトラッパーを生成します。
- 値が null または undefined である場合、空のオブジェクトを生成して返します。
- それ以外の場合は、与えられた値に関連する型のオブジェクトを返します。
- 値がすでにオブジェクトであった場合は、その値を返します。

少し調べた感じJavaScriptもプリミティブ型はオブジェクトではなさそうなので、
プリミティブ型は2つ目の挙動、それ以外の場合は3つ目の挙動になるのだろうか。
この「その値」というのが複製ではなく引き渡したオブジェクト自体がかえるという意味であれば納得できる。

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

【JavaScript】何も考えずfill()で横着して初期化しない

初めに

fill()でずぼらして初期化したら思った挙動にならなかったので備忘録。
実際のコードを引っこ抜いてちょっと変えてるので変な感じになってるかも。

JavaScriptは最近やり始めましたがよくわからんですね。

起こったこと

以下のような変数を作成した

sub.ts
export const FuncA = (): HogeHoge => {
    const [a, setA] = useState("0");
    const [b, setB] = useState("0");
    retunrn {
        parameter: {a,b},
        setter: {setA,setB}
    };
}

fill(funcA)をしたparameters配列のそれぞれの要素を突っ込んだテーブルを以下みたいな感じで作る。

main.tsx
function TableBody(params: Params){
    const parameters = Array(params.lineCount).fill((FuncA));

    return (
        <table>
            <thead>...</thead>
            <tbody> { getLines(parameters) } </tbody>
        </table>
    );
}
...
function getLines(foo: Array<FuncA>) {
    return ([...Array<number>(foo.length)].map((_, i) => {
        return (
            <tr>
                <td>{ i + 1}</td>
                <td>
                    <label>A: <input type="number" value={foo.parameter.a} onChange={(e) => foo.setter.setA(e.target.value)} /></label><br />
                    <label>B: <input type="number" value={foo.parameter.b} onChange={(e) => foo.setter.setB(e.target.value)} /></label>
               </td>
            </tr> 
        )
    }));
}

全てのinputが独立して変更されることを想定していたが、
画面上でinputの値を変更したばあい全ての行の値が変更された。

FuncAの定義をexport const FuncA(){...}にしてfill( ()=> FuncA())とかにもしたけど同じだった。

よくわからないけど直した

fill()をやめてmap()で割り当てたら独立した。
fill(0)をしている場合上記みたいなことにならないので、
非プリミティブ型の場合は参照で引きたわされるのだろうか。
(そもそも関数を変数に入れること自体がこれまでなかったのでそこの挙動の把握も実は微妙)

const parameters = [...Array(params.lineCount)].map(() => FuncA());

おそらくこの部分の記載に基づくもの

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
Polyfillのが同等の実装らしいので少し見たところ
var O = Object(this);のところが多分そうかなとは思った。

Objectのコンストラクタのページは404だったが、Objectクラスの説明に以下の記載があった。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object
Object コンストラクターは、指定された値のオブジェクトラッパーを生成します。
- 値が null または undefined である場合、空のオブジェクトを生成して返します。
- それ以外の場合は、与えられた値に関連する型のオブジェクトを返します。
- 値がすでにオブジェクトであった場合は、その値を返します。

少し調べた感じJavaScriptもプリミティブ型はオブジェクトではなさそうなので、
プリミティブ型は2つ目の挙動、それ以外の場合は3つ目の挙動になるのだろうか。
この「その値」というのが複製ではなく引き渡したオブジェクト自体がかえるという意味であれば納得できる。

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

preventDefaultはなぜ必要なのか

動作環境
Ruby 2.6.5
Rails 6.0.3.2

JavaScriptにおけるpreventDefaultがどういった働きをしているかをようやく理解することができたので、整理するために投稿してみました。

preventDefaultの意味

preventDefaultの必要性について話していく前に、そもそもpreventDefaultが何をしているのかを説明します。

default(デフォルト)の挙動をprevent(妨害)しています。

説明になっていないと思うかもしれませんが、厳密な説明をしようとすると、おそらくかなり長い行数が必要となってしまいますし、具体的な例を提示した方がわかりやすいと思い、端的に説明しました。早速、具体的な例を元に説明していきます。

preventDefaultの具体例

index.rb
<%= form_with url:  "/posts", method: :post do |form| %>
  <%= form.text_field :fuga  %>
  <%= form.submit '投稿する' , id: "submit" %>
<% end %>

index.rbにて投稿するボタンをクリックすると、fugaに入力した内容が投稿されるとします。
このままだと、投稿するボタンをクリックすると、ページの読み込みが始まってしまうので、下記のJavaScriptを記載することで非同期通信にしたとします。

function hoge() {
  const submit = document.getElementById("submit");
  submit.addEventListener("click", (e) => {
    // 投稿するボタンをクリックした時と同じ挙動のコード(長くなってしまうので、省略)
  });
};
window.addEventListener("load", hoge);

しかし、ここで1つ問題が生じてしまいます。それは、非同期通信にしたことによって同じ挙動が追加で行われてしまうので、1回の投稿で2回分投稿したことになってしまうということです。
具体的に説明すると、投稿するボタンをクリックすることで非同期通信が行われ、ページの読み込みなしで投稿が1つ増え、ページの再読み込みをすることで、本来の投稿するボタンをクリックした時の挙動が反映されて、投稿が1つ増えます。そのため、結果として同じ投稿が2回されるということが起きてしまいます。

この問題を解決するためにprevent.Defaultを使います。以下のように1行追加するだけです。

function hoge() {
  const submit = document.getElementById("submit");
  submit.addEventListener("click", (e) => {
    e.preventDefault();
    // 投稿するボタンをクリックした時と同じ挙動のコード(長くなってしまうので、省略)
  });
};
window.addEventListener("load", hoge);

これによって、デフォルトの挙動を妨害することができるので、1回の投稿で2回分投稿することがなくなりました。
しかし、ここで私は「そもそもデフォルトの挙動って何?」「なぜ都合よく非同期通信だけの挙動が妨害されていないの?」
といった疑問を抱いておりました。

デフォルトの挙動とは?

基本的にRuby on Railsでのデフォルトの挙動は、ビューでの操作によって、ビューからコントローラーに情報が送られ、コントローラー内のいずれかのアクションが動くという認識で問題ないようです。
今回でいうと、投稿するボタンをクリックしたことでcreateアクションが動き、fugaを保存するということがデフォルトの挙動です。通常のRuby on Railsの流れですね。
そのため私は、preventDefaultを「通常のRuby on Railsの流れを妨害するもの」と捉えております。

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

js コールバック関数のイメージが掴めた気がする記事

この記事について

Vue.jsの勉強をしているけど、ちゃんと基礎を勉強しなきゃと思いjsの勉強を始めました。
そこで今までなんとなくでしか使ってなかったコールバック変数が何者かが分かった気がするするので投稿。

参考記事

https://qiita.com/yukiji/items/ae2dbbd34f8557d5af19

コールバック変数って?

高階関数と呼ばれる関数の引数となる関数である。

とりあえず高階関数ってなに。。。

高階関数とは

高階関数とは関数を引数、戻り値に使う関数である。
つまり、引数にコールバック引数を受け取って関数の中で関数を実行するんや!

qiita.js
function 高階関数(コールバック関数){
  #色んな処理を実行したり。。。
  #コールバック関数の処理
}

どんな役割があるのか?

例えば自分がギリシャ時代の奴隷の管理者だとして、いちいち全ての奴隷の体調が良い状態で仕事をしているのか確認をするのはチョーめんどくさいわけです。
そんな時に奴隷の中で監督者を指名して、その監督者が確認した上で休憩を取らせるようにさせる。
この監督者が高階関数の役割なのだ!!!つまり関数で関数を監視させるのが高階関数

監督者がいなかったら、いちいち全ての仕事で同じ作業をしないといけない。

qiita.js
function cut(stone) {
 if(#/もし健康だったら /)
  #石を削る仕事をさせる
  end
end
}


function polish(stone) {
 if(#/もし健康だったら /)
  #石を磨く仕事をさせる
  end
end
}

そこで!!!高階関数(監督者)の出番じゃ!!!!

qiita.js
#--------  高階関数   ----------
function health(func) {
 if(#/もし健康だったら /)
    #funcがコールバック関数で高階関数の中で処理をさせる
  func();
 end
};



#--------  コールバック関数  ----------

function cut(stone) {
  #石を削る仕事をさせる
};


function polish(stone) {
  #石を磨く仕事をさせる
};

まとめ

繰り返しをしないためにもチョー便利なのが高階関数とコールバック関数のコンビなんやな。

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

Magenta.jsでローファイヒップホップ曲の無限生成?Lo-Fi Player

lofiplayer.jpg

Sony Flow MachineのLo-Fi Beats “サクラチル”に感化されたのでしょうか?
MagentaもMagenta.jsを使用したLo-Fiヒップホップ生成ウェブアプリ

Lo-Fi Player
https://magenta.github.io/lofi-player/

をリリースしました。

音楽生成や操作してみた動画をアップしましたのでご覧になってみてください。
2つの音楽データ(前半後半)の選択、ベースパート、ドラムパートのオン・オフ、背景画像効果音選択、などが可能です。

https://youtu.be/CmN2-jJoj0c

Lo-Fi Player技術的解説 (VAEについて)

このLo-Fi PlayerはMagenta.jsの中でもMusicVAEのInterpolation(2つの音楽データをつなぐことで新たな音楽を作る)というモデルが使用されている様です。
本来この2つの音楽データ(それぞれ1小節)を最初と最後に配置し、その間にある空間を埋める事により新たな音楽生成を行うのがMusicVAEの仕組みです。
最初と最後が1小節ずつ、としてその間が14小節分あれば合計16小節の音楽が生成されます。
図にしてみると下図の様になります。

m1.png

この区間を埋める時に、関連性の低い音データまでを考慮してしまうと、ニューラルネットワークの計算も大変、かつ、いわゆる音楽的に外れる可能性も高くなってしまいます。
そこでデータの重要な部分だけを残して削減(エンコーダ)し、元に戻す(デコーダ)を行うのがVAE (Variational Auto Encoder )です。
このVAEがMusicVAEの名前の由来になっています。
VAEの使用と不使用の効果を視覚化したのが下図です。

m2.png

MusicVAEは(オフィシャルに公開されているもの)最大3トラックの音楽生成が行えます。
今回のLo-Fi Playerも

・メロディー
・ベース
・ドラム

の3トラックの生成が行われています。
ちなみにドラム以外は単音です。
複数トラックの生成はニューラルネットワークの仕組み的に(データの取り扱い=数式設計の難易度)、それぞれの整合性をとるのが非常に大変なのですが、VAEによるデータの次元削減で音楽的関連性の低いデータを排除する事で、より確率高く、整合性のある音楽生成を可能にしています。

実は今回のLo-Fi Playerは合計2小節の生成です。
おそらく、この2小節は、上述した通り、元となっている音楽データで、その間の音楽生成は行われていないと考えられます。
つまり、用意された2曲の組み合わせ音楽生成アプリであって、機械学習の音楽生成アプリ、ではないと考えて良いかもしれません。(全てを把握していないので断言はできませんが、、、)

プログラムの中身に興味がある方はGitHubにコードが公開されていますので見てみるのも良いでしょう。

Lo-Fi Player GitHubページ
https://github.com/magenta/lofi-player

さらにPlay Mgenatサイトというものがあり、コードも自分で書き換え、音楽やビジュアルの変更が可能なあたりもいかにもMagentaです。

Play Magenta
https://play-magenta.glitch.me/

Play Magentaのコードはこんな感じです。

synth = new Tone.Synth({
 envelope: {
   attack: 0.005,
   attackCurve: "linear",
   decay: 0.1,
   decayCurve: "exponential",
   release: 1,
   releaseCurve: "exponential",
   sustain: 0.3,
 },
 oscillator: {
   count: 3,
   harmonicity: 1,
   modulationFrequency: 0.4,
   modulationIndex: 2,
   modulationType: "square",
   partialCount: 0,
   partials: [],
   phase: 0,
   spread: 20,
   type: "triangle",
   width: 0.2,
 },
});

synth.toDestination ? synth.toDestination() : synth.toMaster();

ちなみに開発者のVibert Thioさんは、記憶に間違いがなければ、元は個人的にMagenta.jsを使用した音楽ゲームアプリを投稿し、それが認められて?現在Google AIのMagentaチームインターンとなった経緯がある方の様です。

日本からも優秀なMusicニューラルネットワーク開発者が生まれる事を願い、さらに音楽AIエンジニア教育事業に力を入れていきたいとあらためて思いました。

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

Adobe Animateで作ったCreateJS形式コンテンツをWebGL再生したい

はじめに

Flash、ついに終わりますね(悲しみ)

3年前、同じようなコンセプトでこんなものを作ったんですが、
Adobe Animateのバージョンが上がり、書き出されるコンテンツの構造が変わっていたりするので

Creap.jsの進化版
Adobe Animate製CreateJSコンテンツを、pixi.jsで再生するプラグイン
Pixim-animate-player

を改めて作ってみました。

目指すのはCreap.jsと同様

  • アニメーションをAnimateで作りたい
  • でも動作速度は改善したい

です。

諸注意

  • このPixim-animate-playerですが、
    名前の頭にもあるようにPixim.js用のプラグインとして制作しています。
    Pixim.jsというのは、pixi.jsをラップして使いやすくしたものと思ってください。
    pixi.js用のビルドもありますが、もし興味があればPixim.jsも触ってみていただけると嬉しいです。
    ※以降はpixi.js向けビルドで説明します。

  • Adobe Animate 20.02のみで動作を確認しています。(2020/09/07時点)
    書き出されるコンテンツの構造が同様であれば、前後のバージョンでも動作するはずですが
    どこから構造が切り替わったか分からないので、とりあえず上記バージョンのみ対応としています。

  • 複雑なことをやっているコンテンツの場合、正常に動作しない可能性があります。
    特にインタラクションイベント周りはかなり怪しい気がしてます。
    もし動かないパターンがあった場合、教えていただけると助かります。

ドキュメントとサンプル

https://tawaship.github.io/Pixim-animate-player/

使いかた

用意するもの

  • CreateJS 1.0.0
  • pixi.js 5.3.2
  • pixi-animate-player 1.0.3
  • Adobe Animateでパブリッシュしたコンテンツ一式

ファイルの読み込み

<script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
<script src="pixi.5.3.2.min.js"></script>
<script src="pixi-animate-player.min.js"></script>
<script src="content/content.js"></script> <!-- Adobe Animateでパブリッシュしたコンテンツ一式のjsファイル -->
  1. 最初にpixi.js及びCreateJS(順不同)
  2. 次にpixi-animate-player
  3. 最後にcontent/content.js

という順で読み込むようにしてください。

実行

var player = new PIXI.animate.Player(
    "[conposition id]", // "lib.properties.id" in Animate content.
    "[root class name]", // Root class name of Animate content.
    "[content directory path]", // Directory path of Animate content.
    {
        antialias: true
    } // Options of PIXI.Application.
);

player
    .prepareAsync({
        useSynchedTimeline: true,
        crossOrigin: false
    })
    .then(function() {
        player.play();
    });
  • [conposition id]
    content.js内でlib.properties.idとして定義されている32文字くらいの文字列。

  • [root class name]
    rootに該当するMovieClipクラス名。
    Animateが勝手に付けるので、大体はflaファイルと同名になっているはず。
    (content.flaであれば content

  • [content directory path]
    「Adobe Animateでパブリッシュしたコンテンツ一式ディレクトリ」の
    HTMLから見た相対パス、または絶対パスのいずれか。
    絶対パスを使用、かつアセットが別サーバにある場合は、player.prepareAsyncの引数のcrossOrigintrueにする。

プラグイン自体にPromise polyfillは含めていないので、
必要があれば別途読み込んでください。

パフォーマンス

ここBenchmarksを見てもらえると分かりますが、
ざっくりと重い処理になればなるほど効果が高いといって差し支えないかなと思います。

例として、私のPCでのFPSは

CreateJS pixi-animate-player(webGL) pixi-animate-player(canvas)
100インスタンス 60 60 60
1000インスタンス 52 60 56
1000インスタンス(着色) 42 58 -
5000インスタンス 12 25 14

のような感じで、基本的にはwebGLの力での高速化が大きく、
加えてpixi.jsのレンダリング処理の最適化も若干利いているという感じでしょうか。

本来pixi.jsであれば、5000インスタンス程度ではFPSは下がらないんですが、
タイムライン+入れ子という構造が処理に強く影響しているような印象です。

仕組み

ここからは内部構造についてちょっと。
使えればいいよって方は読み飛ばしてください。

処理フロー

CreateJSの処理フローはざっくりと

  1. アセットのプリロードやらをして
  2. ティッカーが回って
  3. プロパティが変化して
  4. レンダリングされて
  5. 2に戻る

animate.png

のような形です。

pixi-animate-playerでは、このフローが

  1. CreateJSのアセットのプリロードやらをしつつpixi.jsの準備をして
  2. CreateJSのティッカーが回って
  3. CreateJSのプロパティが変化すると同時にpixi.jsのプロパティも変化して
  4. pixi.jsでレンダリングされて
  5. 2に戻る

core.png

のようになっています。

ユーザスクリプト(flaファイル上でタイムラインに実装するスクリプト)は、
そのほとんどが描画内容を変化させるために実装しているものですので、
最終的な描画内容が確定するまではCreateJSに任せてしまい、
レンダリングだけpixi.jsで横取りすればいいや、という考え方です。

本来はCreateJSを完全にエミュレートした方が処理量が少なくなるのですが、
全てのクラスを完全に実装しない限り、ユーザスクリプトに対応しきれない部分があったので
このような形を採っています。

唯一、ユーザのインタラクションイベント(タップなど)だけは、
イベントを受けるのがpixi.jsのオブジェクトになるため、ひと手間加えた処理をしています。

プラグインの構造

上記フローを実現するために

  • createjs.DisplayObject継承クラスのインスタンス1つ(以下A)につき
    PIXI.DisplayObject継承クラスのインスタンスを1つ(以下B)、対になるように生成、
    (e.g. createjs.MovieClip -> PIXI.Container)

  • Aのプロパティが変化すると同時に、Bの対応するプロパティを変化させる
    (e.g.A.scaleX -> B.scale.x)

  • Bが受けたインタラクションを、Aに対するインタラクションとして処理する
    (e.g. ユーザアクション -> B.emit('pointerdown') -> A.dispatchEvent('mousedown'))
    ※これはイメージで、実際には登録されたハンドラを実行しているだけ

といったことをしています。

おわりに

端末の性能も向上してきたので、Animate製CreateJSコンテンツそのまま再生でも問題ないかもなーと思いつつ
どこかで何かの役に立てばいいなと。

AnimateからのwebGL書き出しはいつになったらスクリプト対応するんですかね?

みんなもAnimate使ってね!

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

JavaScriptでアクセス元のiOSかAndroidかを判定する

はじめに

本関数の内容

  • ES2015のアロー関数を使用
  • ブラウザでWebページを表示時にアクセス元のOSをユーザエージェントを元に判定する
  • 正規表現を使ったiOS, Android, その他のOSを反映

各OSのユーザエージェントはこちらを参考にした

モバイルOSの判定する関数

const getMobileOS = () => {
  const ua = navigator.userAgent
  if (/android/i.test(ua)) {
    return "Android"
  }
  if (/iPad|iPhone|iPod/.test(ua)) {
    return "iOS"
  }
  return "Other"
}

使い方

const os = getMobileOS()
# console.log(os) => 'Android'
# console.log(os) => 'iOS'
# console.log(os) => 'Other'

補足 正規表現の説明

説明した関数の中で使用している/android/i.test(ua)を例に説明

  • /android/ が正規表現の本体
  • /android/i のiは正規表現のフラグで大文字小文字を判定しない
    • AndroidやANDROID,aNdRoIdに対応することになる
    • /android/iでは検証する文字列のどこかに大文字小文字を気にしないでandroidを含む文字列があるかどうかを判定する
  • /android/i.test(ua) のtestメソッドは引数の文字列uaに含まれる文字列が/android/iの正規表現にヒットするかどうかをtrue/falseで返す
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

p5.js で様々なパターンを描画してみた

1. はじめに

p5.jsとは、ProcessingをJavaScriptに移植したもので、
ビジュアルコーディングに特化したJavaScriptライブラリです。

p5.js 公式サイト
https://p5js.org/

今回は p5.jsを使用して、様々な模様を描いてみます。

2. 準備

まず最初に、p5.jsを使用するための準備をします。

2-1. p5.js 取得

p5.jsを使用するにあたって、htmlファイルに p5.jsを読み込ませる必要があります。

以下リンク先からp5.jsをダウンロードして読み込ませるか、CDNのリンクを貼り付けるか、どちらかの方法で、p5.jsを使用することができます。

p5.js: Download
https://p5js.org/download/

スクリーンショット 2020-05-15 17.39.23.png

以下の画面で、p5.jsをローカルにダウンロードする場合は[p5.js]を、CDNのリンクを取得する場合は[CDN]をクリックします。

スクリーンショット 2020-05-15 17.39.31.png

今回はCDNリンクを使用して、p5.jsを読み込ませます。

※ 以下リンク先のテンプレートを使用することもできます。

CodePen: p5.jsテンプレート
https://codepen.io/p5js/pen/wreBKy

2-2. htmlファイル 作成

以下のような内容の htmlファイル(index.html)を作成します。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Creative Coding</title>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>
    <script src="./js/sketch.js"></script>
</body>
</html>

p5.jsを使用するにあたって、
前の手順[2-1. p5.js 取得]で取得したp5.jsと sketch.js を読み込ませます。

    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>
    <script src="./js/sketch.js"></script>

p5.jsの読み込み にはCDNリンク(https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js) を使用します。

sketch.jsは、後の手順[2-3.jsファイル 作成] で配置します。

2-3. jsファイル 作成

描画処理を記述するjsファイル sketch.js を作成します。

下記に sketch.js の雛形を示します。

sketch.js
function setup(){
  // 描画領域のセットアップ
  createCanvas(800, 500);  //サイズ: 800px × 500px
  background(0);           //背景色: 黒
}

function draw(){
  // 描画
}

jsファイルは大まかに、setup関数draw関数の2つで構成されます。

関数 説明
setup() 最初に1回実行される。主に描画領域のセットアップに使用される。
draw() setup関数の後、繰り返し実行される。

今回は、p5.jsを使用して模様を作成するために、
setup関数に描画領域を設定し、
draw関数に模様を描画する処理を記述します。

・ setup関数

まずsetup関数にて、
createCanvas関数で描画領域(キャンバス要素)を作成します。

また、画面に描画領域をわかりやすく表示するため、
background関数で描画領域の背景色を設定します。

関数 説明
createCanvas() キャンバス要素を作成する。サイズはピクセル単位で設定する。
background() キャンバスの背景に使用される色を設定する。デフォルトの背景は透明。

・ draw関数

draw関数には、後ほど 様々な模様を描く処理を記述します。

・ ブラウザ表示

index.htmlをWebブラウザで開くと、以下のような画面が表示されます。

スクリーンショット 2020-05-01 18.25.49.png

setup関数で設定した通り、サイズ:800px × 500px、背景色:黒の 描画領域が表示されます。

3. コーディング

3-1. ボーダー

p5.jsを使用して、ボーダー柄を描きます。

3-1-1. サンプル

See the Pen p5.js_Stripe1-1 by Haruka Ogawa (@haruka0121) on CodePen.

3-1-2. 解説

以下に、ボーダー柄を描くための sketch.jsの内容を示します。

sketch.js
function setup() {
  createCanvas(800, 500); //サイズ: 800px × 500px
  background(0);          //背景色: 黒
}

function draw(){ 
  let border_width = 30;  //ボーダーの間隔
  for(let i=0; i<height; i+=border_width*2){
    strokeWeight(border_width); //ストローク幅: ボーダーの間隔(stripe_width)
    stroke('#66CDAA');          //ストローク色: グリーン
    line(0, i, width, i);
  }
}

・ setup関数内 記述

setup()には、サイズ 800px × 500px、背景色 黒の 描画領域を設定します。

function setup() {
  createCanvas(800, 500); //サイズ: 800px × 500px
  background(0);          //背景色: 黒
}

・ draw関数内 記述

draw()には、ボーダー柄を描くための処理を記述します。

function draw(){ 
  let border_width = 30;
  for(let i=0; i<height; i+=border_width*2){
    strokeWeight(border_width);
    stroke('#66CDAA');
    line(0, i, width, i);
  }
}

ループ処理を使って、線を一定間隔で描画することで、ボーダー柄を作ります。

image.png

使用した関数は、以下の通りです。

関数 説明
strokeWeight() 図形のストローク幅を設定する。
stroke() 図形のストロークに使用する色を設定する。
line() 線(2点間の直接パス)を描画する。
  • strokeWeight()

strokeWeight()でストロークの幅を設定することができます。
strokeWeight()の構文は以下の通りです。

strokeWeight(weight)

weightはストロークの太さで、ピクセル単位で指定します。

[参考]
p5.js Reference: strokeWeight()
https://p5js.org/reference/#/p5/strokeWeight

  • stroke()

stroke()でストロークの色を設定することができます。
stroke()の構文は以下の通りです。

stroke(value)

valueはストロークの色で、ここではカラーコードで指定します。

[参考]
p5.js Reference: stroke()
https://p5js.org/reference/#/p5/stroke

  • line()

line()で 始点・終点を指定し、線を描画することができます。
line()の構文は以下の通りです。

line(x1, y1, x2, y2)

x1, y1は 始点のx, y座標、x2, y2は 終点のx, y座標 を指定します。

[参考]
p5.js Reference: line()
https://p5js.org/reference/#/p5/line

3-2. ストライプ

p5.jsを使用して、ストライプ柄を描きます。

3-2-1. サンプル

See the Pen p5.js_Stripe2 by Haruka Ogawa (@haruka0121) on CodePen.

3-2-2. 解説

以下に、ストライプ柄を描くための sketch.jsの内容を示します。

skech.js
function setup() {
  createCanvas(800, 500);  //サイズ: 800px × 500px
  background('#6666FF');   //背景色: ブルー(#6666FF)
}

function draw(){
  let stripe_width = 3;  //ストライプの間隔
  for(let i=0; i<width; i+=stripe_width*5){
    noStroke(); //ストローク無効化
    fill(250);  //図形の塗り潰し: 白
    rect(i, 0, stripe_width, height);
  }
}

・ setup関数内 記述

setup()には、サイズ 800px × 500px、背景色 青(#6666FF)の 描画領域を設定します。

function setup() {
  createCanvas(800, 500);  //サイズ: 800px × 500px
  background('#6666FF');   //背景色: ブルー(#6666FF)
}

・ draw関数内 記述

draw()には、ストライプ柄を描くための処理を記述します。

function draw(){
  let stripe_width = 3;  //ストライプの間隔
  for(let i=0; i<width; i+=stripe_width*5){
    noStroke();  //ストローク無効化
    fill(250);   //図形の塗り潰し: 白
    rect(i, 0, stripe_width, height);
  }
}

ループ処理を使って、長方形を一定間隔で描画することで、ストライプ柄を作ります。

image.png

使用した関数は、以下の通りです。

関数 説明
noStroke() ストロークの描画を無効にする。
fill() 図形を塗りつぶす色を設定する。
rect() 長方形を描画する。
  • noStroke()

noStroke()でストロークの描画を無効にすることができます。
noStroke()の構文は以下の通りです。

noStroke()

[参考]
p5.js Reference: noStroke()
https://p5js.org/reference/#/p5/noStroke

  • fill()

fill()で図形を塗りつぶす色を設定することができます。
fill()の構文は以下の通りです。

fill(value)

[参考]
p5.js Reference: fill()
https://p5js.org/reference/#/p5/fill

  • rect()

rect()で 長方形を描画することができます。
rect()の構文は以下の通りです。

rect(x, y, w, [h])

x,yは長方形のx,y 座標、
wは長方形の幅、
h (オプション)は長方形の高さ
を指定します。

[参考]
p5.js Reference: rect()
https://p5js.org/reference/#/p5/rect

3-3. 市松模様

p5.jsを使用して、市松模様を描きます。

3-3-1. サンプル

See the Pen p5.js_Checkered pattern by Haruka Ogawa (@haruka0121) on CodePen.

3-3-2. 解説

以下に、市松模様を描くための sketch.jsの内容を示します。

sketch.js
function setup() {
    createCanvas(800, 500); //サイズ: 800px × 500px
    background(0);          //背景色: 黒  
}

function draw(){             
  const step = 30; //各ブロックの間隔

  for (let y = 0; y <= height; y += step) {
    if(y % (step*2) == 0){  // y の値が (step*2) で割り切れる時
      for (let x = step ; x <= width; x += step*2) {
        noStroke();              //ストローク無効化
        fill(255,0,0);           //図形塗り潰し: 赤
        rect(x, y, step, step);  //四角形描画
      }
    }else{  // y の値が (step*2) で割り切れない時
      for (let x = 0; x <= width; x += step*2) {
        noStroke();              //ストローク無効化
        fill(255,0,0);           //図形塗り潰し: 赤
        rect(x, y, step, step);  //四角形描画
      }
    }
  }
}

・ setup関数内 記述

setup()には、サイズ 800px × 500px、背景色 黒の 描画領域を設定します。

function setup() {
    createCanvas(800, 500); //サイズ: 800px × 500px
    background(0);          //背景色: 黒  
}

・ draw関数内 記述

draw()には、市松模様描くための処理を記述します。

function draw(){             
  const step = 30; //各ブロックの間隔

  for (let y = 0; y <= height; y += step) {
    if(y % (step*2) == 0){  // y の値が (step*2) で割り切れる時
      for (let x = step ; x <= width; x += step*2) {
        noStroke();              //ストローク無効化
        fill(255,0,0);           //図形塗り潰し: 赤
        rect(x, y, step, step);  //四角形描画
      }
    }else{  // y の値が (step*2) で割り切れない時
      for (let x = 0; x <= width; x += step*2) {
        noStroke();              //ストローク無効化 
        fill(255,0,0);           //図形塗り潰し: 赤
        rect(x, y, step, step);  //四角形描画
      }
    }
  }
}

ここでもループ処理を使って、正方形を一定間隔を開けて描画します。
市松模様にするために、間隔のあけ方を2パターン作り、
それぞれ交互に実行されるように条件文で制御します。

image.png

使用した関数は、以下の通りです。

関数 説明
noStroke() ストロークの描画を無効にする。
fill() 図形を塗りつぶす色を設定する。
rect() 長方形を描画する。
  • noStroke()

noStroke()でストロークの描画を無効にすることができます。
noStroke()の構文は以下の通りです。

noStroke()

[参考]
p5.js Reference: noStroke()
https://p5js.org/reference/#/p5/noStroke

  • fill()

fill()で図形を塗りつぶす色を設定することができます。
fill()の構文は以下の通りです。

fill(value)

[参考]
p5.js Reference: fill()
https://p5js.org/reference/#/p5/fill

  • rect()

rect()で 長方形を描画することができます。
rect()の構文は以下の通りです。

rect(x, y, w, [h])

x,yは長方形のx,y 座標、
wは長方形の幅、
h (オプション)は長方形の高さ
を指定します。

[参考]
p5.js Reference: rect()
https://p5js.org/reference/#/p5/rect

3-4. ギンガムチェック

p5.jsを使用して、ギンガムチェック を描きます。

3-4-1. サンプル

See the Pen p5.js_Gingham check by Haruka Ogawa (@haruka0121) on CodePen.

3-4-2. 解説

以下に、ギンガムチェック を描くための sketch.jsの内容を示します。

sketch.js
function setup() {
  createCanvas(800, 500); //サイズ
}

function draw(){ 
  background(255);  //背景色:白
  noStroke();       //ストロークの描画 無効化

  const step = 15; //チェック柄の間隔

  for (let y = 0; y <= height; y += step) {
    if(y % (step*2) == 0){
      for (let x = 0 ; x <= width; x += step) {
        if(x % (step*2) == 0){ 
          // 四角形(a) の描画
          fill('rgba(153,204,255,1)');     //図形の塗り潰し: 水色・不透明
          rect(x, y, step, step);          //四角形描画
        }else{ 
          // 四角形(b) の描画
          fill('rgba(153,204,255,0.65)');  //図形の塗り潰し: 水色・半透明
          rect(x, y, step, step);          //四角形描画
        }
      }
    }else{
      for (let x = 0; x <= width; x += step*2) {
        // 四角形(c) の描画
        fill('rgba(153,204,255,0.65)');    //図形の塗り潰し: 水色・半透明
        rect(x, y, step, step);            //四角形描画
      }
    }
  }
}

・ setup関数内 記述

setup()には、サイズ 800px × 500px の 描画領域を設定します。

function setup() {
    createCanvas(800, 500); //サイズ
}

・ draw関数内 記述

draw()には、ギンガムチェックを描くための処理を記述します。

function draw(){ 
  background(255);  //背景色:白
  noStroke();       //ストロークの描画 無効化

  const step = 15; //チェック柄の間隔

  for (let y = 0; y <= height; y += step) {
    if(y % (step*2) == 0){
      for (let x = 0 ; x <= width; x += step) {
        if(x % (step*2) == 0){ 
          // 四角形(a) の描画
          fill('rgba(153,204,255,1)');     //図形の塗り潰し: 水色・不透明
          rect(x, y, step, step);          //四角形描画
        }else{ 
          // 四角形(b) の描画
          fill('rgba(153,204,255,0.65)');  //図形の塗り潰し: 水色・半透明
          rect(x, y, step, step);          //四角形描画
        }
      }
    }else{
      for (let x = 0; x <= width; x += step*2) {
        // 四角形(c) の描画
        fill('rgba(153,204,255,0.65)');    //図形の塗り潰し: 水色・半透明
        rect(x, y, step, step);            //四角形描画
      }
    }
  }
}

ここでは setup関数ではなく、draw関数内で背景色を設定(background関数)します。

ループ処理を使って、正方形を一定間隔を開けて描画します。
正方形の色は3色使用し、
条件文を使って、一定のルールでそれぞれ描画するように制御します。

image.png

使用した関数は、以下の通りです。

関数 説明
noStroke() ストロークの描画を無効にする。
fill() 図形を塗りつぶす色を設定する。
rect() 長方形を描画する。
  • noStroke()

noStroke()でストロークの描画を無効にすることができます。
noStroke()の構文は以下の通りです。

noStroke()

p5.js Reference: noStroke()
https://p5js.org/reference/#/p5/noStroke

  • fill()

fill()で図形を塗りつぶす色を設定することができます。
fill()の構文は以下の通りです。

fill(v1, v2, v3, [alpha])

v1v2v3はRGB値、
alphaは透明度
を指定します。

[参考]
p5.js Reference: fill()
https://p5js.org/reference/#/p5/fill

  • rect()

rect()で 長方形を描画することができます。
rect()の構文は以下の通りです。

rect(x, y, w, [h])

x,yは長方形のx,y 座標、
wは長方形の幅、
h (オプション)は長方形の高さ
を指定します。

[参考]
p5.js Reference: rect()
https://p5js.org/reference/#/p5/rect

3-5. ドット

p5.jsを使用して、ドット柄を描きます。

3-5-1. サンプル

See the Pen p5.js_Dot pattern by Haruka Ogawa (@haruka0121) on CodePen.

3-5-2. 解説

以下に、ドット柄を描くための sketch.jsの内容を示します。

sketch.js
function setup() {
    createCanvas(800, 500); //サイズ
    background('red');      //背景色: レッド
}

function draw(){
    noStroke();       //ストローク無効化        

    const step = 50; 

    for (let y = 0; y <= height; y += step) {
        if(y % (step*2) == 0){
            for (let x = step/2 ; x <= width; x += step) {
                const size = step/2;        //円のサイズ: step/2
                fill(255);                  //図形の塗り潰し: 白
                ellipse(x, y, size, size);  //楕円描画: 中心(x, y), 幅size, 高さsize
            }
        }else{
            for (let x = 0; x <= width; x += step) {
                const size = step/2;        //円のサイズ: step/2
                fill(255);                  //図形の塗り潰し: 白
                ellipse(x, y, size, size);  //楕円描画: 中心(x, y), 幅size, 高さsize
            }
        }
    }
}

・ setup関数内 記述

setup()には、サイズ 800px × 500px、背景色 レッド('red')の 描画領域を設定します。

function setup() {
    createCanvas(800, 500); //サイズ
    background('red');      //背景色: レッド
}

・ draw関数内 記述

draw()には、ドット柄を描くための処理を記述します。

function draw(){
    noStroke();       //ストローク無効化            

    const step = 50;

    for (let y = 0; y <= height; y += step) {
        if(y % (step*2) == 0){
            for (let x = step/2 ; x <= width; x += step) {
                const size = step/2;        //円のサイズ: step/2
                fill(255);                  //図形の塗り潰し: 白
                ellipse(x, y, size, size);  //楕円描画: 中心(x, y), 幅size, 高さsize
            }
        }else{
            for (let x = 0; x <= width; x += step) {
                const size = step/2;        //円のサイズ: step/2
                fill(255);                  //図形の塗り潰し: 白
                ellipse(x, y, size, size);  //楕円描画: 中心(x, y), 幅size, 高さsize
            }
        }
    }
}

ループ処理を使って、正方形を一定間隔を開けて描画します。
市松模様にするために、間隔の開け方を2パターン作り、
それぞれ交互に実行されるように条件文で制御します。

image.png

使用した関数は、以下の通りです。

関数 説明
noStroke() ストロークの描画を無効にする。
fill() 図形を塗りつぶす色を設定する。
ellipse() 楕円を描画する。
  • noStroke()

noStroke()でストロークの描画を無効にすることができます。
noStroke()の構文は以下の通りです。

noStroke()

p5.js Reference: noStroke()
https://p5js.org/reference/#/p5/noStroke

  • fill()

fill()で図形を塗りつぶす色を設定することができます。
fill()の構文は以下の通りです。

fill(value)

p5.js Reference: fill()
https://p5js.org/reference/#/p5/fill

  • ellipse()

ellipse()で楕円を描画することができます。
ellipse()の構文は以下の通りです。

ellipse(x, y, w, [h])

x, yは 楕円の中心のx, y座標、
wは楕円の幅、
h(オプション)は楕円の高さ
を指定します。

p5.js Reference: ellipse()
https://p5js.org/reference/#/p5/ellipse

4. おわりに

今回は、p5.jsを使用して様々な模様を描いてみました。

ここでは ボーダー柄はline()、ストライプ柄はrect()を使って描画していますが、
逆にボーダー柄をrect()、ストライプ柄をline()で描画することも可能ですし、
同じ模様でも パターンの作成方法はたくさんあると思います。

p5.js 公式サイトを見ると、線・四角形・円形以外にも 様々な図形が描けるようですので、
組み合わせ次第で いろんなパターンが作成できると思います。

参考情報

p5.js 公式サイト
https://p5js.org/

p5.js overview
https://github.com/processing/p5.js/wiki/p5.js-overview

p5.js:Examples
https://p5js.org/examples/

ホームページ 作成、運営管理ガイド:カラーコード一覧表
http://www.netyasun.com/home/color.html

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

Promiseを受け取るelement.innerHTMLで非同期処理を隠蔽

非同期処理を同期処理のように書きたい

というニーズがあるらしい。確かに、promise.then()awaitを何回も書くのが煩わしいという気持ちは分かる。
そこで、promise.then()awaitを使わずに、普通の代入文のみで非同期処理を書いてみようというのがこの記事の趣旨。

方法1:代入式の右辺が必ず値を返すようにする

例えば「promiseが解決されていれば値を返し、解決されていなければpromiseをthrowする」という方法がある。Reactで使われているらしい。よく知らない。

方法2:代入式の左辺がPromiseを受け取れるようにする

こっちが本題。
fetchやら何やらで値を取ってきて処理するとして、その値は最終的にHTMLの書き換えに使われる(ことが多い)はず。
というわけで、Element.prototype.innerHTMLを書き換えてpromiseを受け取れるようにする

実装

Element.prototype.innerHTMLの書き換えについてはこちらのサイトを参考にさせて頂いた。
Promiseが解決されるまでは「loading...」と表示する。

{
const desc = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML')
Object.defineProperty(Element.prototype, "innerHTML", {
  get: function(){
    return desc.get.call(this)
  },
  set: function(x){
    if (x instanceof Promise || (x && typeof x.then === 'function')) {
      //Promiseだった場合、解決されるまで'loading...'を表示する
      desc.set.call(this, 'loading...')
      x.then(val=>desc.set.call(this, val))
    } else {
      //Promise以外の場合、そのまま表示する
      desc.set.call(this, x)
    }
  }
})
}

使用例

//1秒後に解決されるPromiseをinnerHTMLに代入
document.body.innerHTML = new Promise(resolve=>{
  setTimeout(_=>resolve('resolveされました'), 1000)
})

これを実行すると、まず「loading...」が表示され、1秒後に「resolveされました」が表示される。

なお、実際に使うときは、
- promise内でエラーが出た場合(promise.catch())
- element.innerTextやelement.insertAdjacentHTML
への対応が必要だと思われる。

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

【GAS】自動で別のスプレッドシートからコピペしてくれるボタンが欲しかった

経緯

【Exel】横書きで書かれた諸々を関数で縦書きの脚本にしたかった

の記事に書いた通り実装してみたものの、もはやコピペする作業すらめんどくさいのではないかと思った。
なので、上記記事の別バージョンとして、ワンタッチ式台本化ツールを作ることにした。

やりたいこと

  1. B2セルに台本化したい漫画の仕様書のURLを書く
  2. B3セルに台本化したいシート名を書く
  3. ボタンを押したら台本が出来上がる

仕組み

経緯にある通り、すでに基礎はできている。
要は、元となる仕様書を全部コピってきて、台本化してくれるシートに張り付けてくれればいい。

作りたいものは
コピー元のシートを全コピーして、コピー先のシートに張り付けてくれるスクリプト

実装

そんなわけで、作りました。
こちらも大体コピペさせていただきました。

GASで別スプレッドシートの値のある全範囲をコピー&ペーストする方法

ありがとうございます。

gas
/*
*
*メイン処理
*仕様書全部コピってペーストしたい
*台本化はシート側の関数でやってくれる
*
*/

function getScenario() {
 var sv = SpreadsheetApp.getActiveSpreadsheet();//このスクリプトが反映されてるスプレッドシートから
 var sheet = sv.getSheetByName("swich");//swichシートを選んで
 var sheet_name = sheet.getRange(3, 2).getDisplayValue();//B3セルに記入された文字列を取得
 var adress = sheet.getRange(2, 2).getDisplayValue();//B2セルに記入されたurlを取得
 var ss_copyFrom = SpreadsheetApp.openByUrl(adress); //コピー元のマスターデータのあるスプレッドシートキー
 var ss_copyTo = SpreadsheetApp.getActiveSpreadsheet();
 var sheet_copyFrom = ss_copyFrom.getSheetByName(sheet_name); //コピー元のスプレッドシートの値を抜き出したいシート名
 var sheet_copyTo = ss_copyTo.getSheetByName('change_scenario'); //ペーストする自分のスプレッドシートのシート名

  sheet_copyTo.clear();// 一旦コピペ先のシートをクリア。

 var lastRow = sheet_copyFrom.getLastRow(); //最終行を取得
 var lastColumn = sheet_copyFrom.getLastColumn(); //最終列を取得

 var copyValue = sheet_copyFrom.getRange(1,1,lastRow,lastColumn).getValues(); //コピー元のシートから値の入っている全範囲をコピー
  sheet_copyTo.getRange(1,1,lastRow,lastColumn).setValues(copyValue); //自分のシートにコピーした値を全範囲をペースト
}


このスクリプトを反映させたボタンを置けばOK。
他の人が使うときは、初回のみ許可するくだりがあるので注意?が必要。

参考

GASで別スプレッドシートの値のある全範囲をコピー&ペーストする方法
スプレッドシートにgasを設定したボタンを置いて実行

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

表示言語を切り替えるブックマークレット

概要

ブラウザで開いているページの URL 中の以下の文字列を置換するもの。

  • ja-jp <-> en-us
  • ja_jp <-> en_us

*こちらで紹介されているスクリプトを一部改変させていただきました。
https://qiita.com/ishiayaya/items/8e7a330b729c9ef2f074

できること

公式ドキュメント等の日本語版 <-> 英語版をワンクリックで切り替えられることがある
(特に Microsoft Docs 向け)

*そもそもブックマークレットとは

ブラウザ内で javascript をプチ実行できる機能。

手順

以下のスクリプトをコピー。

javascript:(function(){a='ja-jp';b='en-us';c='ja_jp';d='en_us';if(document.location.href.indexOf(a)>=0){location.href=document.location.href.replace(a,b);}else if(document.location.href.indexOf(b)>=0){location.href=document.location.href.replace(b,a);}else if(document.location.href.indexOf(c)>=0){location.href=document.location.href.replace(c,d);}else{location.href=document.location.href.replace(d,c);}})(location.href);

ブックマークマネージャで「新しいブックマークを追加」し、URLの欄にペースト。
bmlet_1.jpg

保存するとこんな感じで表示される。
bmlet_2.jpg

スクリプトを整形するならこう?

javascript:(
    function(){
        a='ja-jp';b='en-us';c='ja_jp';d='en_us';
        if(document.location.href.indexOf(a)>=0){
            location.href=document.location.href.replace(a,b);
        }else if(document.location.href.indexOf(b)>=0){
            location.href=document.location.href.replace(b,a);
        }else if(document.location.href.indexOf(c)>=0){
            location.href=document.location.href.replace(c,d);
        }else{
            location.href=document.location.href.replace(d,c);
        }
    }
)(location.href);

どうなるのか

ja-jp <-> en-us の切り替え

  1. Microsoft Azure のドキュメント。英語版が表示されている。
    bmlet_3.jpg

  2. ブックマークレットをクリックして実行すると、日本語版のページに切り替わる。
    (URL 中の "en-us""ja-jp" に置き換わった)
    bmlet_4.jpg

ja_jp <-> en_us の切り替え

  1. AWS のドキュメント。日本語版が表示されている。
    bmlet_5.jpg

  2. ブックマークレットをクリックして実行すると、英語版のページに切り替わる。
    (URL 中の "ja_jp""en_us" に置き換わった)
    bmlet_6.jpg

*ブックマークレットを実行するたびに 1, 2 が切り替わる。

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

To Do リスト 【JavaScript】

ポイント:point_up:

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

MicroCMS 入力フォーム一覧

プラン契約後にしか使用できないフォームもあるので後日追記予定

テキストフィールド

自由入力の1行テキストです。タイトル等に適しています。
inputform_testtesttest.microcms.io_create-api (1).png

テキストエリア

自由入力の複数行テキストです。プレーンテキストによる入力となります。
inputform_testtesttest.microcms.io_create-api.png

リッチエディタ

自由入力の複数行テキストです。リッチエディタによる編集が可能です。APIからHTMLが取得できます。
inputform_testtesttest.microcms.io_create-api (2).png

画像

画像用のフィールドです。APIからは画像URLが返却されます。
inputform_testtesttest.microcms.io_create-api (3).png
inputform_testtesttest.microcms.io_create-api (5).png

日時

Date型のフィールドです。カレンダーから日時を選択することができます。
inputform_testtesttest.microcms.io_create-api (4).png

真偽値

Boolean型のフィールドです。スイッチでオン/オフを切り替えることができます。
inputform_testtesttest.microcms.io_create-api (6).png

セレクトフィールド

定義したリストの中から値を選択するフィールドです。設定により複数選択も可能です。
inputform_testtesttest.microcms.io_create-api (6)のコピー.png

セレクトフィールド(複数選択可)

inputform_testtesttest.microcms.io_create-api (6)のコピー2.png

数字

Number型のフィールドです。入力時は数値型のキーボードが開きます。
inputform_testtesttest.microcms.io_create-api (6)のコピー3.png

レスポンス例

{
    "contents": [
        {
            "id": "fat97z0fc",
            "createdAt": "2020-09-07T05:00:34.844Z",
            "updatedAt": "2020-09-07T05:00:34.844Z",
            "publishedAt": "2020-09-07T05:00:34.844Z",
            "textfield": "自由入力の1行テキストです。タイトル等に適しています。",
            "textarea": "自由入力の複数行テキストです。\nプレーンテキストによる入力となります。",
            "richediter": "<p><strong>自由入力</strong>の複数行テキストです。<br><code>リッチエディタによる編集が可能です。</code><br><em>API</em>から<em>HTML</em>が<u>取得</u><s>できます。</s></p>",
            "image": {
                "url": "https://images.microcms-assets.io/protected/ap-northeast-1:33424e1d-3350-44a5-bf88-758450577d39/service/inputform_testtesttest/media/150x150.png"
            },
            "date": "2020-09-07T03:30:00.000Z",
            "boolean": true,
            "selectfield1": [
                "Item1"
            ],
            "selectfield2": [
                "Item1",
                "Item3"
            ],
            "number": 1234
        }
    ],
    "totalCount": 1,
    "offset": 0,
    "limit": 10
}

追記予定

  • ファイル
  • インラインフレーム
  • コンテンツ参照
  • 複数コンテンツ参照
  • カスタムフィールド
  • 繰り返しフィールド
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SvelteでMarkdown形式で投稿できるブログを1分で構築する

はじめに

嘘のようなタイトルですが、嘘ではありません!
解説はあとでするので早速、構築してみましょう!

構築

1.作業ディレクトリを作成し、作業ディレクトリに移動

mkdir svelte-md-blog
cd svelte-md-blog

2.Routifyのブログテンプレートからプロジェクトを作成

npx @sveltech/routify init --branch blog

3.プロジェクトをVScodeなどで開いて、mdファイルを追加する

src/blog/test.md
---
title: テスト
published: 2020-09-07T21:36:59.459Z
author: oe.kazuma
summary: |
  #### テスト
layout: blog
---

# {title}

### テスト投稿です。

4.サーバーを立ち上げて確認

npm run dev

ezgif-6-496affa2ec49.gif

1分で構築できましたね!
ほぼテンプレートのままなのでこのまま公開とは行きませんが、後はデザインの調整をすればそんなに時間かからずブログ公開できそうですね!

デプロイ

今回使用しているテンプレート内に既にNetlifyVercelへのデプロイ設定は内包されているので簡単にデプロイすることができます。
やり方は以下を参照
https://routify.dev/guide/starter-Template/deployment

解説

今回使用したテンプレートで主に使用されているライブラリを紹介します。

Svelte

svelte.png

VueやReactと違って仮想DOMがなく、ビルド時にピュアなJavaScriptにコンパイルしてくれるコンパイラー
もっと詳細が知りたい方は、僕が以前書いた記事で詳しく説明していますので参照ください。
君はVue,Reactの次に来るSvelteを知っているか?

Routify

Svelteのファイルシステムベースのルーティングライブラリ
ファイルを生成すると自動的にルーティングを作成してくれたり、SPA、SSR、PWA、メタタグ生成、コード分割など欲しい機能が詰まっているライブラリになってます。
Star数も執筆時点で700~800ほどなので非公式のSvelteのライブラリの中では人気なライブラリとなっています。
細かい設定などもでき、ドキュメントも充実しているので詳しくは以下を参照ください。
https://routify.dev/

MDsveX

MarkdownでSvelteコンポーネントを使用したり、SvelteコンポーネントでMarkdownを使用したりできるライブラリ
作者はSvelteにもコミットしているpngwnさんという方なので以下のサイトでもペンギンが可愛く登場してます。
https://mdsvex.com/

さいごに

今回はRoutifyが提供してくれているブログテンプレートに関して紹介しました。
このテンプレートではメタタグ生成などのSEO対策もしてくれていますので、自分のサイトに合わせて文言等を変えればSEO対策もバッチリです。
LightHouseを実行してみた結果、ほぼ全部満点でした!
RoutifyはすごくいいルーティングライブラリなのでまたQiitaで詳しく紹介したいと思ってます。

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

JavaScript~配列の操作いろいろ~

配列の操作をまとめました。
間違いがあったらご指摘いただけると嬉しいです。

使用する配列

const array1 = [1, 2, 3, 4, 5]
const array2 = [6, 7, 8, 9, 10]

※別の配列を使う場合はコード内に記載。

破壊的メソッド(元の配列を変更する)

push (末尾に追加)

const result = array1.push(6)//戻り値は配列の長さ
console.log(result) //6
console.log(array1) //[ 1, 2, 3, 4, 5, 6 ]
array1.push(array2)
console.log(result) //6
console.log(array1) //[ 1, 2, 3, 4, 5, [ 6, 7, 8, 9, 10 ] ]

unshift (先頭に追加)

const result = array1.unshift(0) //戻り値は配列の長さ
console.log(result) //6
console.log(array1) //[ 0, 1, 2, 3, 4, 5 ]
array1.unshift(array2)
console.log(result) //6
console.log(array1) //[ [ 6, 7, 8, 9, 10 ], 1, 2, 3, 4, 5 ]

pop (末尾を削除)

const result = array1.pop()//戻り値は削除した値
console.log(result)//5
console.log(array1)//[ 1, 2, 3, 4 ]

shift (先頭を削除)

const result = array1.shift()//戻り値は削除した値
console.log(result)//1
console.log(array1)//[ 2, 3, 4, 5 ]

splice (値の追加や削除)

const result1 = array1.splice(1, 2)//戻り値は削除した値
console.log(result1)//[ 2, 3 ]
console.log(array1)//[ 1, 4, 5 ]
const result2 = array1.splice(1, 2, "Hello", "Bye")
console.log(result2)//[ 2, 3 ]
console.log(array1)//[ 1, 'Hello', 'Bye', 4, 5 ]

第一引数、どこから。第二引数、削除する数。第三引数以降、追加する値。

copyWithin

const result1 = array1.copyWithin(1, 3)//[1]の(2)の場所に、[3]からの(4,5)をコピー
console.log(result1) //[1, 4, 5, 4, 5]
console.log(array1) //[1, 4, 5, 4, 5]

const result2 = array2.copyWithin(1, 2, 4)//[1]の(7)の場所に、[2]から[4]までの(8, 9)をコピー
console.log(result2) //[6, 8, 9, 9, 10 ]
console.log(array2) //[6, 8, 9, 9, 10 ]

第一引数、どこに。第二引数、どこから。第三引数(省略可能)、どこまで。

reverse (逆転)

const result = array1.reverse()
console.log(result) //[ 5, 4, 3, 2, 1 ]
console.log(array1) //[ 5, 4, 3, 2, 1 ]

sort (並び替え)

const array = [3, 5, 2, 1, 4]
const result = array.sort()
console.log(result)
console.log(array)
const users = [ {name: "J", age: 40}, {name: "A", age: 29}, {name: "M", age: 13}, {name: "T", age: 65}, {name: "B", age: 88}]
users.sort((a, b) => a.age - b.age)//ageで昇順
console.log(users)
/*[
    { name: 'M', age: 13 },
    { name: 'A', age: 29 },
    { name: 'J', age: 40 },
    { name: 'T', age: 65 },
    { name: 'B', age: 88 }
 ] */
users.sort((a, b) => b.age - a.age)//a,bを入れ替えると降順
console.log(users)
/*[
    { name: 'B', age: 88 },
    { name: 'T', age: 65 },
    { name: 'J', age: 40 },
    { name: 'A', age: 29 },
    { name: 'M', age: 13 }
  ]*/

nameでのソート

users.sort((a, b) => {
  if (a.name > b.name) {
    return 1
  } else {
    return -1
  }
})
console.log(users)
/*[
    { name: 'A', age: 29 },
    { name: 'B', age: 88 },
    { name: 'J', age: 40 },
    { name: 'M', age: 13 },
    { name: 'T', age: 65 }
 ]*/

fill (特定の値で埋める)

const array = new Array(10).fill(1)
console.log(array)
array.fill("a", 2)//[2]から"a"にする
console.log(array)//[ 1, 1, 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a' ]
array.fill("b", 3, 7)//[3]から[7]の前までを"b"にする
console.log(array) //[ 1, 1, 'a', 'b', 'b', 'b', 'b', 'a', 'a', 'a' ]

第一引数、入れる値。第二引数、どこから。第三引数、どこまで。

非破壊的メソッド(新しい配列を返す)

concat (配列の結合)

const result = array1.concat(array2)
console.log(array1)//[ 1, 2, 3, 4, 5 ]
console.log(result)//[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

基本的には下で紹介する、スプレッド演算子を使用

... スプレッド演算子 (配列の展開)

const result1 = [...array1, ...array2]
console.log(result1)//[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
const result2 = [ 0, ...result1, 11]
console.log(result2)//[ 0, 1, 2, 3, 4, 5, 6, 7,  8, 9, 10, 11 ]
console.log(array1)//[ 1, 2, 3, 4, 5 ]
console.log(array2)//[ 6, 7, 8, 9, 10 ]

配列に対して...を使う事で展開できる。

slice (部分配列)

const result1 = array1.slice(2)//第二引数を指定しなかった場合は指定した場所からの全ての値
const result2 = array1.slice(1, 4)//[1]から[4]まで
const result3 = array1.slice(-3, -1)//-を使うと最後からになる
console.log(result1)//[ 3, 4, 5 ]
console.log(result2)//[ 2, 3, 4 ]
console.log(result3)//[ 3, 4 ]
console.log(array1)//[ 1, 2, 3, 4, 5 ]

第一引数、どこから。第二引数(省略可能)、どこまで(指定した場所のひとつ前)。

以上!

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

HSL / HSV より人間に寄り添った色表現 CIE L*C*h を使いたい

CIE L*C*h カラーモデルベースの色相環カラーピッカーを作りました。
lch-color-wheel
https://luncheon.github.io/lch-color-wheel/

この記事はその経緯です。

HSL 色表現と色相変数

CSS で使える色表現の 1 つに HSL カラーモデル があります。 HSL では色相(Hue)、彩度(Saturation)、輝度(Lightness)の 3 軸で色を表現します。

色相を CSS カスタムプロパティ(変数)にすれば、彩度と輝度を固定して色相だけ変えることが簡単にできます。

.btn {
  background-color: hsl(var(--hue), 100%, 60%);
  border: 2px solid hsl(var(--hue), 100%, 40%);
}
.btn:hover {
  box-shadow: 0 0 8px hsl(var(--hue), 100%, 40%);
}

.outline {
  background: white;
  color: hsl(var(--hue), 100%, 40%);
}

.primary {
  --hue: 210;
}

.accent {
  --hue: 330;
}
<button type="button" class="btn primary">Primary</button>
<button type="button" class="btn accent">Accent</button>
<button type="button" class="btn primary outline">Primary Outline</button>
<button type="button" class="btn accent outline">Accent Outline</button>

スクリーンショット 2020-09-07 11.14.16.png
CodePen

それで、以前、色相環を利用したカラーピッカーを作りました。彩度と明るさを固定して色相を選ぶ、あるいは色相を固定して彩度と明るさを調整するのに最適化しています。(このカラーピッカーは HSL ではなく HSV カラーモデル を採用しています。 HSL モデルのカラーピッカーは私のメンタルモデルに合いませんでした。 HSL と HSV とで色相は同じ値になるので「彩度と明るさを固定して色相を選ぶ」という目的には適います。)
reinvented-color-wheel
https://luncheon.github.io/reinvented-color-wheel/

ところが HSL + 色相変数というアイディアは思ったほどうまくいきませんでした。

同じ輝度でも、黄色や黄緑は明るく、青や紫は暗く見えます。たとえば先ほどのボタンの例を .accent { --hue: 100; } にすると...
スクリーンショット 2020-09-07 11.10.19.png
同じ「明るさ」には感じられないと思います。黄緑のボタンの白い文字「Accent」はコントラストが弱くて非常に読みづらくなっています。

HSL / HSV カラーモデルでは、結局は色相に合わせて輝度 / 明度も調整する必要があるのです。
色相を変えるたびに明度の調整が必要となるようでは、カラーピッカーの使い勝手としてもよろしくありません。

CIE L*C*h カラーモデル

そんな私の悩みを解決するのが CIE L*C*h カラーモデルです。 L* は明度(Lightness)、 C* は彩度(Chroma)、 h は色相(hue)を表します。というとまるで HSL や HSV と同じように聞こえますが、 CIE L*C*h 色空間は人間の知覚に合わせて設計されているそうです。私は色に詳しいわけではないので詳しい解説はできませんが、実際に利用してみると使い勝手が全然違います。
CIE L*C*h 色空間は CIE L*a*b* 色空間から軸の取り方を変えた派生です。 CIE L*C*h の解説記事はあまり見当たりませんでした。

Wikipedia からこの記事にとって重要な部分を引用します。

RGBやCMYKとは異なり、Lab色空間は人間の視覚を近似するよう設計されている。知覚的均等性を重視しており、L成分値は人間の明度の知覚と極めて近い。したがって、カラーバランス調整を正確に行うために出力曲線を a および b の成分で表現したり、コントラストの調整のためにL成分を使ったりといった利用が可能である。

そんなわけで冒頭の話に戻るのですが、 CIE L*C*h カラーモデルベースの色相環カラーピッカーを作りました。
lch-color-wheel
https://luncheon.github.io/lch-color-wheel/

CIE L*C*h カラーモデルを採用したことで、知覚的な明るさを維持したまま色相を変えられるようになりました。プライマリカラーが決まっている状態でアクセントカラーを選ぶような用途では HSL や HSV カラーモデルのカラーピッカーより活躍すると思います。

CSS で CIE L*C*h 色表現が利用可能に(いずれなるかもしれない)

CSS にも lch() 色関数が提案されています。

CSS Color Module Level 4 (日本語訳)
https://triple-underscore.github.io/css-color-ja.html

色相を CSS カスタムプロパティにすれば、彩度と明度を固定して色相だけ変えることが簡単にできるようになるかもしれません。

さらには color-adjust(peru lightness -20%); のように色を調整できるようになるかもしれません。

CSS Color Module Level 5 (日本語訳)
https://triple-underscore.github.io/css-color5-ja.html

でもこれらはまだもう少し先の話。

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