- 投稿日:2021-04-03T23:49:02+09:00
SPAって何?
Reactを勉強しててSPAという言葉よく聞くようになり、なんとなくの理解だった為改めて文字に起こしてアウトプットしていきたいと思います!
一番下に参考にした記事を載せておきます!
SPAってなに?
SPAとはSingle Page Application(シングルページアプリケーション)のことです。
簡潔にまとめると、必要な部分だけ更新して、必要ないところはそのままの表示にすることです!今までのwebアプリケーションの仕組みは、何かアクション(クリックなど)をすると、
①サーバーにリクエスト
②サーバー側でHTMLを生成
③②で生成されたHTMLを受け取り、ブラウザで描画する
流れでした。従来の仕組みだと不要なデータまで更新される為、表示するまでに時間がかかっていました。
そこでSPAの登場!!
①ユーザーがアクションを起こす
②①のアクションに必要なものだけをサーバーにリクエスト
③帰ってきたデータをJSで処理してHTMLで表示する
流れに変わりました!SPAの仕組みを利用することでユーザーのサービス利用時間が長くなる=購入確率などが高くなる=会社の売り上げに貢献!!←少し飛躍しすぎました。。。
だから今SPAへの温度感が非常に高まってるとか。。。
その他メリット
他のメリットデメリットについては他記事でも乗ってるので興味のある方はそちらを参考にしてみてください!
ここではざっくり説明していきます①よりリットなweb表現ができる
ReactやVue.jsなどのライブラリやフレームワークが誕生したことによって、簡単に様々な物が実装可能なり、いろんなUIを表現することができるようになりました!
②ネイティブアプリの代用ができる
ここに関しては自分自身が体験してない為、あまり実感が湧きませんがSPAを導入することで
ネイティブアプリで実装されてたことがSPAでも表現することが可能とのことです!!←いずれここも体験してみたいです!最後に
どうやらSPAは何かを実装する上でデファクトスタンダードそう。これからもSPAを意識しながら学習に取り組んでいきたいと思います!!
参考記事
https://rara-world.com/spa-single_page-merit/
https://www.oro.com/ja/technology/001/
- 投稿日:2021-04-03T21:34:37+09:00
leaflet.jsで地図に円を描画
index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script> <title>Leaflet</title> </head> <body> <div id="map"></div> <script src="js/leaflet/map.js"></script> </body> </html>css/style.css#map { height: 98vh; }js/map.js/// <reference path="typings/index.d.ts" /> var map = L.map('map').setView([35, 135], 16); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); L.circle([35, 135], { color: 'red', fillColor: 'red', fillOpacity: 0.5, radius: 500 }).addTo(map);
- 投稿日:2021-04-03T21:22:27+09:00
leaflet.jsで地図にmarkerを置く
index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script> <title>Leaflet</title> </head> <body> <div id="map"></div> <script src="js/leaflet/map.js"></script> </body> </html>css/style.css#map { height: 98vh; }js/map.js/// <reference path="typings/index.d.ts" /> var map = L.map('map').setView([35, 135], 16); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); L.marker([35, 135]).addTo(map)
- 投稿日:2021-04-03T21:20:06+09:00
SATソルバ―で遊んでみた(8 Queens/数独)
はじめに
Kinx で Package Manager を作ろうと思い、バージョン依存性チェックを SAT ソルバ―にやらせようと思いました。しかし複数バージョン共存できる方が良いのでは、と、バージョン依存性チェックいらないかも、と思い始めた今日この頃。結局作ったけど使わないかも、な感じになってます。
とはいえ、せっかく実装したのでいくつか遊んでみました。8 Queens と数独を解いてみます。
ちなみに Kinx とは↓コレです。
- 参考
- 最初の動機 ... スクリプト言語 KINX(ご紹介)
- 個別記事へのリンクは全てここに集約してあります。
- リポジトリ ... https://github.com/Kray-G/kinx
- Pull Request 等お待ちしております。
また、最後に(これもせっかくなので)バージョン依存性チェックプログラムを載せておきます。使わないかもですが。
SAT ソルバ―とは
ご注意: SAT ソルバーは v1.0.0 のリリース版には含まれていません。最新の master ブランチに含まれています。
充足可能性問題を解く機械(プログラム)。Wikipedia が詳しいのでご参考。Kinx では picosat をバックエンドに使いました。
簡単に言うと、$(x_1 \lor x_2) \land (x_1 \lor \lnot x_2) \land (\lnot x_1 \lor \lnot x_2)$ みたいな論理式があって、この式全体が真となる各変数の真偽値の組合せを見つけるというものです。例えばこの例の場合、$x_1 = $ 真、$x_2 = $ 偽とすれば全体が真となるのでこの組合せは解の一つです。
SAT ソルバ―を使う、というのは、与えられた問題を上記のような論理式で表し、SAT ソルバーに解かせ、出てきた結果を解釈する、ということを行うこと、意味します。
ちなみに、論理式の形は $(A \lor B \lor \ldots) \land (X \lor Y \lor \ldots) \land ...$ と OR でつながった式を AND でつなげた式として表現します。また、ある命題を解く際に、$A \Rightarrow B$ ($A$ ならば $B$)の条件を考えるシーンがよくありますが、これは論理式として同一の解となる $\lnot A \lor B$ で置き換えて考えます。
Kinx で使うには、以下のように
usingします。また、ライブラリはSatisfiablityクラスで、インスタンス化しておきましょう。using SatisfiablitySolver; var s = new Satisfiablity();8 Queens を解いてみた
8 Queens を解いてみましょう。8 Queens に関しても Wikipedia が詳しいのでご参考。
SAT ソルバ―を使うということは、8 Queens の問題を論理式で表すということです。どう表せばよいでしょう。
SAT ソルバーでは、各変数を整数で管理します。そして、変数 1 が真の場合は +1、偽の場合は -1 として符号で判断できるようにします。従って、全ての状態を変数として番号を付け、ある変数(番号)が真(プラス)の時に他の変数(番号)がどうなるのかを論理式で表すようにします。こんな感じです。
- 変数
- すべてのマスを表す変数を仮定する。8 x 8 マスなので、64 個の変数がある。
- 変数が真の場合は Queen が存在するものとする。
- 条件(制約)
- 行(列でも良い)の中に少なくとも 1 つは Queen が存在する。
- ある $(i, j)$ 座標に Queen があるとした場合、その上下左右、ナナメ全てに Queen は存在しない。
この時、例えば最初の条件として 1 行目を考えます。1行目を表す変数を $x_1$, $x_2$, $x_3$, $\ldots$, $x_8$ とすると、$(x_1 \lor x_2 \lor x_3 \lor x_4 \lor x_5 \lor x_6 \lor x_7 \lor x_8)$ となります。2 ~ 8 行目も同じように条件が書けます。
プログラム的には、これを配列で表して、
[1, 2, 3, 4, 5, 6, 7, 8]と扱います。全部真なのでプラスです。この 1 つの OR の式を節(Clause)と言います。複数の節を作って条件を絞っていきます。まず、分かりやすくするために、ある $(i, j)$ の変数の番号を返す関数を作っておきましょう。ここで
Nは 8 です。+1するのは、変数は 1 始まりだからです。const N = 8; function getVarNum(i, j) { return (i + j * N) + 1; }では各行に少なくとも 1 つは Queen が存在する、というのを書くと以下のようになります。これで
[1, 2, 3, 4, 5, 6, 7, 8]~[57, 58, 59, 60, 61, 62, 63, 64]までの 8 つの節が登録されます。for (var jx = 0; jx < N; ++jx) { s.addClause(N.times().map { => getVarNum(_1, jx) }); }次の条件「ある $(i, j)$ 座標に Queen があるとした場合、その上下左右、ナナメ全てに Queen は存在しない。」はどう表しましょう。これは、$A \Rightarrow B$ の形で個別に指定していきます。例えば、「$(0, 0)$ にあれば $(1, 0)$ には存在しない」は変数で表すと、$x_1 \Rightarrow \lnot x_2$ なので、$\lnot x_1 \lor \lnot x_2$ です。つまり、節としては
[-1, -2]になります。すべてのマスで上下左右、ナナメそれぞれ同じように節を追加していきます。ところで、実は $x_1 \Rightarrow \lnot x_2$ は $x_2 \Rightarrow \lnot x_1$ と同じことを表しています。なぜなら OR は交換可能なのでどちらも $\lnot x_1 \lor \lnot x_2$ だからです。ということで、左上から順にスキャンする場合、あるマス $(i, j)$ の条件を調べる際は既に設定した節と同じものを設定する必要はありません。つまり、右、下、右下、左下、だけをスキャンして節を登録すればよいのです。
これをプログラムにすると、以下のようになります。これをすべてのマスで呼びます。
function addClauses(s, i, j) { const ijQ = getVarNum(i, j); /* right */ for (var ix = i + 1; ix < N; ++ix) { s.addClause([-ijQ, -getVarNum(ix, j)]); } /* down */ for (var jx = j + 1; jx < N; ++jx) { s.addClause([-ijQ, -getVarNum(i, jx)]); } /* right-down */ for (var ix = i + 1, jx = j + 1; ix < N && jx < N; ++ix, ++jx) { s.addClause([-ijQ, -getVarNum(ix, jx)]); } /* left-down */ for (var ix = i - 1, jx = j + 1; ix >= 0 && jx < N; --ix, ++jx) { s.addClause([-ijQ, -getVarNum(ix, jx)]); } } for (var ix = 0; ix < N; ++ix) { for (var jx = 0; jx < N; ++jx) { addClauses(s, ix, jx); } }さて、ここまで準備できたら SAT ソルバーから解を求めます。
Satisfiablityクラスはイテレータブルにしてあるので、以下のようにfor-inで回せます。function display(e) { System.print("\nSolution %d\n" % ++solution); var board = []; for (var ijQ = 0, l = e.length(); ijQ < l; ++ijQ) { var i = ijQ % N; var j = Integer.parseInt(ijQ / N); board[i][j] = e[ijQ] > 0; } for (var jx = 0; jx < N; ++jx) { for (var ix = 0; ix < N; ++ix) { System.print(board[ix][jx] ? " Q" : " ."); } System.println(""); } } for (var e in s) { display(e); }実行すると、無事 92 個の解が表示されました。
$ ./kinx examples/sat_nqueens.kx Solution 1 . . . . . . . Q . Q . . . . . . . . . . Q . . . . . Q . . . . . Q . . . . . . . . . . . . . Q . . . . Q . . . . . . . . . Q . . Solution 2 . . . Q . . . . . . . . . . . Q . . . . Q . . . . . Q . . . . . Q . . . . . . . . . . . . . Q . . Q . . . . . . . . . . . Q . . /* 省略 */ Solution 92 . . . Q . . . . . . . . . . Q . . . . . Q . . . . Q . . . . . . . . . . . Q . . Q . . . . . . . . . Q . . . . . . . . . . . . Q数独を解いてみた
次に数独を解いてみます。数独に関してもまた Wikipedia が詳しいのでご参考。
数独は変数の数が増えます。縦横に加えてマスごとに 1 ~ 9 までの数値を取るといった奥行的な(立体的な)イメージを持つ必要がありますが、考え方は一緒です。
各マスが 1 ~ 9 までの数値を取るので、マスごとに 9 個の状態を持たせます。つまり、左上のマスが 1 であるとき、2 であるとき、... と全て異なる変数として扱います。例えば、$R \times R$ の数独において $(i, j)$ が $v$ であるときの変数の番号を $(i + j \times R) \times R + v$ として定義します。$v$ が 1 から始まるので 1 加える必要はありません。例えば、変数 1 が真であれば、左上のマスは 1 です。変数 9 が真であれば、左上のマスは 9 であることを表します。変数 10 はマスが移って、左上の 1 つ右(左から 2 番目)のマスが 1 ということになります。
では早速先ほどと同様に、変数の番号を返す関数から定義してみましょう。$R \times R$ の数独です。
function getVarNum(i, j, v) { return ((i + j * R) * R) + v; }次に制約条件です。「ミニ区画」と言ってますが、$9 \times 9$ の数独の場合、その中に 9 つ存在する $3 \times 3$ の領域のことを意味しています。この 3 を $N$ とします。つまり、$R = N^{2}$ です。
- あるマス $(i, j)$ には 1 ~ 9 のいずれかの数値が入る。
- あるマス $(i, j)$ が $v$ であるとき、同じ $(i, j)$ のマスには $v$ 以外の数値は存在しない。
- あるマス $(i, j)$ が $v$ であるとき、同じ行に $v$ は存在しない。
- あるマス $(i, j)$ が $v$ であるとき、同じ列に $v$ は存在しない。
- あるマス $(i, j)$ が $v$ であるとき、そのマスを含むミニ区画に $v$ は存在しない。
これをプログラムで書くと、次のようになります。
function addClauses(s, i, j, v) { const ijv = getVarNum(i, j, v); // There is at most one number in each cell. (Cell - uniqueness) for (var vi = 1; vi <= R; ++vi) { if (v != vi) { s.addClause([-ijv, -getVarNum(i, j, vi)]); } } // Each number appears at most once in each row. (Row - uniqueness) for (var ix = i + 1; ix < R; ++ix) { s.addClause([-ijv, -getVarNum(ix, j, v)]); } // Each number appears at most once in each column. (Column - uniqueness) for (var jx = j + 1; jx < R; ++jx) { s.addClause([-ijv, -getVarNum(i, jx, v)]); } var si = Integer.parseInt(i / N) * N; // i as a left top in sub square. var sj = Integer.parseInt(j / N) * N; // j as a left top in sub square. var ei = si + N; var ej = sj + N; // Each number appears at most once in each block. (Block - uniqueness) for (var ix = si; ix < ei; ++ix) { for (var jx = sj; jx < ej; ++jx) { const ijv2 = getVarNum(ix, jx, v); if (ijv2 > ijv) { s.addClause([-ijv, -ijv2]); } } } } for (var j = 0; j < R; ++j) { for (var i = 0; i < R; ++i) { const ijv = ((i + j * R) * R); var v = test[j][i]; if (v == 0) { // There is at least one number in each cell. (Cell - definedness) s.addClause(R.times().map { => ijv + (_1 + 1) }); } else { s.addClause([ ijv + v ]); } R.times().each { => addClauses(s, i, j, (_1 + 1)) }; } }実際に $9 \times 9$ の数独を解いてみましょう。
+----------+----------+----------+ | 8 . . | . . . | . . . | | . . 3 | 6 . . | . . . | | . 7 . | . 9 . | 2 . . | +----------+----------+----------+ | . 5 . | . . 7 | . . . | | . . . | . 4 5 | 7 . . | | . . . | 1 . . | . 3 . | +----------+----------+----------+ | . . 1 | . . . | . 6 8 | | . . 8 | 5 . . | . 1 . | | . 9 . | . . . | 4 . . | +----------+----------+----------+↓
+----------+----------+----------+ | 8 1 2 | 7 5 3 | 6 4 9 | | 9 4 3 | 6 8 2 | 1 7 5 | | 6 7 5 | 4 9 1 | 2 8 3 | +----------+----------+----------+ | 1 5 4 | 2 3 7 | 8 9 6 | | 3 6 9 | 8 4 5 | 7 2 1 | | 2 8 7 | 1 6 9 | 5 3 4 | +----------+----------+----------+ | 5 2 1 | 9 7 4 | 3 6 8 | | 4 3 8 | 5 2 6 | 9 1 7 | | 7 9 6 | 3 1 8 | 4 5 2 | +----------+----------+----------+解けました。
ところが、これでもいいのですが、実は $25 \times 25$ の数独で返ってきません。実は冗長であっても節を増やしたほうが余計な条件を枝刈りして速く解を得ることができたりします。ここでは、以下の条件を追加してみます。
- 各数値は、少なくとも 1 つは各行に存在する。
- 各数値は、少なくとも 1 つは各列に存在する。
プログラムで書くとこんな感じです。
for (var v = 1; v <= R; ++v) { // Each number appears at least once in each row. (Row - definedness) for (var j = 0; j < R; ++j) { s.addClause(R.times().map { => getVarNum(_1, j, v) }); } // Each number appears at least once in each column. (Column - definedness) for (var i = 0; i < R; ++i) { s.addClause(R.times().map { => getVarNum(i, _1, v) }); } }これを加えると、$25 \times 25$ の数独も 1 秒未満で答えが得られました!
+----------------+----------------+----------------+----------------+----------------+ | . 12 . . . | . . . . . | . . . 9 . | . . 15 . . | 22 . . . . | | . . . . . | . 9 . 19 . | . . 10 11 . | . . . . . | . . . . . | | . 4 . 22 . | . . . . . | . . . . . | . . 12 . . | 20 15 1 . . | | 16 1 20 15 . | . . . . . | . . . . . | 14 . 4 . 22 | 12 25 . . . | | . . . . . | . 7 2 11 . | 23 . 19 8 . | . . . 13 . | . . . . . | +----------------+----------------+----------------+----------------+----------------+ | 13 . 8 . 2 | . . . . . | . . 7 23 6 | . 9 . 19 11 | . . . . . | | . . . . 23 | . . . . 16 | . . . . . | . . . . . | 1 . . . . | | 7 . . . 10 | 3 . . . . | . . 9 19 . | . 13 . 23 . | . . . 5 . | | . . . . . | 15 . . . 22 | . . . . . | . . . . . | 25 20 . . . | | . . . . . | 12 . 14 1 25 | . . . . . | . . 3 . . | 16 4 15 . . | +----------------+----------------+----------------+----------------+----------------+ | . . . . . | . 19 9 . . | . . 13 7 . | . . . 5 . | . . . 23 10 | | . 22 . 25 17 | . . . . . | . . . . . | 12 . 20 . . | . . . . . | | . 20 12 16 . | . . . . . | . . . . 14 | 15 22 1 . 25 | . . . . . | | . 15 . . . | . 11 . . . | . . . . . | . . 16 . . | . . . 9 . | | . . . 1 . | . 10 . 23 . | . . . . 18 | . . . . . | . . . . 8 | +----------------+----------------+----------------+----------------+----------------+ | 10 . . . 8 | . 13 . 5 . | . . . . . | . 19 . 11 23 | . . . 6 . | | . . . 17 7 | . . . . . | . . . . 1 | . . . . . | 4 22 . . . | | . . . . 11 | . 23 . . . | . . . . 20 | . . . 2 . | 14 . . . . | | 19 . 23 . 5 | . 8 . 9 . | . 21 . . . | . 10 . 7 . | . . . . . | | . 3 . . . | . . . . . | 25 4 . . 12 | . . . . . | 15 1 16 . . | +----------------+----------------+----------------+----------------+----------------+ | . . . . . | . . . . 15 | . 12 . . 25 | 1 . 22 . . | 3 . . . . | | 23 . . . 19 | . 2 . . . | . . . . . | . . . 10 . | . . . 7 11 | | . . . 18 . | . . . . . | . 20 . . . | . . . . . | . . . . . | | . . . . . | . . . . 4 | 14 15 . . 22 | . . . . . | . . . 10 . | | 11 . . . 9 | . . . . . | . . . . . | . . . . . | . . . 19 . | +----------------+----------------+----------------+----------------+----------------+↓
+----------------+----------------+----------------+----------------+----------------+ | 8 12 11 10 18 | 14 25 4 16 24 | 20 17 1 9 21 | 19 5 15 6 2 | 22 23 7 3 13 | | 2 23 13 7 25 | 22 9 15 19 20 | 12 24 10 11 4 | 17 16 18 1 3 | 8 6 5 14 21 | | 9 4 19 22 21 | 13 3 18 17 5 | 16 6 25 14 7 | 23 11 12 8 10 | 20 15 1 24 2 | | 16 1 20 15 24 | 10 21 6 8 23 | 5 2 18 3 13 | 14 7 4 9 22 | 12 25 11 17 19 | | 14 5 17 6 3 | 1 7 2 11 12 | 23 22 19 8 15 | 21 25 24 13 20 | 10 9 18 16 4 | +----------------+----------------+----------------+----------------+----------------+ | 13 25 8 4 2 | 18 20 21 24 10 | 15 1 7 23 6 | 16 9 5 19 11 | 17 3 14 22 12 | | 22 24 3 21 23 | 9 17 19 13 16 | 18 14 20 25 5 | 6 12 2 15 4 | 1 10 8 11 7 | | 7 14 15 12 10 | 3 4 8 2 11 | 22 16 9 19 17 | 20 13 25 23 1 | 6 24 21 5 18 | | 17 11 16 5 1 | 15 6 23 7 22 | 3 13 4 12 8 | 18 21 10 14 24 | 25 20 19 2 9 | | 6 9 18 19 20 | 12 5 14 1 25 | 2 10 21 24 11 | 7 17 3 22 8 | 16 4 15 13 23 | +----------------+----------------+----------------+----------------+----------------+ | 3 8 2 11 4 | 20 19 9 15 18 | 1 25 13 7 16 | 24 6 14 5 17 | 21 12 22 23 10 | | 5 22 10 25 17 | 2 1 3 4 13 | 8 11 6 21 19 | 12 23 20 18 9 | 7 14 24 15 16 | | 18 20 12 16 13 | 8 24 5 6 7 | 9 23 17 10 14 | 15 22 1 21 25 | 19 11 2 4 3 | | 24 15 7 23 14 | 21 11 25 12 17 | 4 5 22 20 2 | 10 8 16 3 19 | 13 18 6 9 1 | | 21 19 9 1 6 | 16 10 22 23 14 | 24 3 12 15 18 | 11 2 7 4 13 | 5 17 20 25 8 | +----------------+----------------+----------------+----------------+----------------+ | 10 16 1 14 8 | 25 13 12 5 2 | 17 18 15 22 24 | 4 19 21 11 23 | 9 7 3 6 20 | | 12 2 25 17 7 | 6 15 24 18 21 | 10 19 11 13 1 | 3 14 9 20 16 | 4 22 23 8 5 | | 15 21 4 13 11 | 17 23 16 22 3 | 7 9 8 5 20 | 25 1 6 2 12 | 14 19 10 18 24 | | 19 18 23 24 5 | 4 8 20 9 1 | 6 21 14 16 3 | 22 10 17 7 15 | 11 2 13 12 25 | | 20 3 6 9 22 | 11 14 7 10 19 | 25 4 23 2 12 | 13 18 8 24 5 | 15 1 16 21 17 | +----------------+----------------+----------------+----------------+----------------+ | 4 10 5 8 16 | 23 18 11 21 15 | 19 12 2 6 25 | 1 24 22 17 7 | 3 13 9 20 14 | | 23 17 22 20 19 | 24 2 1 25 6 | 21 8 3 4 9 | 5 15 13 10 14 | 18 16 12 7 11 | | 25 13 21 18 15 | 7 12 10 14 9 | 11 20 24 17 23 | 8 3 19 16 6 | 2 5 4 1 22 | | 1 7 24 2 12 | 19 16 13 3 4 | 14 15 5 18 22 | 9 20 11 25 21 | 23 8 17 10 6 | | 11 6 14 3 9 | 5 22 17 20 8 | 13 7 16 1 10 | 2 4 23 12 18 | 24 21 25 19 15 | +----------------+----------------+----------------+----------------+----------------+尚、サンプルプログラムでは答えに色を付けて、元々あった数値と区別できるようにしています。こんな感じです。
バージョン依存性チェックを作ってみた
さて、当初の目的の本命です。使わないかもしれませんが。
製品(プロダクト)とバージョンの組合せを変数にして、問題を解かせて組合せとして真となるかを判断するのは一緒です。番号付けが登録順で対処する感じですが、まぁ考え方は一緒です。ただ、バージョンチェックに関していうとこれまでと違うところがあります。それは 偽となったときに何が悪かったのか を教えてあげないといけないのです。どの版数とどの版数がコンフリクトしているのか、と。
これは、これまでのやり方だけではできません。ここでは picosat の機能を使って実現しました。picosat には MUS(Minimally Unsatisfiable Subformulas)というものを見つける仕組みがあり、これを使います。
バージョンチェックには特別に用意した
VersionSatisfiablityクラスを使います。var vs = new VersionSatisfiablity();製品 X のバージョン 0.0.1 がターゲットです。製品 A, B, Z はそれぞれバージョン 0.0.1 と 0.0.2 が存在します。
var X = vs.addProduct("X") .addVersion("0.0.1", true); var A = vs.addProduct("A") .addVersion("0.0.1") .addVersion("0.0.2"); var B = vs.addProduct("B") .addVersion("0.0.1") .addVersion("0.0.2"); var Z = vs.addProduct("Z") .addVersion("0.0.1") .addVersion("0.0.2");ここで、製品 A, B, Z の依存関係を定義します。ここでは単純に同じバージョンで依存する感じで A と B が Z に依存するものとします。
A("0.0.1").dependsOn(Z("0.0.1")); A("0.0.2").dependsOn(Z("0.0.2")); B("0.0.1").dependsOn(Z("0.0.1")); B("0.0.2").dependsOn(Z("0.0.2"));ここで、X が A と B に依存し、どちらも同じ版数に依存していれば問題無いですね。やってみましょう。
X("0.0.1").dependsOn(A("0.0.2")); X("0.0.1").dependsOn(B("0.0.2")); tryit();ちなみに
tryit()は結果を出すものですが、その定義は重要ではないので後程お見せします。今回は結果だけ。ちゃんと定義した依存関係の版数で問題無し、という結果になりました。1: [{ "index": 1, "isFix": 1, "name": "X", "version": "0.0.1" }, { "index": 3, "isFix": null, "name": "A", "version": "0.0.2" }, { "index": 5, "isFix": null, "name": "B", "version": "0.0.2" }, { "index": 7, "isFix": null, "name": "Z", "version": "0.0.2" }]条件を変えて、B の 0.0.1 に依存するようにしてみましょう。
X("0.0.1").dependsOn(A("0.0.2")); X("0.0.1").dependsOn(B("0.0.1")); tryit();こうなりました。
Unsatisfiable - Conflict here - X is v0.0.1 - Z is v0.0.1 => Z is NOT v0.0.2 - A is v0.0.2 => Z is v0.0.2 - B is v0.0.1 => Z is v0.0.1 - X is v0.0.1 => A is v0.0.2 - X is v0.0.1 => B is v0.0.1これは、以下の組合せが満たせない、と伝えています。今回はほぼほぼ全部出てしまってますが、条件が複雑になると満たせなかった条件だけが出力されます。
- X が 0.0.1 であること
- Z の 0.0.1 と 0.0.2 が排他であること
- A が 0.0.2 のとき Z が 0.0.2 であること
- B が 0.0.1 のとき Z が 0.0.1 であること
- X が 0.0.1 のとき A が 0.0.2 であること
- X が 0.0.1 のとき B が 0.0.1 であること
おわりに
せっかく SAT ソルバー入れてみたのでどこかで使えるといいな。パズルを解かせるのはちょっと面白かった。
そして本題の Package Manager はどうやって実現しようか考え中です。
P.S.
tryit()の定義function msg(a) { if (a.not) { return ("%1% is NOT v%2%" % a.value.name % a.value.version).format(); } else { return ("%1% is v%2%" % a.value.name % a.value.version).format(); } } function error(item) { switch (item.length()) { when 1: System.println("- ", msg(item[0])); when 2: item[0].not = !item[0].not; // (!A || B) means (A => B) System.println("- ", msg(item[0]), " => ", msg(item[1])); else: System.println("- ", item.map { => msg(_1) }.join(', or\n ')); } } function tryit() { var count = 0; for (var e in vs) { System.println("%d: " % ++count, e.toJsonString(true)); } if (count == 0) { System.println("Unsatisfiable - Conflict here"); vs.getConflict().each { => error(_1) }; } }
- 投稿日:2021-04-03T20:46:41+09:00
Javascriptでマイクから生データを取り出す
はじめに
マイクで取得したデータを、数値の配列にしたいだけなのに、苦労した話です。
調べるとScriptProcessorNodeを使ったものがいくつか出てきますが、非推奨っぽい。
AudioWorkletで代用してね、という話もあるが、これは扱いにくそう。
MediaRecorderからWav形式に変換できる方法もあるようだが、そこからうまいこと取り出すのも大変。
もっと簡単にできる方法があると思う。結論
- MediaRecorderでMediaStreamからデータを取得し適当な音声ファイル形式に出力
- FileReaderにより音声ファイルをArrayBuffer形式に変換
- AudioContext.decodeAudioDataによりArrayBufferをAudioBuffer形式に変換
- AudioBufferからgetChannelDataなどにより生データの取得
※取得される値の詳細はAudioBufferを参照してください。WAV形式で保管される数値とは異なります。
※コードは後述調べたこと
ScriptProcessorNode
次の方の記事に書いてあるような方法になります。
ただmozillaによると、本機能は廃止対象とのことです。なので長期使用を考える場合は、これを使わない方法が必要となります。
AudioWorklet
ScriptProcessorNodeの代替を調べると、AudioWorkletが出てきます。
次の方の記事が詳しいです。
む、難しいです。また、ファイル数が増えることや、実装行数が増えることもちょっといただけません。
私はただ、音声データの配列が欲しいだけなのです。MediaRecorder
やはりこれで何とかしないと、とは思うのですが、うまく検索できませんでした。
Audio要素
Audio要素で再生するのは簡単ですね。
ふむ、ではここに格納されたデータを取得してうまいことできる方法は、と思ったのですが、見つけられませんでした。decodeAudioData
音声ファイルをAudioBufferに変換するものだそうです。
音声ファイルはMediaRecorderで生成できるので、あとはAudioBufferから生データを取り出せれば、できます。まとめ
手順は以下の通りです。
まず、MediaDevicesからMediaStreamを取得します。
let audioStream = null navigator.mediaDevices.getUserMedia({audio: true}).then(stream => { audioStream = stream })次に、MediaRecorderを作成します。
そしてEventListenerでデータの取得・変換を行うようにします。chunks = [] const mediaRecorder = new MediaRecorder(audioStream, { mimeType: 'audio/webm' }) mediaRecorder.addEventListener('dataavailable', e => { if (e.data.size > 0) { chunks.push(e.data); } }) mediaRecorder.addEventListener('stop', e => { const blob = new Blob(chunks) const reader = new FileReader() reader.readAsArrayBuffer(blob) reader.onload = () => { audioCtx.decodeAudioData(reader.result).then(buf => { const b = buf.getChannelData(0) console.log(b) }) } })あとは適切なタイミングでMediaRecorderを開始・停止するだけでになります。
mediaRecorder.start() // some process mediaRecorder.stop()完成コード
おまけ
そもそもなぜ必要だったかというと、
でマイクからの音声をデータとして扱えるようにするためです。
よかったら見てもらえると嬉しいです。
- 投稿日:2021-04-03T20:45:38+09:00
jsx,tsxでEmmetを使用する方法
はじめに
ReactでEmmetが使えたら便利だな〜って思って調べたら利用出来たのでご紹介します。
VScodeだと素の状態でもEmmetがインストールされているのでおすすめです。方法
方法はsetting.jsonを弄るだけ。
command + ,(Windows: Ctrl + ,)で設定を開きます。
検索にsetting.jsonと入れてsetting.jsonを編集をクリックその後setting.jsonに
setting.json{ "emmet.triggerExpansionOnTab": true, "emmet.includeLanguages": { "javascript": "javascriptreact", "typescript": "typescriptreact", }, }を入れればtsx,jsxでEmmetが使用できます。
さいごに
Emmetを愛していたのでEmmetが使用できない環境は苦痛でしたが解決しました。
Emmetを使い慣れてる人にはおすすめです。
- 投稿日:2021-04-03T20:39:25+09:00
leaflet.jsで地図を描画
R/leafletを使ってたけど、
leaflet.jsを使った方が使えるプラグインも多そうなのでリハビリ。index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-widtho, initial-scale=1.0"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script> <title>Leaflet</title> </head> <body> <div id="map" style="height: 98vh;"></div> <script> var map = L.map('map').setView([35, 135], 16); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); </script> </body> </html>ファイルを分けて書くと下記のような感じ。
index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-widtho, initial-scale=1.0"> <link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script> <title>Leaflet</title> </head> <body> <div id="map"></div> <script src="js/map.js"></script> </body> </html>css/style.css#map { height: 98vh; }jsフォルダで下記を実行し、
typings install dt~leaflet --global --savejs/map.jsに
/// <reference path="typings/index.d.ts" />を追記するとVisual Studio Codeでのleaflet.jsの実装が楽。js/map.js/// <reference path="typings/index.d.ts" /> var map = L.map('map').setView([35, 135], 16); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map);色々と思い出せない。
- 投稿日:2021-04-03T20:39:25+09:00
leaflet.jsでOpenStreetMapを描画
R/leafletを使ってたけど、leaflet.jsの方が使えるプラグインも多いので練習。
index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script> <title>Leaflet</title> </head> <body> <div id="map"></div> <script src="js/map.js"></script> </body> </html>ファイルを分けて書くと下記のような感じ。
index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-widtho, initial-scale=1.0"> <link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script> <title>Leaflet</title> </head> <body> <div id="map"></div> <script src="js/map.js"></script> </body> </html>css/style.css#map { height: 98vh; }js/map.js/// <reference path="typings/index.d.ts" /> var map = L.map('map').setView([35, 135], 16); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map);map.jsのあるフォルダで下記を実行し、
typings install dt~leaflet --global --savemap.jsに
/// <reference path="typings/index.d.ts" />を追記するとVisual Studio Codeでのleaflet.jsの実装が楽になる。
- 投稿日:2021-04-03T20:14:28+09:00
HTMLで絵を書く
上の画像のようなものが作れます。
index.html<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> <meta charset="utf-8" /> </head> <body style="background-color:#D0D0D0;"> <div class="parent"> <div class="bg"> <canvas id="SimpleCanvas" width="640" height="640" style="background-color:#FFFFFF;"></canvas> <canvas id="SimpleCanvas-layer2" width="120" height="120" style="background-color:rgba(255,0,0,0);;"></canvas> <canvas id="SimpleCanvas-layerFont1" width="120" height="120" style="background-color:rgba(0,0,0,0);;"></canvas> </div> </div> </body> <link rel="stylesheet" href="./layout.css" type="text/css"> <script type="text/javascript" src="./draw.js"></script> </html>layout.cs.parent { width: 720px; height: 0px; position: relative; /*relativeに設定*/ padding: 0em 0em; margin:0; z-index: 0; } .l1 { top: 20px; left: 0px; width: 720px; padding: 0em 0em; position: relative; z-index: 2; } .bg { top: 0px; /*動的に変更されるので適当*/ left: 0px; /*動的に変更されるので適当*/ width: 400px; /*撮影枠サイズ*/ height: 60px; /*撮影枠サイズ*/ position: relative; /*absoluteに設定*/ padding: 0em 0em; z-index: 1; } .bg canvas { position: absolute; }draw.jsfunction draw_1Pattern(step){ console.log(width,height,pixels.length,width*height*4) // ピクセル単位で操作できる var cnt = 0 for (var x = 0; x < width; ++x) { for (var y = 0; y < height; ++y) { var base = (y * width + x) * 4; if((Math.floor(x/step))%2 == 0){ pixels[base + 0] = 38 pixels[base + 1] = 31 pixels[base + 2] = 135 pixels[base + 3] = 255 } else{ pixels[base + 0] = 0 pixels[base + 1] = 133 pixels[base + 2] = 84 pixels[base + 3] = 255 } } } } var canvas = document.getElementById('SimpleCanvas'); canvas.width = screen.width*0.9 canvas.height = screen.height*0.8 var context = canvas.getContext('2d'); // キャンバス全体のピクセル情報を取得 var imageData = context.getImageData(0, 0, canvas.width, canvas.height); var width = imageData.width, height = imageData.height; var pixels = imageData.data; // ピクセル配列:RGBA4要素で1ピクセル //console.log(pixels) var step = 160//ボーダーの横幅ピクセル draw_1Pattern(step) //console.log(pixels) // 変更した内容をキャンバスに書き戻す context.putImageData(imageData, 0, 0);
- 投稿日:2021-04-03T18:59:30+09:00
ローディング画面実装方法
ページを読み込むときにローディング画面が欲しくなりますね。
ローディング画面実装の備忘録をサンプルコード付きでまとめました。1.ページ読み込み時にはローディング画面を表示
2.DOM読み込みが終了するとローディング画面を消すサンプルコード
<html lang="ja" class="is-loading"> <div class="c-loader"> <div class="c-loader__bg"></div> <div class="c-loading__inner"> <div class="c-loader__icon"></div> </div> </div>.c-loader { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-align: center; -ms-flex-align: center; align-items: center; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 100; text-align: center; pointer-events: none; } .c-loader__bg { display: block; position: absolute; top: 0; right: 0; z-index: 0; width: 100%; height: 100%; opacity: 0; -webkit-transition: 0.5s cubic-bezier(0.215, 0.61, 0.355, 1) 0s; transition: 0.5s cubic-bezier(0.215, 0.61, 0.355, 1) 0s; -webkit-transition-property: opacity; transition-property: opacity; background-color: #fff; } html.is-loading .c-loader__bg { opacity: 1; } .c-loader__icon { position: relative; z-index: 1; width: 24px; height: 24px; margin: -4% auto 0; -webkit-animation: 1s loading linear infinite; animation: 1s loading linear infinite; border: 1px solid #f5f5f5; border-top: 1px solid #000; border-radius: 50%; opacity: 0; -webkit-transition: 0.5s cubic-bezier(0.215, 0.61, 0.355, 1) 0s; transition: 0.5s cubic-bezier(0.215, 0.61, 0.355, 1) 0s; -webkit-transition-property: opacity; transition-property: opacity; } html.is-loading .c-loader__icon { opacity: 1; } @-webkit-keyframes loading { from { -webkit-transform: rotate(0deg); transform: rotate(0deg); } to { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } @keyframes loading { from { -webkit-transform: rotate(0deg); transform: rotate(0deg); } to { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }var isLoading = false; $(function() { setTimeout(function(){ $('html').removeClass('is-loading'); },300) }); $(window).on('load', function() { if (isLoading==false) { loadingInit(); } }); function loadingInit() { function loading() { $('html').addClass('is-loaded'); } loading(); }DOMが読み込まれたタイミングで0.3秒後にis-loadingというクラスを解除、すべて読み込みが終わるとis-loadedというクラスがつくようにしています。
- 投稿日:2021-04-03T18:24:50+09:00
ブックマークレットで Markdown 用のリンクテキストを自動生成
またまた、バッチシリーズです。
今回は Markdown 用のリンクテキストを自動生成するブックマークレットを作成したのでご紹介いたします。
最近、手動で Markdown や Textile で参考サイトのリンクを作るのが面倒になってきたので自動化したら、(個人的には)面倒ではなくなりました。
※ただし、Mac の Safari と Chrome しか動作確認していないので、ご注意ください。仕様
例:Yahoo のサイトでブックマークレットを実行した場合、以下のテキストがクリップボードにコピーされる
[Yahoo! JAPAN](https://www.yahoo.co.jp/)ブックマークレットを実行したら、開いているサイトの Markdown 用リンクテキストをクリップボードにコピーする。
失敗時は alert or prompt で通知する。あとは良しなにペーストしてお使いください。
使い方
まずはブックマークレット作成
- ブラウザでブックマークを追加する(空でも何でも良いです)
- 以下に記載してある 圧縮後 (Markdown_Link) の JavaScript をコピーする
- 追加したブックマークの URL に JavaScript をそのままペーストして保存する
参考サイト
ブックマークレットを実行
- リンクテキストを作りたいサイトをブラウザで表示する
- ブラウザのお気に入りからブックマークレットを選択する
- ブックマークレットが実行され、リンクテキストがクリップボードにコピーされる
- あとは Markdown テキストにペーストしてお使いください
JavaScript (Markdown_Link)
( () => { // --------------------------------------------------------------- const pageTitle = document.title; const url = location.href; if (pageTitle && url) { // link text (Markdown) const text = '[' + pageTitle + '](' + url + ')'; // textarea const ta = document.createElement('textarea'); ta.textContent = text; document.body.appendChild(ta); // select text ta.select(); // copy to clipboard if (!document.execCommand('copy')) { prompt('コピー失敗', text)(); } // remove textarea ta.parentNode.removeChild(ta); } else { alert('テキスト取得失敗'); } // --------------------------------------------------------------- } )();圧縮後 (Markdown_Link)
javascript:(()=>{const pageTitle=document.title;const url=location.href;if(pageTitle&&url){const text='['+pageTitle+']('+url+')';const ta=document.createElement('textarea');ta.textContent=text;document.body.appendChild(ta);ta.select();if(!document.execCommand('copy')){prompt('コピー失敗',text)()} ta.parentNode.removeChild(ta)}else{alert('テキスト取得失敗')}})()実行結果
[Yahoo! JAPAN](https://www.yahoo.co.jp/)JavaScript (Textile_Link)
Redmine をお使いの方は Textile 版も用意しましたので、こちらをお使いください。
( () => { // --------------------------------------------------------------- const pageTitle = document.title; const url = location.href; if (pageTitle && url) { // link text (Textile) const text = '\"' + pageTitle + '\":' + url; // textarea const ta = document.createElement('textarea'); ta.textContent = text; document.body.appendChild(ta); // select text ta.select(); // copy to clipboard if (!document.execCommand('copy')) { prompt('コピー失敗', text)(); } // remove textarea ta.parentNode.removeChild(ta); } // --------------------------------------------------------------- } )();圧縮後 (Textile_Link)
javascript:(()=>{const pageTitle=document.title;const url=location.href;if(pageTitle&&url){const text='\"'+pageTitle+'\":'+url;const ta=document.createElement('textarea');ta.textContent=text;document.body.appendChild(ta);ta.select();if(!document.execCommand('copy')){prompt('コピー失敗',text)()} ta.parentNode.removeChild(ta)}})()実行結果
"Yahoo! JAPAN":https://www.yahoo.co.jp/参考サイト
- プレーンテキストとしてコピーするブックマークレット - Qiita
- ソースはこちらの記事のほぼ完コピーです。(ちょっと違うぐらい)
- @c3091 さん、素晴らしい記事をありがとうございます!
- ページタイトルとURLを取得できるブックマークレットの例|JavaScript・promptメソッド
- 初心者必見!JavaScript if~elseが即わかる!andやorなどの論理演算子も紹介
- JavaScriptの正規表現でエスケープが必要な文字を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン
注意事項
Document.execCommand()廃止
- Document.execCommand() は廃止されたので、いつまで使えるのか分かりません
- ブラウザで使えなくなった時のためにエラー時は手動コピーしやすいように prompt で表示させてます
参考サイト
Chrome の「新しいタブ」画面でブックマークレットが動かない
- テスト実行時はどこかのWebサイトを表示している状態でブックマークレットを実行して下さい
参考サイト
ドロップした仕様
元々はキーボードから手を離さずに実行出来るようにしたかったのですが、以下のやり方では Mac 版 Chrome で実行しても XML が表示されるだけだったので今回は諦めました。
手順
- 適当なショートカット(.webloc)を作成する
- webloc の URL にブックマークレットを入れて保存する
- Commnad + space で Spotlight を表示
- Spotlight でショートカット(.webloc)を検索して、ブックマークレットを実行
- [NG] Chrome で XML が表示されるだけ
参考サイト
- 投稿日:2021-04-03T17:44:26+09:00
配列
配列(要素を取得)
let fruits = ['もも','りんご','バナナ','みかん','ぶどう']; //1番目の要素 console.log(fruits[0]); //fruits[0]:もも //4番目の要素 console.log(fruits[3]); //fruits[3]:みかん //6番目の要素(空っぽ) console.log(fruits[6]); //fruits[5]:undefinedindexOf
- 配列データに存在する場合にその場所を「インデックス番号」で取得
let fruits = ['もも','りんご','バナナ','みかん','ぶどう']; let fruits2 = fruits.indexOf('りんご'); console.log(fruits2);//1push
- 配列要素の追加
let fruits = ['もも','りんご','バナナ','みかん','ぶどう']; fruits.push ('梨'); console.log(fruits);//(6) ["もも", "りんご", "バナナ", "みかん", "ぶどう", "梨"]delete
- 要素の削除
let fruits = ['もも','りんご','バナナ','みかん','ぶどう']; delete fruits[2]; console.log(fruits);//(5) ["もも", "りんご", empty, "みかん", "ぶどう"]
- 投稿日:2021-04-03T16:56:17+09:00
Puppeteer(パペティア)でcsvファイルをダウンロードする
概要
ずっとやりたかったPuppeteerでファイルダウンロードをやってみた。
世の中いろんなやり方が書かれていて混乱したが、やってみたら簡単にできたので共有。参考 - 公式issue
https://github.com/puppeteer/puppeteer/issues/299環境
- Ubuntu18.04
- Node.js v14.15.1
- Puppeteer 8.0.0
ソース
意外と普通にできました!
何か罠が待っている気もしなくもなく。const puppeteer = require('puppeteer'); const fs = require('fs'); const path = require("path"); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); const width = 1980; const height = 1080; await page.setViewport({ width, height }); await page.goto('https://portal.local/'); // gotoの後は、waitしなくてよさげ // ファイルダウンロード const downloadPath = path.resolve('/hoge/fuga/'); // ここは絶対に絶対パス await page._client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath: downloadPath }); // csvダウンロードボタンクリック await page.click('.csvDownload'); await page.waitForTimeout(5000); await browser.close(); })();参考
- 投稿日:2021-04-03T16:41:07+09:00
【WebIOPi】スマホでサーボモーターを制御!③
スマホでサーボモータを制御!
今回は、スマホでサーボモータを制御するシリーズの第3回目です。
Raspberry Piを用いて簡単にIoTを作成できるライブラリ、WebIOPiを使用し、スマホからサーボモーターを制御します。
これまでの記事は以下を参考にしてください。
1. WebIOPiのインストール
2. HTML/CSSファイルの作成
3. JavaScriptファイルの作成サーボモーター
サーボモーターを動作させるPythonスクリプトを作成します。
サーボモーターは、パルスの周期に応じて回転します。それぞれの仕様によりますが、制御パルスは1.0ms ~ 2.4msの範囲だと思います。
ちなみに、SG90というサーボモータは、0.5ms~2.4msだそうです。
サーボモーターを動作させるために、
pigpioというライブラリを使用します。pigpioインストール
サーボモータを動作させるときに、安定したパルスを生成できるように、pigpioというライブラリを使用します。
Raspberry Piのターミナルで以下のコマンドを打ちます。sudo apt install pigpiopigpioを使用する場合は、以下のコマンドで起動(デーモン)します。
sudo pigpiodPythonスクリプト作成
まず、全体は以下のようになります。
pyfile.pyimport webiopi import pigpio webiopi.setDebug() # WebIOPiのデバッグをセット pi = pigpio.pi() # サーボモーターへのパルス出力ピンを指定 SV_1 = 12 # SERVO1 SV_2 = 19 # SERVO2 """以下、サーボ動作""" # SERVO1 @webiopi.macro def GET1(val): value1 = int(val) pi.set_servo_pulsewidth(SV_1, value1) webiopi.debug(value1) # SERVO2 @webiopi.macro def GET2(val): value2 = int(val) pi.set_servo_pulsewidth(SV_2, value2) webiopi.debug(value2)WebIOPiでは、デバッグを行う際、
webiopi.setDebug()という記述をします。
そして、webiopi.debug(表示したいパラメータ)と記述することでデバッグの際、そのパラメータの値を確認することができます。webiopi.macro
ところで、JavaScriptで作成した
webiopi.callMacro()関数を覚えていますか?webiopi().callMacro('GET1',value1)この第一引数は、実行したいPython関数、ここでは
def GET1(val):です。
また、JavaScriptより引数valを受け取ります。互いの関係は下の写真を参考にしてください。
ここで、webiopiで実行するPython関数には、関数名の上に
@webiopi.macroという記述が必須です!あとは、受け取った値を数値型に変換し、pigpioによりサーボを動作させます。
最後に
次回が最終回です。
WebIOPiを使用してサーバーを起動させます。
- 投稿日:2021-04-03T16:08:56+09:00
switch文
- 投稿日:2021-04-03T15:54:37+09:00
QiitaやGoogleなどで使える、クオートで囲うBookmarklet作ってみた
クオートで囲うの面倒くさい
選択して
"やshift+@で囲ってくれるエディターの便利機能に体が慣れてしまった。
誰か作ってるだろうけど、練習用に作ってみた。使い方
- コピーしてブックマークのURL欄に貼り付け、編集画面でそれをクリック。
- カーソルが十字になる。
- 文字を選択して
`とか"とか{とか押すだけ。詳しくはソース参照。bookmarkletjavascript: /* eslint semi: "error"*/ (function() { var sandwich = (p1, p2) => { if (p2 === undefined) { p2 = p1.slice(p1.length / 2); p1 = p1.slice(0, p1.length / 2); } var textarea = document.activeElement; var pos_start = textarea.selectionStart; var pos_end = textarea.selectionEnd; var val = textarea.value; var range = val.slice(pos_start, pos_end); var beforeNode = val.slice(0, pos_start); var afterNode = val.slice(pos_end); var insertNode = p1 + range + p2; textarea.value = beforeNode + insertNode + afterNode; textarea.setSelectionRange(pos_start + p1.length, pos_end + p1.length); }; var handleKeydown = (ev) => { var el = ev.target; if (!['TEXTAREA', 'INPUT'].includes(el.tagName)) return; if (el.selectionStart === el.selectionEnd) return; var pd = () => ev.preventDefault() || true; if (ev.key === '`' && ev.ctrlKey) return pd() && sandwich('\n```\n', '\n```\n'); if (ev.key === '`') return pd() && sandwich('``'); if (ev.key === '{') return pd() && sandwich('{}'); if (ev.key === '[') return pd() && sandwich('[]'); if (ev.key === '(') return pd() && sandwich('()'); if (ev.key === '~') return pd() && sandwich('~~~~'); if (ev.key === '*') return pd() && sandwich('****'); if (ev.key === '_') return pd() && sandwich('__'); if (ev.key === '"') return pd() && sandwich('""'); if (ev.key === '\'') return pd() && sandwich('\'\''); if (ev.key === '>') return pd() && sandwich('> ', ''); if (ev.key === 'L' && ev.ctrlKey) return pd() && sandwich('[](', ')'); }; document.addEventListener('keydown', handleKeydown, {once: !true}); document.title = '@' + document.title; var style = document.createElement('style'); style.textContent = 'textarea,input{ cursor: crosshair}'; document.body.appendChild(style); })();作ってみて分かったこと
- 書き換えた瞬間、編集履歴が消える。
ctrl+zで戻れない- ブラウザIDEとかは戻れるし、別の方法があるんだろうなあ。
- Qiitaでは、書き換えの直後はプレビューに反映されない。
- ev.preventDefault()してるからかな。
- 日本語入力中だと、なんかおかしい。
- 総合的に、そんなに便利じゃない。
```はctrlかcontrolのコンビネーション。
- alt系にしたかったけど。Macで
ev.keyが不明になり諦める。- Qiitaにドラッグアンドドロップで使えるリンクを作れなかった。
- markdownのリンク書式にコードを改行消して入力してみたが、括弧の認識がずれて無理だった。
- Bookmarklet Builder が let const に対応してなかった。
- Bookmarkletはセミコロン必須
- 動作してるかどうか分かる何かが必要。alertは邪魔くさい。
- カーソル変化は良いと思った。bodyでなくtextareaに。
- vscodeで
javascript:(123)て書いても怒られない。
- ラベルはまだ良くわからない。
参考にさせてもらったページ
- 投稿日:2021-04-03T15:40:26+09:00
条件分岐
- 投稿日:2021-04-03T15:26:54+09:00
【WebIOPi】スマホでサーボモーターを制御!②
スマホでサーボモータを制御!
今回は、スマホでサーボモータを制御するシリーズの第2回目です。
Raspberry Piを用いて簡単にIoTを作成できるライブラリ、WebIOPiを使用し、スマホからサーボモーターを制御します。
WebIOPiのインストールはこちらの記事を参考にしてください。
まだHTMLファイルを作成していない方は、こちらの記事も参考にしてください。
WebIOPiとJavaScript
今回は、前回作成したHTMLファイルにJavaScriptを追加します。
WebIOPiでは、JavaScriptを使用することで、Pythonとの連携が可能になります。
それでは作成していきます!
JavaScriptの実装
WebIOPiの公式ドキュメントには様々な機能が紹介されています(公式ドキュメント:機能紹介)。
その中でも、今回はWebiopi.callMacro()という関数を使用します。
この関数は、
JavaScriptから特定のPython関数を実行することができます。例えば、Webページ上のボタンが押されたら、PythonのLEDを光らせる関数を実行することも可能になります!
Webiopi.callMacro()について
WebIOPi.callMacro(macro, [args[, callback]])Call a macro on the server.
(string) macro : name of the macro to call
(string) arg (optional) : array containing arguments
(function) callback (optional) : function called when result received from the serverWebIOPi公式ドキュメントより
引数には以下を指定します。
- 1つ目:実行したいPython関数名
- 2つ目:Python関数に渡す引数(スライドバーの値など)
- 3つ目:Python関数が実行された後に実行するJavaScriptの関数
また、HTMLにJavaScriptを埋め込む際、headタグに
controller.html<head> ... <script type="text/javascript" src="webiopi.js"></script> </head>を忘れないよう、記述してください。
コード
controller.js// 前半 var current1 = document.getElementById("myRange1"); // SERVO1 pin12 var output1 = document.getElementById("out1"); output1.innerHTML = current1.value; var current2 = document.getElementById("myRange2"); // SERVO2 pin19 var output2 = document.getElementById("out2"); output2.innerHTML = current2.value; // 後半 function func1(){ var value1 = current1.value; output1.innerHTML = value1; webiopi().callMacro('GET1',value1); } function func2(){ var value2 = current2.value; output2.innerHTML = value2; webiopi().callMacro('GET2',value2); }まず、前半はページにアクセスがあったとき、スライダの現在の値を取得して表示するプログラムです。
後半でwebiopi.callMacro関数を使用します。
function func1(){ var value1 = current1.value; output1.innerHTML = value1; webiopi().callMacro('GET1',value1); }まず、2行目では現在のスライダの値を取得し、それを
output1(idがout1の場所)に表示します。
そして、スライダの値をGET1というPython関数に渡します。HTML全体
これまでのHTML/JavaScript全体は次のようになります。
controller.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="mobile-web-app-capable" content="yes"> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <title>Servo Controller</title> <script type="text/javascript" src="webiopi.js"></script> <link rel="stylesheet" type="text/css" href="controller.css"> </head> <body> <div align="center"> <tr> <td> <table> <tbody> <tr> <!--SERVO 1--> <td id="name1">SERVO 1</td> <td class="SS1"> <div class="slidecontainer1"> <input type="range" min="1000" max="2500" value="2000" step="10" class="slider" id="myRange1" oninput="func1()"> </div> </td> <td id="out1"></td> <!--SERVO 2--> <td id="name2">SERVO 2</td> <td class="SS2"> <div class="slidecontainer2"> <input type="range" min="1000" max="2500" value="2000" step="10" class="slider" id="myRange2" oninput="func2()"> </div> </td> <td id="out2"></td> </tr> </tbody> </table> </td> </tr> </div> </body> <script> var current1 = document.getElementById("myRange1"); //SERVO1 pin12 var output1 = document.getElementById("out1"); output1.innerHTML = current1.value; var current2 = document.getElementById("myRange2"); //SERVO2 pin19 var output2 = document.getElementById("out2"); output2.innerHTML = current2.value; function func1(){ var value1 = current1.value; output1.innerHTML = value1; webiopi().callMacro('GET1',value1); } function func2(){ var value2 = current2.value; output2.innerHTML = value2; webiopi().callMacro('GET2',value2); } </script> </html>最後に
次回、スライダの値に応じてサーボモーターを動かすPythonプログラムを作成していきます。
- 投稿日:2021-04-03T15:04:06+09:00
【Kintoneカスタマイズテンプレート】日付フィールドAの◯日後の日付を日付フィールドBへ登録する
目次
・はじめに
・カスタマイズ概要
・テンプレート
・変数説明
・おわりにはじめに
プログラミング初心者の方や、コードをいちいち書きたくないという方の為に、4つの変数の値だけ変更すれば即使えるテンプレートを作りました!
JSEditに以下のコードをコピー&ペーストするだけでOKです。カスタマイズ概要
from(日付フィールドA)から14日後をto(日付フィールドB)へ反映させる。
尚且、to(日付フィールドB)は手動で更新も可能。
①〜④までの変数の値を利用する用途やフィールドコードに合わせて変更してお使い下さい。テンプレート
// #処理概要 // from(日付フィールド)から14日後をto(日付フィールド)に反映する。to(日付)は手動で更新も可能。 // #変数 // ①reflectionDays: 加算する日数を指定する // ②bA: 日数を加算する場合は"add"(◯日後を表示させたい) // 日数を減算する場合は"subtract"(◯日前を表示させたい) // ③from: 計算の元となるフィールド(フィールドコード) // ④to: 14日後を反映するフィールド(フィールドコード) (function() { 'use strict'; const reflectionDays = 14; const bA = 'add'; const conf = [ { from: '作業依頼日', to: '提出希望日' }, // 2ペアで処理を行いたい場合は以下も記入 { from: '作業依頼日_3', to: '提出希望日_4' } ]; // 値の変更の元となるフィールド(n) let targetFields = []; conf.forEach(field=>{ targetFields.push(field.from); }) const targetEvents = createEvents(targetFields) function createEvents(fields){ var eventLists = [ //#対応イベント // レコード追加画面 表示後 // レコード追加画面 表示後 モバイル版 'app.record.create.show', 'mobile.app.record.create.show', ]; fields.forEach(field=>{ // #対応イベント // レコード一覧 インライン編集のフィールド値変更 // レコード編集画面 フィールド値変更 // レコード編集画面 フィールド値変更 モバイル版 // レコード追加画面 フィールド値変更 // レコード追加画面 フィールド値変更 モバイル版 eventLists.push(`app.record.index.edit.change.${field}`); eventLists.push(`app.record.edit.change.${field}`); eventLists.push(`mobile.app.record.edit.change.${field}`); eventLists.push(`app.record.create.change.${field}`); eventLists.push(`mobile.app.record.create.change.${field}`); }) return eventLists; } kintone.events.on(targetEvents, function(event) { // イベントタイプを配列に分解 const eventTypeArray = event.type.split("."); console.log(eventTypeArray); const eventType = eventTypeArray[eventTypeArray.length -2]; // フィールドの変更イベントか判定 if(eventType == 'change') { // エベントタイプの一番後ろの文字(変更されたフィールド)を代入 const changeField = eventTypeArray[eventTypeArray.length -1]; // 変更元フィールドに対しての更新先フィールドをconfの中から探して代入 const targetSetting = conf.find(setting => setting.from === changeField).to; // 計算処理 let result = moment(event.record[changeField].value)[bA](reflectionDays, 'd').format('YYYY-MM-DD'); // 計算結果を反映先フィールドへ更新 event.record[targetSetting]['value'] = result; console.log('変更イベントの場合の計算結果:' + result); // その他のイベントの場合 }else{ // fromからtoへそれぞれ計算結果を反映する conf.forEach(fields => { // 計算処理 let result = moment(event.record[fields.from].value)[bA](reflectionDays, 'd').format('YYYY-MM-DD'); event.record[fields.to].value = result; console.log('変更イベント以外の場合の計算結果:' + result); }) } return event; }); })();おわりに
この記事を上げた理由は以下になります。
1. Kintoneのアプリ開発をしていると同じようなカスタマイズ案件に出会うことがあり、その度に、毎回調べてコードを書くのが面倒。(自分の為)
2. 変数だけ変えるだけで使えるようにテンプレート化してしまえば、コードを書けない方でも使えるじゃん! と。コードは書けないけどKintoneの標準機能だけでは満足出来ない!という方の為になれば幸いです。
これからも、変数の値を変えるだけで使えるカスタマイズテンプレートを随時投稿していきたいと思います。
最後まで見てくださりありがとうございます!
- 投稿日:2021-04-03T14:30:41+09:00
javascript基本メモ
初めに
JavaScriptをもう一度基礎から勉強した基本構文メモです。
自分用なので誤字脱字があるかもしれません。
今後色々追加していく予定です。変数
変数とは繰り返し使う値を格納しておく入れ物
変数を定義することを「変数を宣言する」と言う//変数の宣言 let a = 0; const b = 0; var c = 2; //出力 console.log(a); console.log(b); //letかconstを使う let(ES6~) const(ES6~) var(非推奨)関数
関数とは一連の手続き(処理)を実行する文の集まり
関数に渡す変数が「引数」
関数の呼び出し元に返す値が「戻り値」//関数の宣言 function fn(引数1, 引数2){ return 戻り値; } //関数の呼び出し fn(実引数1, 実引数2);オブジェクト
オブジェクトとは名前(プロパティまたはキー)と値(バリュー)をペアで管理するもの
名前:値
名前:関数名
名前:オブジェクトlet obj = { property: 'hello', property: function(){ }, property: { d: 'Bye' } } //プロパティへのアクセス方法 //ドット記法 obj.name //ブランケット記法 obj['name'] //例 let obj = { prop1: 'value1', prop2: 'value2', prop3: function(){ console.log('value3') }, prop4: { prop5: 'value5' } } obj.prop3(); console.log(obj.prop4); console.log(obj.prop4.prop5); //値を追加することもできる obj.prop6 = 'value6'; console.log(obj['prop6'])JavaScriptとは
・ECMAScriptの仕様に基づいて実装されているプログラミング言語
・環境によって使える機能が変わってくる
・universal JavaScript は様々な環境で動くように作られたJavaScript
・ブラウザとJSエンジンで違うが一番シェアがあるのはV8
・JavaScriptからweb APIsを通してブラウザを操作する
・APIとはアプリケーションプログラミングインターフェースの略でソースから別のアプリケーションを動かす仕組みのこと実行環境
コードが実行されるまで
・JSエンジンによって生成されるコード内のどこからでもアクセスできるオブジェクト
・JavaScript実行前にはグローバルオブジェクトとthisが準備される
・ブラウザのグローバルオブジェクトはwindowオブジェクトとなる実行コンテキスト
コードを実行する際の文脈・状況
(コードが実行されているとき、どのような状況で実行されているか)グローバルコンテキスト(ファイル直下で実行するもの)
・実行中のコンテキスト内の変数・関数
・グローバルオブジェクト
・this
が使用可能関数コンテキスト(関数内で実行されるもの)
・実行中のコンテキスト内の変数・関数
・argument
・super
・this
・外部変数コールスタック
Javascriptエンジンが保持している為、実行中のコードがたどってきたコンテキストをたしかめることができる
このようなスタックの仕組みを「後入れ先だし」LIFO(Last In ,First Out)というホイスティング(宣言の巻き上げとも言う)
コンテキスト内で宣言した変数や関数の定義をコード実行前にメモリーに配置すること//実行される a(); function a() { console.log('hogehoge') } //変数の場合 var b = 0; console.log(b);//出力される console.log(c);//出力されない var c = 0;関数宣言の仕方
関数と関数式では挙動の違いがある//これだとaを出力しようとするとエラーがでる a(); const a = function(){ console.log(c); let c = 1; }スコープ
スコープとは実行中のコードから値と式が参照できる範囲
windowオブジェクト=グローバルスコープ
一般的にはスクリプトスコープもグローバルスコープと呼ばれる関数スコープとブロックスコープ
関数宣言はブロックスコープは無視されてしまう
function a() { //関数スコープ } { //ブロックスコープjavascriptでは{}の中と言う意味 変数の宣言で使えるのは let , const }レキシカルスコープ
レキシカルとはプログラミングではどこになにがかかれているかという意味
実行中のコードから見た外部スコープのことレキシカルスコープとはコードを書く場所によって参照できる変数が変わるスコープのこと
コードを記述した時点で決定するため「静的スコープ」とも言うlet a = 2; function fn1(){ let b = 1; function fn2(){ let c = 3; } fn2(); } fn1();スコープチェーン
スコープが複数階層で、連なっている状態
//スコープが複数階層になっている場合一番内側の変数から探しにいく let a = 2; function fn1(){ let a = 1; function fn2(){ let a = 3; console.log(a); } fn2(); } fn1(); >>3クロージャー
レキシカルスコープの変数を関数が使用している状態
function incrementFactory(){ let num = 0; function increment(){ num = num + 1; console.log(num); } return increment; } const increment = incrementFactory; increment(); increment(); increment(); increment();クロージャー(動的)
function addNumberFactory(num){ function addNumber(value){ return num + value; } return addNumber; } const add5 = addNumberFactory(5); const add10 = addNumberFactory(10); const result = add10(10); console.log(result); >>20即時関数(IIFE)
関数定義と同時に一度だけ実行される関数
実行結果が呼び出し元に返却されるfunction a() { console.log('called'); } a(); let c = (function(){ console.log('called'); return 0; }) console.log(c); let b = function(){ console.log('called'); }();変数
厳格な等価性と抽象的な等価性
//厳格な等価性(型の比較あり) a === b //抽象的な等価性(型の比較なし) a == bfalsyとtruthy
falsyな値とはBooleanで真偽値に変換した場合falseになる値の事
falsyな値 ・false ・null ・0 ・0n ・undefined ・NaN ・"" truthyな値 ・上記以外AND条件OR条件
//記述方法 a && b a || b //値の初期化を簡略化できる function hello(name){ name = name || 'Tom'; console.log('Hello' + name); } hello('Bob') let name; name && hello(name);関数とオブジェクト
プリミティブ型とオブジェクト
・データ型ではプリミティブ型とオブジェクトが存在する
・オブジェクトは参照を名前付きでしている入れ物・プリミティブ型は値の比較
・オブジェクトは参照の比較【プリミティブ型】
String, Null, Number, Symbol, Boolean, Biglnt, Undefined【オブジェクト】
Object関数
関数は「実行可能」なオブジェクトである
prop「属性」
function a(){ console.log('hello'); } a.prop = 0; a.method = function(){ console.log('method'); } a(); a.method(); console.log(a.prop); >>hello >>method >>0コールバック関数
他の関数に引数として渡される関数
//関数はオブジェクトとして扱える、関数を引数として渡せる function hello(){ console.log('hello'); } function fn(cb){ cd(); } fn(hello); >>hellothis
呼び出し元のオブジェクトへ参照を保持するキーワード
オブジェクトのメソッドとして実行された場合
'this'→呼び出し元オブジェクト関数として実行された場合
'this'→グローバルオブジェクトconst person = { name: 'Tom', hello: function(){ console.log('Hello' + this.name) } } person.hello(); >>Hello >>Tombindと'this'
bindによって'this'や引数を固定した新しい関数を作成
→bindによるthisの束縛function a(name){ console.log('hello' + name); } const b = a.bind(null, 'Tim') b();call,applyと'this'
bind→'this'や引数の参照先を変更、使用時点で実行しない
call,apply→'this'や引数の参照先を変更、同時に関数を実行するfunction a(name, name1){ console.log('hello' + name + ' ' + name1) } const tim = {name: 'Tim'}; const b = a.bind(tim); b(); //配列の場合には a.apply('tim', ['Tim', 'Bob']); //変数が独立している場合には a.call(tim, 'Tim', 'Bob');アロー関数
無名関数を記述しやすくした省略記法
アロー関数はthis,argments,new,prototypeの値を保持できないconst b = name => 'hello ' + name; console.log(b('Tom')) >>hello Tomコンストラクター関数
新しくオブジェクトを生成するための雛形となる関数
newでオブジェクトを生成することを「インスタンス化」
newで生成したオブジェクトを「インスタンス」function Person(name, age){ this.name = name; this.age = age; } const bob = new Person('Bob', 18) const tom = new Person('Tom', 65) const sun = new Person('sun', 34)prototype
・オブジェクトに存在する特別なプロパティ
・コンストラクター関数と合わせて使用function Person(name, age){ this.name = name; this.age = age; } Person.prototype.hello = function(){ console.log('hello' + this.name) } const bob = new Person('Bob', 18) const tom = new Person('Tom', 65) const sun = new Person('sun', 34) bob.hello(); tom.hello();new演算子
コンストラクター関数からインスタンスを作成するために使用する演算子
//コンストラクター関数の戻り値がオブジェクト function F(a, b){ this.a = a; this.b = b; return {}; } F.prototype.c = function() {} //残余引数構文で、不定数の引数を配列として表す function newOpe(c, ...args){ console.log(args); } const insta = newOpe(F ,1 ,2); console.log(insta); >>(2) [1, 2] //コンストラクター関数の戻り値がオブジェクト以外 function F(a, b){ this.a = a; this.b = b; } F.prototype.c = function() {} function newOpe(c, ...args){ const _this = Object.create(c.prototype); const result = c.apply(_this, args); console.log(result, _this); return _this; } const insta = newOpe(F ,1 ,2); console.log(insta); >>undefined F {a: 1, b: 2}instanceof
どのコンストラクターから生成されたオブジェクトかを確認する
//Fがコンストラクターとして定義 function F(a, b){ this.a = a; this.b = b; // return {a: 1}; } F.prototype.c = function(){} const instance = new F(1, 2); console.log(instance instanceof F) >>trueクラス
コンストラクター関数をクラス記述でかけるようにしたもの
class Person { constructor(name, age){ this.name = name; this.age = age; } hello(){ console.log('hello' + this.name) } } const bob = new Person('Bob', 23); console.log(bob); >>Person {name: "Bob", age: 23}クラス継承
他のクラスのプロパティーとメソッドを継承すること
class Person { constructor(name, age){ this.name = name; this.age = age; } hello(){ console.log('hello' + this.name) } } class Japanese extends Person { constructor(name, age, gender){ super(name, age); this.gender = gender; } hello(){ console.log('konnitiwa' + this.name); } bye(){ console.log('sayonara' + this.name); } } const bob = new Japanese('Bob', 23, 'men'); console.log(bob); >>Japanese {name: "Bob", age: 23, gender: "men"}スーパー
継承元の関数を呼び出すためのキーワード
反復処理
ループ文
for(let i = 0; i < 10; i++){ console.log(i); } let i = 0; while(i < 10){ console.log(i); i++; }演算子と優先順位
演算子とは・・・値(オペランド)を元に処理を行い、結果を返す記号のこと
演算子の優先順位
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Operator_Precedencelet a = 1 + 2 * 3; console.log(a); >>7 let b = (1 + 2)* 3; console.log(b); >>9ループ文とブロックスコープ
ループを使ったブロックスコープの場合には、宣言したループごとに全く異なるメモリー空間に参照を保持する
for(let i =0; i < 5; i++){ var j = i * 2; setTimeout(function(){ console.log(j); }, 1000); } >>⑤ 8配列とループ文
const array = [1, 2, 3, 4, 5]; for(let i =0; i < array.length; i ++){ console.log(array[i]); } let v, i = 0; while(v = array[i++]){ console.log(v); }for...inと列挙可能性
「for...in」・・・列挙可能プロティーに対して順不同で反復処理を実行する
プロトタイプチェーン内も列挙対象となる→Object.hasOwnProperty()
Symbolで定義したプロパティは「for...in」で列挙対象にならないconst obj = { prop1: 'value1', prop2: 'value2', prop3: 'value3' } Object.prototype.method = function(){} //enumerable: falseにすると列挙対象から外れる Object.defineProperty(Object.prototype, 'method',{ enumerable: false }); for(let key in obj){ console.log(key, obj[key]); } //Symbolで定義した場合 const s = Symbol(); const obj = { prop1: 'value1', prop2: 'value2', prop3: 'value3', [s]: 'value4' } Object.prototype.method = function(){} //enumerable: falseにすると列挙対象から外れる Object.defineProperty(Object.prototype, 'method',{ enumerable: false }); for(let key in obj){ console.log(key, obj[key]); }for...ofと反復可能性
イテレーターを持つオブジェクトの反復操作を行う
「イテレーター」とは反復操作を行う際に使用するオブジェクトオブジェクトに格納されているイテレーターの挙動に依存する
const array = ['a', 'b', 'c']; for(let v of array){ console.log(v) }MapとSet
「MapとSet」・・・データを管理するための入れ物
コレクションとも呼ぶ//Map const map = new Map(); const key1 = {}; map.set(key1, 'value1'); console.log(map.get(key1)) const key2 = function(){} map.set(key2, 'value2') console.log(map.get(key2)) let key3; map.set(key3 =0, 'value3') console.log(map.get(key3)) map.delete(key3); console.log(map.get(0)) for(const[k,v] of map){ console.log(k) } //Set const s = new Set(); s.add(key1); s.add(key2); s.add(key3); s.delete(key3); console.log(s.has(key2)) for(let k of s){ console.log(k) }参考
【JS】初級者から中級者になるためのJavaScriptメカニズム
【JavaScript&CSS】ガチで学びたい人のためのWEB開発徹底実践(フロントエンド編)
- 投稿日:2021-04-03T14:29:58+09:00
【WebIOPi】スマホでサーボモーターを制御!
スマホでサーボモーターを制御
今回はRaspberry Piを使用して、スマホからサーボモーターを制御します。
WebIOPiというライブラリを使用します。インストールは、こちらの記事を参考にしてください。今回作成するのはこんなイメージです。
Webページ上のスライダを動かすと、サーボモーターが動作します!
これから作成していくのは、
- スライダを表示する
HTML- スライダの値を取得する
JavaScript- サーボモーターを動作させる
Pythonスクリプトです。WebIOPiは、スライダの値を取得する
JavaScriptとサーボモータを動作させるPythonの橋渡しのような役割を果たします。それでは作成していきましょう!
HTML作成
HTMLファイルを作成していきます。今回は、サーボを2つ使用するため、スライダも2つ用意します。
WebIOPiを使用する際は、headタグ内に<script type="text/javascript" src="webiopi.js"></script>が必要です。忘れないようにしてください。
以下、スクリプトです。inputタグでtype=rangeとすることで、スライドバーが挿入されます。controller.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="mobile-web-app-capable" content="yes"> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <title>Servo Controller</title> <script type="text/javascript" src="webiopi.js"></script> </head> <body> <div align="center"> <tr> <td> <table> <tbody> <tr> <!--SERVO 1--> <td id="name1">SERVO 1</td> <td class="SS1"> <div class="slidecontainer1"> <input type="range" min="1000" max="2500" value="2000" step="10" class="slider" id="myRange1" oninput="func1()"> </div> </td> <td id="out1"></td> <!--SERVO 2--> <td id="name2">SERVO 2</td> <td class="SS2"> <div class="slidecontainer2"> <input type="range" min="1000" max="2500" value="2000" step="10" class="slider" id="myRange2" oninput="func2()"> </div> </td> <td id="out2"></td> </tr> </tbody> </table> </td> </tr> </div> </body> </html>現状のコードをブラウザで確認すると、こんな感じだと思います。
ただし、ブラウザの種類(Safari, Chrome, IEなどなど)によって、スライダの形は変わります。
後ほど、JavaScriptの実装を行うと、
<td id="out1"></td>部分にスライダの値が表示されます。CSS作成
現状のままのスライドバーだと、どこか味気ないのでアレンジしていきます。
CSSスクリプトを作成することで、HTMLをデコレーションすることができます。
デフォルトのスライドバーよりも、スライド部分が長く、取手も大きくなっています。
それでは作成していきます。
まず、どのブラウザでも同じようなスライダが表示されるよう設定します。
SafariやChrome,FireFoxでは同様のスライダが表示されると思います。他のブラウザはわかりません...controller.css#myRange1, #myRange2{ -webkit-appearance:none; background:#182005; height:10px; width: 30%; border-radius:8px; } input[type=range]::-webkit-slider-thumb{ -webkit-appearance:none; background:hsl(182, 90%, 61%); height:50px; width:20px; border-radius:40%; border: 1px solid rgb(66, 32, 129); } input[type=range]::-ms-tooltip{ display:none; } input[type=range]::-moz-range-track{ height:0; } input[type=range]::-moz-range-thumb{ background:hsl(182, 90%, 61%); height:50px; width:20px; border-radius:40%; border: 1px solid rgb(66, 32, 129); }次に、スライダのサイズ、位置を調整していきます。
#myRange1{ position:absolute; top:20%; transform:scale(2,2); right:50%; } #out1{ transform:scale(2,2); position:absolute; top:20%; right:20%; } #myRange2{ position:absolute; top:50%; right:50%; transform:scale(2,2); overscroll-behavior-y :none; } #out2{ transform:scale(2,2); position:absolute; top:50%; right:20%; }HTMLでCSSを読み込む
HTMLから作成したCSSファイルを読み込むよう、記述します。
HTMLとCSSを同じディレクトリに置き、HTMLのheadタグ内に以下を記述します。
controller.html<head> ... <link rel="stylesheet" type="text/css" href="controller.css"> </head>全体
これまでのHTML/CSS全体は次のようになります。
controller.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="mobile-web-app-capable" content="yes"> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <title>Servo Controller</title> <script type="text/javascript" src="webiopi.js"></script> <link rel="stylesheet" type="text/css" href="controller.css"> </head> <body> <div align="center"> <tr> <td> <table> <tbody> <tr> <!--SERVO 1--> <td id="name1">SERVO 1</td> <td class="SS1"> <div class="slidecontainer1"> <input type="range" min="1000" max="2500" value="2000" step="10" class="slider" id="myRange1" oninput="func1()"> </div> </td> <td id="out1"></td> <!--SERVO 2--> <td id="name2">SERVO 2</td> <td class="SS2"> <div class="slidecontainer2"> <input type="range" min="1000" max="2500" value="2000" step="10" class="slider" id="myRange2" oninput="func2()"> </div> </td> <td id="out2"></td> </tr> </tbody> </table> </td> </tr> </div> </body> </html>controller.cssbody{ margin:0; overscroll-behavior-y: none; } #myRange1, #myRange2{ -webkit-appearance:none; background:#182005; height:10px; width: 30%; border-radius:8px; } input[type=range]::-webkit-slider-thumb{ -webkit-appearance:none; background:hsl(182, 90%, 61%); height:50px; width:20px; border-radius:40%; border: 1px solid rgb(66, 32, 129); } input[type=range]::-ms-tooltip{ display:none; } input[type=range]::-moz-range-track{ height:0; } input[type=range]::-moz-range-thumb{ background:hsl(182, 90%, 61%); height:50px; width:20px; border-radius:40%; border: 1px solid rgb(66, 32, 129); } #myRange1{ position:absolute; top:20%; transform:scale(2,2); right:50%; } #out1{ transform:scale(2,2); position:absolute; top:20%; right:20%; } #myRange2{ position:absolute; top:50%; right:50%; transform:scale(2,2); overscroll-behavior-y :none; } #out2{ transform:scale(2,2); position:absolute; top:50%; right:20%; }最後に
今回は、WebIOPiでサーボを動作させるためのHTML/CSSファイルを作成しました。
次回は、JavaScriptを実装していきます。
- 投稿日:2021-04-03T11:31:37+09:00
Proxyについて
Proxyとは
プロパティーの操作のために独自の処理を追加するためのオブジェクト。
値の変更を検知して、独自の処理を追加する。
targetの処理に割り込んで別の処理をさせることができる仕組みのこと。const target = { a: 0 }; const handler = { set: function(target, prop, value, receiver) { console.log(`set: ${prop},${value}`); target[prop] = value; }, get: function(target, prop, receiver) { console.log(`get: ${prop}`); return target[prop]; }, deleteProperty: function(target, prop) { console.log(`del: ${prop}`); delete target[prop]; } } const pxy = new Proxy(target, handler); pxy.a = 1; pxy.a; delete pxy.a;target →対象のオブジェクト
handler →ターゲットに対する操作を表すオブジェクト
handlerはトラップと呼ばれる。
handlerとは、トラップの処理を上書きするもので各トラップに対して対応するhandlerが用意されてる。
上記のようにProxy経由で変更を加えることでpxy.a=1によって、変更があったことによりsetが呼び出される。
pxy.aによってgetが呼び出される。
deleteによって、deletePropertyが呼び出されることになる。Reflect
Reflect オブジェクトは JavaScript エンジンが内部で使用している汎用的な関数(内部メソッド)を格納しているオブジェクト。
class Men { constructor(name, age) { this.name = name; this.age = age; } } const m1 = Reflect.construct(Men, ['taro', 25]) console.log(m1); console.log(Reflect.has(m1, 'name'));.constructを使用すると、new 演算子によるインスタンス生成と同等のことを、関数の記述形式で行うことができる。
.has() メソッドを使用すると、in 演算子によるプロパティの判定と同等のことを、関数の記述形式で行うことができる。const cat = { na: 'Bob', _hello: function () { console.log(`hello ${this.na}`); } } const cat2 = { na: 'Suke', _hello: function () { console.log(`hello ${this.na}`); }, get hello() { return this._hello(); }, } Reflect.get(cat2, 'hello', cat);.get() メソッドは第3引数 receiver を指定することもできる。第1引数 targetObj と第2引数 prop で指定したオブジェクトのプロパティの値 targetObj[prop] がゲッターのとき、そのゲッターの処理内の this を第3引数で指定することができる。
ProxyとReflect
const targetObj = { a: 0, get value() { return this.b; } } const handler = { get: function(target, prop, receiver) { console.log(`get: ${prop}`); if(target.hasOwnProperty(prop)) { return Reflect.get(target, prop, receiver); } else { return 'none'; } } } const pxy = new Proxy(targetObj, handler); console.log(pxy.value);Proxyのhandlerから渡ってくる引数を、Reflectの引数として渡すことができる。
上記の場合だと、bに対してgetトラップを呼んでいるが、bのpropは存在しないので、noneが戻り値となる。基礎の定着は欠かせないものなので今後も基礎のインプットはしっかりしていこうと思います。ありがとうございました。
参考
https://qiita.com/irico/items/86a03db80bb081f59519
https://www.udemy.com/course/javascript-essence/
https://hidekazu-blog.com/javascript-reflect/
- 投稿日:2021-04-03T10:19:54+09:00
Reactで複数要素に動的にクリック→スクロールする(createRef, useRef)
Reactでクリック→スクロールする方法について
Reactで『クリック→該当部分までスクロール』を行う場合、以下のようにcreateRefを使用することがありました。
App.tsxconst ref = createRef<HTMLDivElement>()ここで定義したrefをスクロールさせたい箇所に渡して、
App.tsxconst handleJump = useCallback(() => { ref!.current!.scrollIntoView({ behavior: "smooth" }) }, [ref])クリック時にスクロールするよう関数を定義する。すると以下のようにクリック→スクロールができます。
ソースコードは以下を参照いただければ幸いです。
CodeSandboxですが、これだとスクロール箇所が増える度に、ユニークなrefを定義していく必要があるので、
複数の要素に動的にスクロールさせることを検討しました。結論
以下がソースコードです。
CodeSandboxApp.tsxtype Item = { title: string; background: string; service: string; otherContent?: boolean; }; const items: Item[] = [ { title: "コンテンツ1", background: "skyblue", service: "サービス1" }, { title: "コンテンツ2", background: "yellow", service: "サービス2" }, { title: "コンテンツ3", background: "green", service: "サービス3", otherContent: true } ];↑のような配列のデータがある場合、
App.tsxconst pageRef = useRef(items.map(() => createRef<HTMLDivElement>())); const scrollToView = (id: number) => { pageRef.current[id]!.current!.scrollIntoView({ behavior: "smooth" }); };↑のコードのように
- mapを使ってrefの配列を作り
- 関数(scrollToView)の引数にはidを受け取るとすることでrefを何度も定義せずに動的にクリック→スクロールをさせることができました!
参考
- 投稿日:2021-04-03T09:52:08+09:00
documentとHTMLElementにgetElementByXPath定義する
ブラウザのコンソールでのちょっとした作業用。探せば色々見つかるのだけど、
documentに対してだけではなく、どのHTMLElementに対しても実行できるところまでまとまってるのはあまりないようだったので。(() => { const __getElementByXPath = function(path, root) { const result = document.evaluate(path, root, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null) return result.snapshotLength > 0 ? result.snapshotItem(0) : null } const __getElementsByXPath = function(path, root) { const array = [] const result = document.evaluate(path, root, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for (let i = 0; i < result.snapshotLength; i++) { array.push(result.snapshotItem(i)); } return array; } document.getElementByXPath = function(path) { return __getElementByXPath(path, this) } document.getElementsByXPath = function(path) { return __getElementsByXPath(path, this) } HTMLElement.prototype.getElementByXPath = function(path) { return __getElementByXPath(path, this) } HTMLElement.prototype.getElementsByXPath = function(path) { return __getElementsByXPath(path, this) } })()
- 投稿日:2021-04-03T09:17:49+09:00
GASで別のHTMLファイルをインクルードする
はじめに
「画面の共通部分を部品化して、複数の画面から使いたい」ということはよくあると思います。
これまで経験したプロジェクトでもincludeという関数を作ってそのようなことを実現してきていましたが、インクルード先ではスクリプトレットが使えない、など制限があるものでした。
もっと使いやすくならないかといろいろ試した結果を書いておきます。対象者
- 基本的な JavaScript が読める方
- GAS で Web アプリを作ったことがある方
いきなり結論
試行錯誤の結果、これでやりたかった以下のことができました。
- インクルード先の HTML でもスクリプトレットを使える
- インクルードのネスト(インクルード先でインクルード可能)
- インクルード先にパラメーターを渡せる
Code.gs/** * HTMLをインクルードする. * * @param {string} filename HTMLファイル名 * @param {Object} params インクルード先に渡すパラメーター */ function include(filename, params) { const template = HtmlService.createTemplateFromFile(filename); if (params) { for (const key in params) { template[key] = params[key]; } } return template.evaluate().getContent(); }動作確認
の
include関数を動作確認します。
以下のように複数の HTML を作りました。index.html<!DOCTYPE html> <html> <head> <base target="_top"> <?!= include('css'); ?> </head> <body class="index"> <h2>this is index.</h2> <?!= include('sub1'); ?> <?!= include('sub2', { a:1, b:2 }); ?> </body> </html>css.html<style> div { margin: 5px; padding: 5px; } .index { background-color: #ffffff; } /* 白 */ .calc { background-color: #99ffff; } /* 水色 */ .sub1 { background-color: #99ff99; } /* 緑 */ .sub2 { background-color: #ffff99; } /* 黄色 */ .sub2_1 { background-color: #9999ff; } /* 紫 */ </style>calc.html<div class="calc"> <h4>this is calc.</h4> <?= a ?> + <?= b ?> = <?= a + b ?> </div>sub1.html<div class="sub1"> <h3>this is sub1.</h3> <? const a = 'A'; const b = 'B'; ?> <?= a + b ?> </div>sub2.html<div class="sub2"> <h3>this is sub2.</h3> <?!= include('calc', { a:a, b:b }); ?> <?!= include('sub2_1', { a:a*2, b:b*2 }); ?> </div>sub2_1.html<div class="sub2_1"> <h3>this is sub2_1.</h3> <?!= include('calc', { a:a, b:b }); ?> </div>HTML どうしの関係は以下のようになっています。
- index.html
- css.html
- sub1.html
- sub2.html
- calc.html
- sub2_1.html
- calc.html
つづけて
Code.gsにdoGetを追加します。Code.gsfunction doGet(e) { return HtmlService.createTemplateFromFile('index').evaluate(); }最後に作成した Web アプリにアクセスすると以下のように表示され、期待通りの動作が確認できました。
所感
- GAS で HTML を扱うクラスには、
HtmlOutputやHtmlTemplateがあり、色々と機能を持っているようなのでもっと深堀りしてみると面白そうですdoGet関数はHtmlOutputを返す必要があるようなので、最初はreturn HtmlService.createHtmlOutputFromFile('index');と書いていましたが、これではインクルードが期待通り動きませんでした。
- 一度
HtmlTemplate経由でHtmlOutputを生成することに意味がある模様
- 投稿日:2021-04-03T01:51:03+09:00
Node.jsでの標準入力の書き方をまとめてみた
初めに
最近になって、あらためて Node.jsでatCoderやLeetCodeでアルゴリズム問題を解いてみようと思ったのですが、Node.jsの標準入力の書き方は調べてみると色々あってどれがいいんだろうと少し迷ったので、いい機会なのでまとめてみました。
Node.js(標準入力)のパターン
1.
/dev/stdinをreadFileSyncを利用して読み込むinput1.jsvar input = require("fs").readFileSync("/dev/stdin", "utf8"); console.log(input);標準入力を表す特殊ファイル(
/dev/stdin)をreadFileSyncで読み込みます。メリット : 記述がかなりシンプル
デメリット:/dev/stdinはUNIX系のOSでしか利用できないので、Windowsでは利用できない2.
process.stdinをreadlineモジュールを利用して読むinput2.jsprocess.stdin.setEncoding("utf8"); var lines = []; var reader = require("readline").createInterface({ input: process.stdin, }); reader.on("line", (line) => { //改行ごとに"line"イベントが発火される lines.push(line); //ここで、lines配列に、標準入力から渡されたデータを入れる }); reader.on("close", () => { //標準入力のストリームが終了すると呼ばれる console.log(lines); });改行単位で
Readable streamを処理できるreadlineモジュールを利用してデータを読み込みます。メリット : 改行単位で読み込めるので、1行ごとで処理を行う場合や一度に処理するにはインプットが大きすぎる場合には便利。
デメリット: 改行ごとの処理と読み込み完了後の処理に分かれるため、記述量が多い。3.
process.stdinをfor await...ofを利用して読むinput3.js(async () => { const buffers = []; for await (const chunk of process.stdin) buffers.push(chunk); const buffer = Buffer.concat(buffers); const text = buffer.toString(); console.log(text); })()標準入力を非同期のイテラブルなオブジェクトとしてループで処理します。
メリット : 分割して読み込みされるので、一度に処理しきれないインプットを扱う場合には便利。
デメリット: 文字列への変換が必要なので、やや記述量が多い。またデータが分割される場所をコントロールしにくい。おわりに
Windowsを利用していない場合やインプットが多くない場合は1の方法を利用して、それ以外のケースでは必要に応じて2または3の方法で記述するのが良さそうだなと思いました。
それでは素敵なNode.jsライフを。
参考リンク














