- 投稿日:2020-06-19T23:25:59+09:00
regl 利用でアニメーション(+ React)
こんにちは。
regl (Functional WebGL, GitHub) + React を利用しランダムな点のアニメーションを描いてみました。今回、use-interval (GitHub) を用いてみました。
ただし、fragColorなど未だに十分理解できていないので変な箇所があるかもしれません。App.jsimport React from 'react'; import createRegl from 'regl'; import useInterval from 'use-interval'; const App = () => { const pointSize = 14; const pointRandom = () => [2 * Math.random() - 1, // x 2 * Math.random() - 1, // y Math.random(), Math.random(), 0]; // r, g, b const generatePoints = (nPoints) => Array(nPoints).fill(0).map(pointRandom); const regl = createRegl(document.querySelector('.canvas')); const frag = ` precision mediump float; varying vec3 fragColor; void main() { gl_FragColor = vec4(fragColor, 1); } `; const vert = ` varying vec3 fragColor; attribute vec2 pos; attribute vec3 color; uniform float pointSize; void main() { fragColor = color; gl_PointSize = pointSize; gl_Position = vec4(pos, 0, 1); } `; const drawPoints = regl({ frag: frag, vert: vert, primitive: 'points', count: (context, props) => props.points.length, attributes: { pos: (context, props) => props.points.map(d => d.slice(0, 2)), color: (context, props) => props.points.map(d => d.slice(2, 5)) }, uniforms: { pointSize: pointSize, points: regl.prop('points') } }); const clearRegl = () => regl.clear({ color: [0, 0, 0, 0], depth: 1 }); let i = 0; useInterval(() => { clearRegl(); drawPoints({points: generatePoints((++i%2)*4+2)}); }, 1000); return <div className='canvas'></div>; }; export default App;
- 投稿日:2020-06-19T23:25:59+09:00
ランダムな点のアニメーション(regl + React 利用)
こんにちは。
regl (Functional WebGL, GitHub) + React を利用しランダムな点のアニメーションを描いてみました。今回、use-interval (GitHub) を用いてみました。App.jsimport React from 'react'; import createRegl from 'regl'; import useInterval from 'use-interval'; const App = () => { const pointSize = 14; const pointRandom = () => [2 * Math.random() - 1, // x 2 * Math.random() - 1, // y Math.random(), Math.random(), 0]; // r, g, b const generatePoints = (nPoints) => Array(nPoints).fill(0).map(pointRandom); const regl = createRegl(document.querySelector('.canvas')); const frag = ` precision mediump float; varying vec3 fragColor; void main() { gl_FragColor = vec4(fragColor, 1); } `; const vert = ` varying vec3 fragColor; attribute vec2 pos; attribute vec3 color; uniform float pointSize; void main() { fragColor = color; gl_PointSize = pointSize; gl_Position = vec4(pos, 0, 1); } `; const drawPoints = regl({ frag: frag, vert: vert, primitive: 'points', count: (context, props) => props.points.length, attributes: { pos: (context, props) => props.points.map(d => d.slice(0, 2)), color: (context, props) => props.points.map(d => d.slice(2, 5)) }, uniforms: { pointSize: pointSize, points: regl.prop('points') } }); const clearRegl = () => regl.clear({ color: [0, 0, 0, 0], depth: 1 }); let i = 0; useInterval(() => { clearRegl(); drawPoints({points: generatePoints((++i%2)*4+2)}); }, 1000); return <div className='canvas'></div>; }; export default App;
- 投稿日:2020-06-19T23:23:50+09:00
fetch template
phpでデータを取ってくる。
変数(parameter)と値(value)をsetDataに渡して、setDataToDB.phpでは例えばPDOでDBにデータをupdate(or insert)する。
setDataToDB.phpがもし戻り値としてデータセットを返す場合には
then((json)の中で処理する。fetchSample.jsfunction setData(paramater, value) { var res = fetch("./setDataToDB.php", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "value": value }) }).then((response) => { if(response.ok) return response.json(); // レスポンスをテキストとして変換する }).then((json) => { //console.log(json[0]["someParameter"]); //複数のデータセットが帰ってくる場合は0でない。 //何らかの処理; return json[0]["otherParameter"]; //returnで返らない? }); }fetchで取ってきた値を他の関数でも使いたい場合にはどうするの?
受信するphp
setDataToDB.php<?php $str_json = file_get_contents('php://input'); $json_obj = json_decode($str_json, true); //データベース接続ーーーーーーーーーーーーーーーー $dsn = 'mysql:dbname=データベース名;host=localhost'; $user = 'ユーザ名'; $password = 'パスワード'; $dbh = new PDO($dsn, $user, $password); //SQL処理ーーーーーーーーーーーーーーーーーーーーー $dbh->beginTransaction(); $stmt = $dbh->prepare("update テーブル名 set カラム名=:mode;"); $stmt->bindParam(':mode', $json_obj["value"], PDO::PARAM_STR); $stmt->execute(); $dbh->commit(); ?>
- 投稿日:2020-06-19T22:44:41+09:00
現代のフロントエンド開発シリーズ(7)- フロントエンドでもエンジニアリング必要があるのはなぜですか?
ソフトウェアの品質を改善するために、定量化および標準化された方法でソフトウェアを開発する
では、なぜフロントエンドを体系的にする必要があるのでしょうか。私はいくつかの点があるかと思います:
- 以前、フロントエンドの複雑さはそれほど高くなく、静的なページを表示するだけで十分でしたが、技術の進歩に伴い、エンジニアリングに役立つツールを導入しないと、多くのアプリケーションがますます複雑になっています(googleドライブ、facebookなど)。エンジニアリングの手法を導入しないと、プログラムの開発は管理しにくくなるでしょう
- フロントエンドの開発が徐々に成熟しているため、フロントエンドエンジニアリング用のツール(gulp、webpackなど)を開発する余裕があります。
実際、フロントエンドエンジニアリングは、「ああ、エンジニアリングを今すぐやろう」みたいな流行語ではありませんが、開発中は「ああ、できるなら」のように感じられます。
今日は、フロントエンドエンジニアリングを行う際の一般的な概念と構築ツールをいくつか紹介します。
Webpack
大規模なフロントエンドプロジェクトの中で、
webpackはほとんど不可欠なツールです。JavaScript初期の頃は、モジュールを管理する適切な方法がなく、モジュール化にするには、いくつかの設計パターンとエンジニア自身の経験などにしか頼ることができませんでした。
例として、モジュールをファイルで分けると、すぐに次のような依存関係の問題が発生します。
<script type="text/javascript" src="a.js"></script> <script type="text/javascript" src="b.js"></script> <script type="text/javascript" src="c.js"></script> <script type="text/javascript" src="d.js"></script>仮にdはa、b、cに依存するとして、順番が変更されるとバグが爆発します。時間が経つにつれ、全員が同じ場所ですべてをモジュール化することを望まなくなります。
依存関係メソッドを定義するCommonJSやRequireJSなどのいくつかのソリューションがあり、ライブラリを使用して依存関係の問題を解決できました。
ただし、まだいくつかの問題があります。
- 依存関係は自分で宣言する必要があります
- 動的インポート(Dynamic Import)を実行する場合、あまり解析する方法はない
webpackが登場した後、非常に強力なので、すぐに開発者に好まれました。JavaScriptファイルに加えて、ローダーが設定されている限り、画像、フォントファイル、CSSなどをロードすることもできます。
Tree Shaking
初めてこの言葉を見たときは、すごくかっこよくて、概念的に考えやすかったと思いますね。木を振ると枯れ葉が落ちてしまいました。
ここで、枯れ葉の実は不要なコードを指します。ツリーの揺れとは、プログラムを作成するときにモジュールコードの一部しか使用しないことがあるため、ファイル全体をバンドルする必要がないことを指します。バンドルサイズを小さくすることができます。
このコードがほかの部分も使用されていないことをどのように知るかのを問題です。
一般的な静的言語では、コンパイラーに依存してこれを行うことができますが、JavaScriptでは、自分自身にのみ依存できます。幸い、webpack2後、開発者のためにツリーシェーキング関数を追加しましたが、静的に分析できるようにするためにes6インポートの構文に依存しています。
デッドコードの排除(dead code elimination)
以下のような絶対に実行しないコードを削除することができ、略してDCEと呼ばれます。
`javascript`
if(false){
// コード
}
条件は
falseのため、コードをここで実行することはできず、トランスパイルを実行するときにプラグを抜くことができます。詳細については、Danの開発モードの仕組みを参照してください。
コード分割(Code Splitting)
コード分割の主な目的は、アプリケーションのコードとサードパーティライブラリのコードを2つのファイルに分割することです。
通常、使用するThird Partyのコードは、バージョン番号が変更のない限りほとんど変更されません。これらのコードをパッケージに分解し、CDNにアップロードしてキャッシュを有効にすることができるため、変更がある部分がリロードすることができます。アプリケーション自体のコードだけ、将来バージョン番号が変わったときに、ハッシュメカニズムで簡単に更新できます。
最小化
開発中は、目的を理解するために他の人(そして私たち自身)が理解できるように変数に明確な名前を付けますが、本番環境のビルドでは必要ありません。このとき、変数名を最小限に抑えることができます。
前述のように、パラメーターを渡す方法により、パラメーターで使用されているパラメーターを明確に把握できるだけでなく、最小化するのにも便利です。
動的なインポート(dynamic import)
ユーザーがホームページをロードするだけの場合、プロファイルページに入る必要がない場合があります。
したがってホームページに関連するコードのみをロードし、ユーザーがプロファイルページにアクセスしたときにコードをロードします。まとめ
Webpack構成ファイルは、使いやすい構成ファイルを書き込むのは非常に困難で、テストとパラメーターの調整に多くの時間がかかります。
完璧な構築メカニズムが必要な場合は、さまざまなプラグインを研究するのに長い時間がかかることがあります。もちろん、時間をかけて作図ツールを勉強すれば異なる意見を持つでしょう。設定の時間を節約したい場合は、
create-react-appまたはvue-cliやその他のエコシステム構築ツールを使用することもできます。ほとんどの設定は実際には同じですが(フロントエンド開発セクションで)、ニーズと使用条件に応じて異なる調整が必要になる場合があります。
さらに、アップグレードは非常に面倒な場合があります。たとえば、webpackのアップグレード時に、重大な変更が頻繁に発生します(それほど重大ではありませんが)。構成ファイルを変更するための調査にさらに時間がかかります。
記事のタイトルはフロントエンドエンジニアリングに関するものですが、この記事では主に構築ツールとそれに対応する概念について説明しました。
- 投稿日:2020-06-19T22:35:43+09:00
レガシーなウェブシステムのSELECTを使いやすくするJavascript(ブックマークレット)
困りごと
うちの会社には、スーパーなレガシーシステムがありまして、、、
HTMLで作られているWebシステムなのですが、<select>がめっちゃ長いんですよ。選択肢が50とか100とかあります。管理者が選択肢を作れるようなシステムなので、増やす一方です。その画面に入力する身としては、たまったもんじゃありません。目で見て選択するなんて無理です。
ということで、レガシーシステムに息を吹き込むブックマークレットを作りました。
使うもの
- Javascript
- jQuery
レガシーシステムが相手なので、jQueryで楽させてもらいます。
コード
こんな感じになりました。コメント入れて60行くらい。
さすがにレガシーといえど、jQueryを使ってコード書きました。select-filter.jsvoid ((function (f) { if (window.jQuery && jQuery().jquery > '2.0') { console.log('use jquery'); f(jQuery); } else { console.log('load jquery'); var script = document.createElement('script'); script.src = '//code.jquery.com/jquery-3.2.1.min.js'; script.onload = function () { var $ = jQuery.noConflict(true); f($); }; document.body.appendChild(script); } })( function ($, undefined) { $('select').each(function (i, select) { //selectにidが無ければ、nameから作り出す if (!select.id) { select.id = select.name + '_' + i; } let options = $(select).children('option').length; let text = `<input type="text" data-selectid="${select.id}" class="__ft">`; let badge = `<span id="${select.id}_hit">${options}</span>/<span>${options}</span>`; //textboxを突っ込む $(select).before(`${text} ${badge} <br>`); }); //追加したテキストボックス全てに適用するイベントハンドラ $('input.__ft').on('input', function (ev) { let selector = '#' + $(this).data('selectid'); let selectid = $(selector).attr('id'); console.log(selector); let txt = this.value; console.log(txt); //フィルタした瞬間に選択肢を元に戻すならコメントはずす // $(selector).prop("selectedIndex", 0); let hit = 0; $(selector).children('option').each(function (i, e) { if (isDefaultOption(e) || matchOption(e, txt)) { $(e).show(); hit++; } else { $(e).hide(); } }); $(`span#${selectid}_hit`).text(hit); }); //デフォルトオプションの判定(システムに合わせて適当に書き換える必要があるかも) function isDefaultOption(element) { return $(element).val() == '' || $(element).hasClass('default'); } //表示する条件(半角全角の調整など必要なことをやるべし) function matchOption(element, input) { return $(element).text().indexOf(input) != -1; } } ))コードはここにも置いてあります。
https://github.com/kanaxx/kanaxx.github.io/tree/master/bookmarklet/select-filterコードの説明
説明するほどのコードじゃないですけど、一応。
$('select')なので、画面にある全ての<select>が対象です。<select>に
idが振られていないようなレガシーシステムの場合のことも考えて、nameの値を元に<select>にidを追加しています。テキストボックスの真下にある<select>の
idをテキストボックスのdata-selectidとして仕込むことで関連を持たせ、classに__ftクラスを割り当てる
__ftクラス全体にinputイベントハンドラを登録するイベントハンドラでselectからoptionを探して、部分一致したものだけ
show()する実行
選択肢が多いページが必要だったので、自分で作りました。
https://kanaxx.github.io/bookmarklet/select-filter/index.htmlF12でDevToolを開いて、Consoleにスクリプトを投げ込んでください。
ブックマークレット(スクリプト)を実行
画面内にあるセレクトの上に、テキストボックスが生まれる!
フィルタ実行
テキストに「都」を入れると「東京都」と「京都府」で2つの選択肢だけになるので、選びやすい。3/48表記は左がマッチした選択肢数、右が選択肢の総数です。テキストボックスから消すと全部元通りです。
まとめ
程よく汎用的に作ったので、たいていのサイトの選択肢<select>をハックできるはずです。一番上の選択肢を上手に判定する方法が見つからなかったけど。
気に入ったら、ブラウザのブックマークバーに登録してみてください。参考資料
国の一覧はここを一覧を使わせていただきました。5年前の国名らいし2020年だと変わってるかもしれません。
https://qiita.com/tao_s/items/32b90a2751bfbdd585ea
- 投稿日:2020-06-19T22:35:43+09:00
レガシーなウェブシステムのSELECTタグを使いやすくするJavascript(ブックマークレット)
久しぶりにブックマークレット
困りごと
うちの会社には、スーパーなレガシーシステムがありまして、、、
HTMLで作られているWebシステムなのですが、<select>がめっちゃ長いんですよ。選択肢が50とか100とかあります。管理者が選択肢を作れるようなシステムなので、増やす一方です。その画面に入力する身としては、たまったもんじゃありません。目で見て選択するなんて無理です。
ということで、レガシーシステムに息を吹き込むブックマークレットを作りました。
使うもの
- Javascript
- jQuery
レガシーシステムが相手なので、jQueryで楽させてもらいます。
コード
こんな感じになりました。コメント入れて60行くらい。
さすがにレガシーといえど、jQueryを使ってコード書きました。select-filter.jsvoid ((function (f) { if (window.jQuery && jQuery().jquery > '2.0') { console.log('use jquery'); f(jQuery); } else { console.log('load jquery'); var script = document.createElement('script'); script.src = '//code.jquery.com/jquery-3.5.1.slim.min.js'; script.onload = function () { var $ = jQuery.noConflict(true); f($); }; document.body.appendChild(script); } })( function ($, undefined) { $('select').each(function (i, select) { //selectにidが無ければ、nameから作り出す if (!select.id) { select.id = select.name + '_' + i; } let options = $(select).children('option').length; let text = `<input type="text" data-selectid="${select.id}" class="__ft">`; let badge = `<span id="${select.id}_hit">${options}</span>/<span>${options}</span>`; //textboxを突っ込む $(select).before(`${text} ${badge} <br>`); }); //追加したテキストボックス全てに適用するイベントハンドラ $('input.__ft').on('input', function (ev) { let selector = '#' + $(this).data('selectid'); let selectid = $(selector).attr('id'); console.log(selector); let txt = this.value; console.log(txt); //フィルタした瞬間に選択肢を元に戻すならコメントはずす // $(selector).prop("selectedIndex", 0); let hit = 0; $(selector).children('option').each(function (i, e) { if (isDefaultOption(e) || matchOption(e, txt)) { $(e).show(); hit++; } else { $(e).hide(); } }); $(`span#${selectid}_hit`).text(hit); }); //デフォルトオプションの判定(システムに合わせて適当に書き換える必要があるかも) function isDefaultOption(element) { return $(element).val() == '' || $(element).hasClass('default'); } //表示する条件(半角全角の調整など必要なことをやるべし) function matchOption(element, input) { return $(element).text().indexOf(input) != -1; } } ))コードはここにも置いてあります。
https://github.com/kanaxx/kanaxx.github.io/tree/master/bookmarklet/select-filterコードの説明
説明するほどのコードじゃないですけど、一応。
$('select')なので、画面にある全ての<select>が対象です。<select>に
idが振られていないようなレガシーシステムの場合のことも考えて、nameの値を元に<select>にidを追加しています。テキストボックスの真下にある<select>の
idをテキストボックスのdata-selectidとして仕込むことで関連を持たせ、classに__ftクラスを割り当てる
__ftクラス全体にinputイベントハンドラを登録するイベントハンドラでselectからoptionを探して、部分一致したものだけ
show()する実行
選択肢が多いページが必要だったので、自分で作りました。
https://kanaxx.github.io/bookmarklet/select-filter/index.htmlF12でDevToolを開いて、Consoleにスクリプトを投げ込んでください。
ブックマークレット(スクリプト)を実行
画面内にあるセレクトの上に、テキストボックスが生まれる!
フィルタ実行
テキストに「都」を入れると「東京都」と「京都府」で2つの選択肢だけになるので、選びやすい。3/48表記は左がマッチした選択肢数、右が選択肢の総数です。テキストボックスから消すと全部元通りです。
まとめ
程よく汎用的に作ったので、たいていのサイトの選択肢<select>をハックできるはずです。一番上の選択肢を上手に判定する方法が見つからなかったけど。
気に入ったら、ブラウザのブックマークバーに登録してみてください。参考資料
国の一覧はここを一覧を使わせていただきました。5年前の国名らいし2020年だと変わってるかもしれません。
https://qiita.com/tao_s/items/32b90a2751bfbdd585ea
- 投稿日:2020-06-19T20:23:30+09:00
obniz を特定の人だけアクセスできるようにする
はじめに
obnizは、ハードウェアをあたかもWebサイトの要素の一つであるかのように制御することができるマイコン開発ボードです。
購入して電源を繋いで、Wi-Fiのパスワードを入力すれば、専用のobniz Cloudに接続され、あとはHTML+JavaScriptなどでプログラムを記述し、スマートフォンやパソコンのブラウザから制御することができます。
ファームウェアの書き換えが不要なので、I2C通信など、センサの制御をちょっと試したいときは、ブラウザで再読み込みするだけで良いのでとても便利に使っています。
obniz OS搭載のM5StickCなら、6軸センサや赤外線LEDも搭載されているので、マイコン開発がほぼ遠隔でできてしまいますね。obniz Cloudのリポジトリでは、HTMLやJavaScriptのプログラムを書いて保存することができるようになっていて、公開または非公開から選べるようになっています。非公開の場合は、自分のobniz アカウントでログインする必要があります。
今回は、自分と、共有した相手だけobnizを制御できるようにしたいと思い、比較的簡単に実現する方法をまとめます。
今回の方法では、必ずしも安全というわけではなく、セキュリティは弱い方法です。あくまでも簡易的に実装してみた方法ですので、今回のコードを使用される場合は、その点を必ずご承知おきください。obniz のアクセストークン
obniz 初回起動時の状態では、8桁のobniz IDを入力すれば、誰でもそのobnizを制御できる状態になっています。
https://docs.obniz.io/ja/guides/common/connection/
に記載されているように、obniz 開発者コンソールのデバイスから、アクセストークンを発行することができます。アクセストークンを発行すれば、以下のように正しいアクセストークンを入力しなければ、obnizに接続することができなくなります。
index.htmlnew Obniz('1234-5678', {access_token: 'your token here'});アクセストークンは、更新や削除もできるようになっています。
そこでこのアクセストークンを、HTMLの外から入力できるようにしてあげます。プロンプトと Cookie による方法
window.promptと Cookie を利用して、初回接続時にはプロンプトを表示し、アクセストークンの入力を促します。一度接続できれば、アクセストークンを Cookie に保存し、次回以降は Cookie から呼び出して自動的に接続します。
JavaScript Cookieを使用しています。js.cookie.jsへのパスは適宜書き換えてください。index.html<script src="/path/to/js.cookie.js"></script> <script> var access_token; if(Cookies.get('obniz_access_token')){ access_token=Cookies.get('obniz_access_token'); }else{ access_token=window.prompt("Input token", ""); } var obniz = new Obniz("XXXX-XXXX", {access_token}); obniz.onconnect = async function () { Cookies.set('obniz_access_token', access_token, { expires: 7 });この方法であれば、URLとobnizのアクセストークンを共有し、URLにアクセスしてもらって、アクセストークンをコピペしてもらえば良いですね。
URLパラメータによる方法
ただURLにアクセスするだけで繋げられる方法です。共有するURLは、通常のobniz Cloudの実行URLの後に、
?access_token=XXXXXXXXXXXXXXXのような形で、obniz のアクセストークンを繋げたものになります。index.html<script> var queryStr = window.location.search; var queryObj = {}; if(queryStr){ queryStr = queryStr.substring(1); let paramArr = queryStr.split('&'); paramArr.forEach(p=>{ let elem=p.split('='); queryObj[decodeURIComponent(elem[0])]=decodeURIComponent(elem[1]); }); } var access_token=queryObj.access_token; var obniz = new Obniz("OBNIZ_ID_HERE", {access_token});
http://obniz.io/users/XXX/repo/XXXXX.html?access_token=XXXXXXXXXXXXXXX
のようなURLとなります。2つの方法を統合
2つの方法を統合し、一部修正しました。
URLパラメータにあればそれを使い、接続できればCookieに保存します。
URLパラメータになければ、Cookieを呼び出し、Cookieにもなければ、プロンプトで入力を促します。index.html<script src="/path/to/js.cookie.js"></script> <script> var queryStr = window.location.search; var queryObj = {}; if(queryStr){ queryStr = queryStr.substring(1); let paramArr = queryStr.split('&'); paramArr.forEach(p=>{ let elem=p.split('='); queryObj[decodeURIComponent(elem[0])]=decodeURIComponent(elem[1]); }); } var access_token=queryObj.access_token; if(!access_token){ access_token=Cookies.get('obniz_access_token'); } if(!access_token){ access_token=window.prompt("Input token", ""); } var obniz = new Obniz("XXXX-XXXX", {access_token}); obniz.onconnect = async function () { Cookies.set('obniz_access_token', access_token, { expires: 7 });最後に
この方法を使えば、obniz のプログラムを友だちに共有しやすくなりますね!
Let's みんなで obniz!
- 投稿日:2020-06-19T20:08:17+09:00
Javascriptの復習(5)
この記事について
Javascript初学者がアウトプットの場として書いている記事なので、スルーしてもらっても大丈夫です。Rubyを学習した後なのでRubyと絡めて記事を書くこともあります。
ファイルの分割
コードの量が増えてくるとコードの修正が大変になります。1つのファイルに書くのではなく、複数のファイルに分割することで管理しやすくなります。今回は分割する時のコードの書き方を説明します。
ファイルを分割する時は、それぞれのファイルを関連付け必要な値を渡さなければなりません。
「export default クラス名や定数等」とクラスや定数の下に書く事で他のファイルへとエクスポートできます。
※因みにexportは英語で「輸出」を意味します。animal.jsclass Animal { } //クラス定義後に書く事で他のファイルで使うように準備できます。 export default Animal;次に、上記のAnimalクラスを使いたいファイルで「import クラス名や定数 from "./ファイル名"と書きます。
これにより、そのファイル内でAnimalクラスを使用できます。ファイル名の拡張子である".js"は省略できます。
※因みにimportは英語で「輸入」を意味します。cat.js//この記述でcat.js内でanimal.jsで定義したAnimalクラスを使用できる。 import Animal from "./animal"デフォルトエクスポート
これまで書いていたexport defaultですが、この書き方は「デフォルトエクスポート」と言います。importする時にfrom"./ファイル名"とファイルを指定すると思いますが、この記述を書くと自動的にexportに書かれているクラスや定数が読み込まれます。先程「import クラス名や定数 from "./ファイル名"」と書くと説明しましたが、ここにあるクラス名や定数名はexportに書いてあるものと一致しなくても実は大丈夫です。importでファイル名を指定した時点で、自動的にexport defaultに書かれているクラス名や値がインポートされます。
animal.jsclass Animal { } export default Animal;cat.js//クラス名がAnimalじゃなくても問題ない。このCat.jsファイル内では、AnimalクラスはBeastクラスとして扱われる。 import Beast from "./animal"ただし、エクスポートできるのは1つの値で、複数のクラスや定数をエクスポートしようとするとエラーが出ます。
名前付きエクスポート
名前付きエクスポートとはdefaultと書かずに名前を指定してエクスポートする方法で、名前は{}で囲います。
dog.jsconst dog1 = "チワワ" //定数名を指定してエクスポート export {dog1};インポートする時も名前を書きます(importとfromの間)
script.js//インポートも定数名を書く。 import {dog1} from"./dog" console.log(dog1);名前付きエクスポートはデフォルトエクスポートと違い複数のクラス及び定数をエクスポートすることができます。
dog.jsconst dog1 = "チワワ" const dog2 = "チャウチャウ" //{}の中にエクスポートしたい物を全て書く export {dog1, dog2};script.js//インポートも同様に全て書く import {dog1, dog2} from"./dog" console.log(dog1); console.log(dog2);パッケージ
Javascriptには「パッケージ」と呼ばれる誰かが既に作ってそのまま支える物があります。自分のプログラムでも引用して使用できます。フレームワークと同じ物だと思います。今回は以下のパッケージを使用しました。
chalk : コンソールに出力する文字に色を付けられる。
redline-sync : コンソールに値を手入力できる(Rubyのgetsのような機能)パッケージを使用する時は、importでパッケージをインポートします(exportはいらない)。
script.js//定数はなんでも良いが、同じ名前の方がわかりやすい。 import chalk from "chalk";インポートしたファイルではパッケージの機能を使えるようになります。
script.js//文字列をchalk.yellow等で囲むと色が変化する。 import chalk from "chalk"; //文字がイエローになる console.log(chalk.yellow("Hello World")); //背景色がシアンになる(bgはback groundの略) console.log(chalk.bgCyan("Hello World"));redline-syncというパッケージを使用すると、コンソール上に値(文字)を入力することができ、その値をプログラム上で使用できるようになります。今回はredlineSyncという定数としてredline-syncを使用できるようにしていますが、readlineSync.question("質問文")というコードを書くことで、質問文が表示され、文字が入力できるようになります。
整数を入力したい時はquestionではなくquestionIntと書きます(integerのintだと思う。)script.js//redline-syncをredlineSyncとしてインポートする。 import redlineSync from "redline-sync"; //質問文が表示されて、文字を入力できるようになる。入力された文字は定数wordに代入される。 const word = readlineSync.question("文字を入力してください : "); console.log(`${word}と入力されました。`)
- 投稿日:2020-06-19T19:55:59+09:00
あるだけで一寸嬉しいちょっとした簡易ブックマークレット(たち)
概要
最近自分で作ったブックマークレットの内、
利用頻度が高く、作って良かったと感じたものをいくつか記載。※『作った』とかいう表現は大げさだけど
ブックマークレットたち
上へ
所謂『1つ上の階層』に移動する。
☝javascript:(function(){ window.location.href = window.location.href.replace(/[^\/]+\/?$/g, ''); })();☝ (ブックマーク登録用)javascript:(function(){window.location.href = window.location.href.replace(/[^\/]+\/?$/g, '');})();title
ページ タイトルを入力ダイアログで表示し、コピーを補助する。
titlejavascript:( function(){window.prompt('page title', document.title); })();title (ブックマーク登録用)javascript:(function(){window.prompt('page title', document.title);})();(補足)
調べ物などをしていてWEBページをメモる時に
URLと一緒にタイトルも一緒に欲しくなることが多い。
firefoxなどではページのプロパティ画面があったのでchromeだと同様のものがなく、また元々手数も多かった。
ページを非破壊でコピーまですることは出来ないので、
入力ダイアログを代用している形。picture size
画像の解像度を表示。
picture sizejavascript:(function(){ var img = new Image(); img.onload = function () { Promise.resolve().then(function() { window.prompt('width x height', '' + img.width + 'x' + img.height + ''); }); }; img.src = location.href; })();picture size (ブックマーク登録用)javascript:(function(){var img = new Image(); img.onload = function () {Promise.resolve().then(function() { window.prompt('width x height', '' + img.width + 'x' + img.height + '');});}; img.src = location.href;})();(補足)
一応chromeでもファイル名の末尾に解像度は出ているものの、
ファイル名が長くなる + 沢山タブを開いてると、すぐに見えなくなる。
結果的には再ダウンロードがかかってしまうので、トラフィックにはあまり優しくない。user agent
そのまま。
javascriptで取得出来るUserAgentを表示。useragentjavascript:(function(){ window.prompt('user agent', window.navigator.userAgent); })();useragent (ブックマーク登録用)javascript:(function(){window.prompt('user agent', window.navigator.userAgent);})();amazondp
amazonの商品へのリンク
****/dp/****/
を抜き出して遷移。amazondpjavascript:(function() { var p = window.location.pathname.match(/\/dp\/[^\/?#=&]+/g); if(p) { window.location.href = window.location.origin + p[0]; }; })();amazondp (ブックマーク登録用)javascript:(function(){var p = window.location.pathname.match(/\/dp\/[^\/?#=&]+/g); if(p) {window.location.href = window.location.origin + p[0];};})();(補足)
AmazonのURLは共有しようとすると○したくなるくらい長い。
~商品名とかURLに埋め込む必要あんの???openpopup
今開いているページをポップアップで表示。
openpopupjavascript:(function() { window.open(window.location.pathname, undefined, 'menubar=no, toolbar=no'); })();openpopup (ブックマーク登録用)javascript:(function() { window.open(window.location.pathname, undefined, 'menubar=no, toolbar=no');})();(補足)
漫画は縦長が多いので、どうしてもアドレスバーやタブバーなどの分の解像度がもったいなくなる。
ポップアップで開くことで高さを稼いだりできる。
ただし、ポップアップ ブロッカーなどで起動の確実性は保証できない。
また、あくまでURLで開き直しているだけなので、POST系などのページでは遷移出来ない。余談
ブラウザ拡張のツールバー文化は完全廃れたけど。
ブックマークバーとブックマークレットの併用でも、
ブラウザを便利にできることは多いので、もっと活用していけたらなぁと思う、
- 投稿日:2020-06-19T19:02:26+09:00
P5.js 日本語リファレンス(textDescent)
このページでは「P5.js 日本語リファレンス」 の textDescent関数を説明します。
textDescent()
説明文
現在のフォントの現在のサイズでディセントを返します。ディセントとはベースラインより下のディセンダーが最も長い文字のピクセル単位の距離を表します。
構文
textDescent()
戻り値
Number:ディセンダー(ピクセル単位)
例1
function setup() { createCanvas(200, 200); let base = 100; // ディセントに乗ずる係数(フォントごとに異なります) let scalar = 0.6; textSize(32); // ディセントを算出します let desc = textDescent() * scalar; // ベースラインにラインを描画します stroke(255, 0, 0); line(0, base, 50, base); // 文字のボトムにラインを描画します stroke(0, 0, 255); line(0, base + desc, 50, base + desc); // ベースラインにテキストを描画します text('dp', 0, base); textSize(64); // ディセントを算出します desc = textDescent() * scalar; // ベースラインにラインを描画します stroke(255, 0, 0); line(60, base, 140, base); // 文字のボトムにラインを描画します stroke(0, 0, 255); line(60, base + desc, 140, base + desc); // ベースラインにテキストを描画します text('dp', 60, base); }実行結果
https://editor.p5js.org/bit0101/sketches/oaP4skQVW
著作権
p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.
ライセンス
Creative Commons(CC BY-NC-SA 4.0) に従います。
- 投稿日:2020-06-19T18:59:16+09:00
P5.js 日本語リファレンス(textAscent)
このページでは「P5.js 日本語リファレンス」 の textAscent関数を説明します。
textAscent()
説明文
現在のフォントの現在のサイズでアセントを返します。アセントとはベースラインより上の最も高い文字の距離をピクセル単位で表したものです。
構文
textAscent()
戻り値
Number:アセント(ピクセル単位)
例1
function setup() { createCanvas(200, 200); let base = 100; //アセントに乗ずる係数(フォントごとに異なります) let scalar = 0.9; textSize(32); // アセントを算出します let asc = textAscent() * scalar; // ベースラインにラインを描画します stroke(255, 0, 0); line(0, base, 50, base); // 文字のトップにラインを描画します stroke(0, 0, 255); line(0, base - asc, 50, base - asc); //ベースラインにテキストを描画します text('dp', 0, base); textSize(64); // アセントを算出します asc = textAscent() * scalar; // ベースラインにラインを描画します stroke(255, 0, 0); line(60, base, 140, base); // 文字のトップにラインを描画します stroke(0, 0, 255); line(60, base - asc, 140, base - asc); //ベースラインにテキストを描画します text('dp', 60, base); }実行結果
https://editor.p5js.org/bit0101/sketches/t7SwDYYj0
著作権
p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.
ライセンス
Creative Commons(CC BY-NC-SA 4.0) に従います。
- 投稿日:2020-06-19T18:53:44+09:00
P5.js 日本語リファレンス(textWidth)
このページでは「P5.js 日本語リファレンス」 の textWidth関数を説明します。
textWidth()
説明文
文字または文字列の幅(ピクセル単位)を計算して返します。
構文
textWidth(theText)
パラメタ
- theText
String:計算する文字または文字列戻り値
Number:文字または文字列の幅(ピクセル単位)
例1
function setup(){ createCanvas(300, 300); textSize(14); let aChar = 'P'; // テキストサイズ14の文字幅を取得します let cWidth = textWidth(aChar); text(aChar, 0, 40); // 取得したサイズの位置にラインを描画します line(cWidth, 0, cWidth, 50); let aString = 'p5.js'; // テキストサイズ14の文字列幅を取得します let sWidth = textWidth(aString); text(aString, 0, 85); // 取得したサイズの位置にラインを描画します line(sWidth, 50, sWidth, 100); textSize(28); aChar = 'P'; // テキストサイズ28の文字幅を取得します cWidth = textWidth(aChar); text(aChar, 100, 40); // 取得したサイズの位置にラインを描画します line(cWidth + 100, 0, cWidth + 100, 50); aString = 'p5.js'; // テキストサイズ28の文字列幅を取得します sWidth = textWidth(aString); text(aString, 100, 85); // 取得したサイズの位置にラインを描画します line(sWidth + 100, 50, sWidth + 100, 100); }実行結果
https://editor.p5js.org/bit0101/sketches/WSc3auwdt
著作権
p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.
ライセンス
Creative Commons(CC BY-NC-SA 4.0) に従います。
- 投稿日:2020-06-19T18:52:22+09:00
P5.js 日本語リファレンス(textStyle)
このページでは「P5.js 日本語リファレンス」 の textStyle 関数を説明します。
textStyle()
説明文
システムフォントのテキストスタイルを NORMAL、ITALIC、BOLD または BOLDITALIC に設定または取得します。
注:これは CSS スタイルによってオーバーライドされる場合があります。非システムフォント(OpenTypem、TrueType など)の場合は, 代わりにスタイル付きフォントをロードしてください。構文
textStyle(theStyle)
textStyle()
パラメタ
- theStyle 定数:設定するテキストスタイル(NORMAL、ITALIC、BOLD、BOLDITALIC)
例1
function setup() { createCanvas(200, 200); strokeWeight(0); textSize(12); textStyle(NORMAL); text('Font Style Normal', 10, 15); textStyle(ITALIC); text('Font Style Italic', 10, 40); textStyle(BOLD); text('Font Style Bold', 10, 65); textStyle(BOLDITALIC); text('Font Style Bold Italic', 10, 90); }実行結果
https://editor.p5js.org/bit0101/sketches/_jPKbmCl7
著作権
p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.
ライセンス
Creative Commons(CC BY-NC-SA 4.0) に従います。
- 投稿日:2020-06-19T18:50:21+09:00
P5.js 日本語リファレンス(textSize)
このページでは「P5.js 日本語リファレンス」 の textSize関数を説明します。
textSize()
説明文
現在のフォントサイズを設定/取得します。フォントサイズはピクセル単位です。
構文
textSize(theSize)
textSize()
パラメタ
- theSize
Number:設定するフォントサイズ(ピクセル単位)例1
function setup() { createCanvas(200, 200); textSize(12); text('Font Size 12', 10, 30); textSize(14); text('Font Size 14', 10, 60); textSize(16); text('Font Size 16', 10, 90); }実行結果
https://editor.p5js.org/bit0101/sketches/68BT2rvNr
著作権
p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.
ライセンス
Creative Commons(CC BY-NC-SA 4.0) に従います。
- 投稿日:2020-06-19T18:45:43+09:00
P5.js 日本語リファレンス(textLeading)
このページでは「P5.js 日本語リファレンス」 の textLeading関数を説明します。
textLeading()
説明文
テキストの行間の間隔をピクセル単位で設定/取得します。この設定は textSize() の以降のすべての呼び出しで使用できます。
構文
textLeading(leading)
textLeading()
パラメタ
- leading
Number:設定する行間隔のサイズ(ピクセル単位)例1
function setup(){ createCanvas(200, 200); textSize(12); // 表示するテキスト。 '\n' は「改行」文字です let lines1 = 'ABC\nDEF\nGHI\nJKL\nMNO\nPQR\nSTU'; // 行間隔を10に設定します textLeading(10); text(lines1, 10, 25); // 表示するテキスト。 '\n' は「改行」文字です let lines2 = 'ABC\nDEF\nGHI\nJKL'; // 行間隔を20に設定します textLeading(20); text(lines2, 50, 25); // 表示するテキスト。 '\n' は「改行」文字です let lines3 = 'ABC\nDEF\nGHI'; // 行間隔を30に設定します textLeading(30); text(lines3, 90, 25); }実行結果
https://editor.p5js.org/bit0101/sketches/hMPn3Obkn
著作権
p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.
ライセンス
Creative Commons(CC BY-NC-SA 4.0) に従います。
- 投稿日:2020-06-19T18:41:55+09:00
P5.js 日本語リファレンス(textAlign)
このページでは「P5.js 日本語リファレンス」 の textAlign関数を説明します。
textAlign()
説明文
テキストを描画するための現在の配置を設定します。 horizAlign(LEFT、CENTER、RIGHT) および vertAlign(TOP、BOTTOM、CENTER、BASELINE)の2つの引数を受け入れます。
horizAlign パラメータは text() 関数のx値を参照し、vertAlign パラメータはy値を参照しています。
したがって、textAlign(LEFT) と記述する場合、テキストの左端を text() で指定したx値に揃えます。 textAlign(RIGHT, TOP)と書くとテキストの右端をx値に、テキストの上端をy値に揃えます。
構文
textAlign(horizAlign, [vertAlign])
textAlign()
パラメタ
horizAlign
定数:水平方向の配置。LEFT, CENTER または RIGHTvertAlign
定数:垂直方向の配置。TOP, BOTTOM, CENTER または BASELINE(オプション)例1
function setup(){ textSize(16); strokeWeight(0.5); line(width/2, 0, width/2, height); // horizAlign = RIGHT のとき textAlign(RIGHT); text('ABCD', 50, 30); // horizAlign = CENTER のとき textAlign(CENTER); text('EFGH', 50, 50); // horizAlign = LEFT のとき textAlign(LEFT); text('IJKL', 50, 70); }実行結果
https://editor.p5js.org/bit0101/sketches/LFTwB-_ak
例2
function setup(){ createCanvas(120, 120); textSize(16); strokeWeight(0.5); // vertAlign = TOP のとき line(0, 12, width, 12); textAlign(CENTER, TOP); text('TOP', 0, 12, width); // vertAlign = CENTER のとき line(0, 40, width, 40); textAlign(CENTER, CENTER); text('CENTER', 0, 40, width); // vertAlign = BASELINE のとき line(0, 68, width, 68); textAlign(CENTER, BASELINE); text('BASELINE', 0, 68, width); // vertAlign = BOTTOM のとき line(0, 110, width, 110); textAlign(CENTER, BOTTOM); text('BOTTOM', 0, 110, width); }実行結果
https://editor.p5js.org/bit0101/sketches/xLU2NwpLc
著作権
p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.
ライセンス
Creative Commons(CC BY-NC-SA 4.0) に従います。
- 投稿日:2020-06-19T18:29:26+09:00
JavaScriptの日付フォーマットはこれだけあれば十分な気がする
日付フォーマットは言語やライブラリによって微妙に書き方が違ったりして覚えるのが面倒です。
JavaScriptならライブラリ不要で以下の定義を入れておけば足りそうな気がしたのでメモしておきます。
Date.prototype.format = function(formatter, utc) { var month_en = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; var wday_en = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; var wday_ja = '日月火水木金土'; return formatter(utc ? { year: this.getUTCFullYear(), month: ('0' + (this.getUTCMonth() + 1)).slice(-2), month_en: month_en[this.getUTCMonth()], day: ('0' + this.getUTCDate()).slice(-2), wday: this.getUTCDay(), wday_ja: wday_ja.charAt(this.getUTCDay()), wday_en: wday_en[this.getUTCDay()], hour: ('0' + this.getUTCHours()).slice(-2), minute: ('0' + this.getUTCMinutes()).slice(-2), second: ('0' + this.getUTCSeconds()).slice(-2), ms: ('00' + this.getUTCMilliseconds()).slice(-3) } : { year: this.getFullYear(), month: ('0' + (this.getMonth() + 1)).slice(-2), month_en: month_en[this.getMonth()], day: ('0' + this.getDate()).slice(-2), wday: this.getDay(), wday_ja: wday_ja.charAt(this.getDay()), wday_en: wday_en[this.getDay()], hour: ('0' + this.getHours()).slice(-2), minute: ('0' + this.getMinutes()).slice(-2), second: ('0' + this.getSeconds()).slice(-2), ms: ('00' + this.getMilliseconds()).slice(-3) }); };※IE8とかでも動くような書き方にしています。
//年月日曜日なら new Date().format(({year, month, day, wday_ja}) => `${year}年${month}月${day}日(${wday_ja})`) //=> 2020年06月19日(金)//前0を消したいならNumber()で new Date().format(({year, month, day, wday_ja}) => `${year}年${Number(month)}月${Number(day)}日(${wday_ja})`) //=> 2020年6月19日(金)//年月なら new Date().format(({year, month}) => `${year}/${month}`) //=> 2020/06//時間なら new Date().format(({hour, minute, second, ms}) => `${hour}:${minute}:${second}.${ms}`) //=> 17:51:33.491//分割代入やアロー関数、テンプレートリテラルが使えない場合は new Date().format(function(t) { return t.hour + ':' + t.minute + ':' + t.second + ':' + t.ms }) //=> 17:51:33.491//HTTPヘッダ用日付形式なら new Date().format(t => `${t.wday_en.slice(0, 3)}, ${t.day} ${t.month_en.slice(0, 3)} ${t.year} ${t.hour}:${t.minute}:${t.second} GMT`, true) //=> Fri, 19 Jun 2020 09:20:58 GMTという感じで、どんな形式でもわりと直感的にかけるのではと思いました。
- 投稿日:2020-06-19T18:25:15+09:00
ビデオチャット作るときの逆引きTipsまとめ
リモートワークのため社内用ビデオチャットツールを先日作ったのですが、せっかくなので一部をオープンソースにしてGitHubにあげておきました。
ukkz/v-sky: Single page video chat appplication
※リファクタしてないので見辛いですSPAなのでGiHub Pages上でそのまま利用もできます。
https://ukkz.github.io/v-skyで、一旦開発が落ち着いたので、作業時のメモを簡単に整理して公開しておくことにします。
構成
- Vue.js (@vue/cli v4.3.1)
- Vuetify @2.3.1
- Vuex @3.4.0
- Vue-Router @3.3.4
- SkyWay
まとめ一覧
ビデオストリームの情報を知りたい
使ったところ:実際の映像アスペクト比とブラウザ画面のアスペクト比から複数映像配置時の自動最適化
VideoTrack.getSettings()でいろいろ情報がとれました。// メディアストリームオブジェクト const my stream = new MediaStream; // ビデオトラックの配列(ないこともある) const video_tracks = mystream.getVideoTracks(); // 少なくとも1つのビデオトラックがあるとき、そのトラックの情報を見る(空配列はtrue判定になるので必ずインデックス0を確認する) if (video_tracks[0]) { const track_settings = video_tracks[0].getSettings(); console.log( track_settings ); /* 検出例(640x480の映像のとき) aspectRatio: 1.3333333333333333, deviceId: "1d3fa9…", frameRate: 30.000030517578125, groupId: “66fa41…", height: 480, resizeMode: "none", width: 640, */ }アス比は循環小数になることがあるためあまり使い勝手がよくなさそうです。
ストリームやトラックに関しては以下がたいへん詳しく書かれておりました。
getUserMedia()の設定項目|npaka|noteビデオタグに関して幅と高さを知りたい・設定したい
使ったところ:映像にcanvasをオーバーレイさせるときのcanvasサイズの指定
<video id="my-video" :srcObject.prop="my_stream" width="160px" height="120px"></video> <style> #my-video { width: 320px; height: 240px; background-color: red; } </style>上記のDOMの場合のビデオタグの大きさは、インライン指定よりもCSS指定のほうが優先されます。
実際のビデオトラック(映像)はCSSで指定されたサイズ(CSSしていなければインライン指定サイズ)の枠内に収まるように配置されます。
上記で枠内の余白部分はbackground-colorで指定された色になります。
CSSもインラインもどちらも指定されていなければ、ビデオトラックのオリジナルサイズで配置されます。const video_element = document.getElementById('my-video’); // 以下、取得できる様々なサイズの例 console.log( video_element.width ); // 160px(インライン指定の幅・未指定ならundefined) console.log( video_element.height ); // 120px(インライン指定の高さ・未指定ならundefined) console.log( video_element.offsetWidth ); // 320px(CSS指定の幅・mounted前ならundefined) console.log( video_element.offsetHeight ); // 240px(CSS指定の高さ・mounted前ならundefined) console.log( video_element.videoWidth ); // 640px(ビデオトラックの実際の幅・メタデータ受信前ならundefined) console.log( video_element.videoHeight ); // 480px(ビデオトラックの実際の高さ・メタデータ受信前ならundefined)メディアストリームをビデオタグに動的に追加したい [Vue.js]
使ったところ:相手からストリームを受信する前にvideoタグをDOMにマウントしたいとき
<video :srcObject.prop="my_stream"></video>
:srcObject.propにdataで定義した変数を指定しておいて、相手からストリーム受信したり自分がgetUserMediaしたりしたタイミングでメディアストリームオブジェクト(空の場合new MediaStream()で生成)を入れればよいです。
Vue歴2ヶ月ですが、リアクティブっていいなあと思いました。ビデオタグのプロパティについて
使ったところ:ビデオタグぜんぶ(ほぼテンプレ構文)
<video :srcObject.prop="my_stream" muted autoplay playsinline></video>
muted:音声再生しません。自身のストリームだとハウリング起こすので必須の属性です。
autoplay:mutedとともに有効のとき自動再生します。カメラからのストリームだとこれがないとそもそもデータが流れないので表示されません。インライン(videoタグ内)で指定しない場合は、スクリプト内のどこかで明示的にplay()を実行しないと再生されません。
playsinline:インライン(Webページ上の表示そのまま)で再生します。これがないとiOSでは自動的に映像がフルスクリーンになってしまうのでほぼ必須です。ボタンの背景色にあわせて文字色の白黒を変えたい [Vuetify]
使ったところ:ボタンのデザイン全般
Vuetifyのv-btnでは基本的にdarkプロパティをつけておくと良さそうです。
背景色にあわせて文字色も自動で変えてくれる唯一のコンポーネントのようです。<v-btn rounded dark color="blue darken-4" @click=“any"> <v-icon>mdi-message-text</v-icon> チャット </v-btn>v-dialogを使った子コンポーネントを親コンポーネント側から開閉させたいが反応しない [Vuetify, Vue.js:v-model]
使ったところ:ボタンクリックでテキストチャットダイアログを開くとき
v-dialogはv-ifなどではなくv-modelで開閉します。
(開いているときにダイアログ外側をクリックorタップで閉じれるようにするため)
このv-dialogを用いた子コンポーネントを親コンポーネントから制御するときは、computedにおいてゲッターとセッターを利用するとうまくいきます。
Vue.jsのv-modelを正しく使う - Qiita親コンポーネント<template> <v-container> <!-- v-dialogを開くボタン --> <v-btn @click.stop="chat_open = true">チャットを開く</v-btn> <!-- :showで開く・toggleイベント受信で閉じる --> <ChatWindow :show="chat_open" @toggle="chat_open = $event" /> </v-container> </template> <script> data() { return { chat_open: false, } } </script>子コンポーネント<template> <v-dialog v-model="toggle"> // 略 </v-dialog> </template> <script> name: 'ChatWindow', props: { show: { type: Boolean, required: true, default: false, }, }, computed: { toggle: { get() { return this.$props.show }, set(v) { this.$emit('toggle', v) } }, }, </script>ゲッターは
props変数を受け取り、セッターは$emitで親コンポーネントに変化を通知します。
親側では変化を受信してpropsに与えた変数を子側から受け取った$event値に変更します。チェックボックスなど設定値の変化でVuexの値を変えたい [Vue.js:v-model]
使ったところ:設定ダイアログで特定機能をスイッチボタンでON/OFFするとき
v-dialogの例と同様で、入力系コンポーネントでv-modelを使うときはゲッターとセッターを使います。
ゲッターは$store.stateから取り出し、セッターでは$store.commitでVuexにセットします。<template> <v-container> <v-switch v-model="speech_onoff" :label="` 音声認識:${(speech_onoff)?'有効':'無効'} `"></v-switch> </v-container> </template> <script> computed: { speech_onoff: { get() { return this.$store.state.config.speech_recognition }, set(onoff) { this.$store.commit('speechConfig', onoff) }, }, }, </script>テキストチャットで長文入力したときに折り返しできない・overflowやword-breakが効かない [Vuetify]
使ったところ:テキストチャット内の吹き出し部分(個別の発言)
pタグのようなブロック要素で囲み
text-wrapクラスを適用させましょう。
(Vuetifyのpタグはデフォルトでmargin-bottomが入ってしまうのでmb-0クラスと組み合わせるとよい)
text-align: left;にしておくと折り返しても左寄せで表示してくれます。文字起こししたい
使ったところ:そのまま(喋った内容を文字列にしてテキストチャットに逐次送信)
SpeechRecognition(Web Speech API)が便利です。すごく簡単。
Webページでブラウザの音声認識機能を使おう - Web Speech API Speech Recognition - Qiita音声入力を明示的に指定できるわけではない(というか選べない)みたいなので、マイクミュート時は手動で
abortすべきですね。
2020年6月現在もChromiumベースのブラウザしか対応していないので注意です。
Samsung, Baidu, QQといった中韓系ブラウザは利用者層が音声入力を好むためか対応しておりなかなか興味深いです。背景切り抜きなどの加工をしたい
使ったところ:画面共有とカメラ映像の合成(公開してないコンポーネントで利用)
- RGB値をキーにして合成(クロマキー)しているもの
canvasでクロマキー合成っぽいことしてみる - Qiita
SkyWayでデスクトップの映像とカメラの映像をクロマキー合成して配信する - Qiita- 静止時と人物登場時の差分を抽出しているもの
SkyWay のビデオチャットに雑なクロマキー合成してみた - Qiita- OpenCV.jsを使って白黒のマスクを作り、輪郭抽出で背景を消すもの
opencv.jsの作法 その9 - Qiita
OpenCV.jsはminifiedでも容量がけっこう大きいのでロード時には注意です。今回オープン版には実装してませんが内部向けで消したり合成したりの用途にはOpenCV.jsのパターンを使いました。
findContoursで輪郭抽出後、一番外の階層のある程度の大きさの輪郭をアルファで塗りつぶすだけで、クロマキー色を指定したりしなくても消したり背景合成したりすることができました。
上記参考記事では画像に対して利用されていますが、映像に対しても15fps程度の速さで処理できたのでそのうち別途記事かきます。スクリーン共有でストリームを置き換えたら音声が送られなくなった
使ったところ:画面共有とカメラ映像の切替時
getUserMediaで音声取得 +getDisplayMediaで映像取得 を組み合わせたメディアストリームを作ります。
MediaStream APIで画面キャプチャとマイクからの音声を同時に収録する - エムスリーテックブログconst display_stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false }); const camera_stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); // ディスプレイ映像と外部音声のトラックから新しくメディアストリームを作成 const merged_stream = new MediaStream([ display_stream.getVideoTracks()[0], camera_stream.getAudioTracks()[0] ]); // または、既存のカメラからのストリームに画面キャプチャ用のトラック追加してもいい(受信側で区分する必要あり) camera_stream.addTrack( display_stream.getVideoTracks()[0] );スマホとPCでエレメントのサイズ・デザインを簡単に切り替えたい [Vutify]
使ったところ:レスポンシブデザインとして全体
v-ifと$vuetify.breakpoint.smAndDownを組み合わせましょう。
ブレイクポイントはほかにもたくさんあります。
Breakpoints — Vuetify.js
v-ifとv-elseで同一機能を異なるデザインで作成して並列に配置すると画面幅によって表示切り替えが可能です。
以下はボタンの例で、PCではアイコン+文字だが、smサイズ以下だとアイコンのみのボタンが表示されます。<v-list-item-action> <v-btn v-if="$vuetify.breakpoint.smAndDown" fab depressed small @click="open = true"><v-icon>mdi-open</v-icon></v-btn> <v-btn v-else rounded depressed @click="open = true"><v-icon>mdi-open</v-icon>開く</v-btn> </v-list-item-action>ビデオを上下中央センタリングしたい
使ったところ(トリミングなし):自分の映像の確認用・チャットルーム内で話者の映像を拡大する用
使ったところ(トリミングあり):チャットルーム内での基本的な映像表示単純なCSSだけどメモ
一番外側の枠となるdivはあらかじめサイズが決まっている(100%指定とかでもよい)ものとし、その中にビデオを配置するときにどうしましょ?という話です。<div class="video-frame"> <video :srcObject.prop="local_media_stream" muted autoplay playsinline></video> </div>上記DOMが基本構造とします。
トリミングなし(上下または左右に余白を作る)
div.video-frame { position: relative; background-color: black; width: 100%; // 枠幅:固定値でもよい height: 100%; // 枠高さ:固定値でもよい } div.video-frame video { position: absolute; top: 50%; left: 50%; transform: translateY(-50%) translateX(-50%) scale(-1, 1); width: auto; height: auto; max-width: 100%; max-height: 100%; }ビデオ側で
widthとheightをautoにしつつも最大サイズを親要素の100%にしておくとアスペクト比を変えずにフィットさせられます。
このままだと左上に寄ってしまうので、topとleftを使って親要素の上左からそれぞれ半分(つまり真ん中)に寄せつつ、translateで自要素の幅と高さ半分ずつ戻すことでセンタリングさせています。また、センタリングとは関係ないですが、
scale(-1, 1)は左右フリップして鏡状態にしてくれるので自分のカメラ映像の時のみ使います。transformは多重に適用できないため、別のクラスなどでtransform: scale(-1, 1);を指定してもうまく反映されません。そのためここでまとめて指定しています。余白は外枠である
video-frameクラスで指定した背景色になります。トリミングあり(上下をフィットさせ左右をカットする)
div.video-frame { position: relative; background-color: black; width: 100%; // 枠幅:固定値でもよい height: 100%; // 枠高さ:固定値でもよい overflow: hidden; // はみ出ても表示させない } div.video-frame video { position: absolute; left: 50%; transform: translateX(-50%); width: auto; height: 100%; }
width: auto;とheight: 100%;で、アスペクト比を維持したまま高さが外枠に合わせられます。
そのあと左右方向に対してleft: 50%;とtranslateX(-50%)でセンタリングさせています。トリミングあり(左右をフィットさせ上下をカットする)
div.video-frame { position: relative; background-color: black; width: 100%; // 枠幅:固定値でもよい height: 100%; // 枠高さ:固定値でもよい overflow: hidden; // はみ出ても表示させない } div.video-frame video { position: absolute; top: 50%; transform: translateY(-50%); width: 100%; height: auto; }上下と左右、
widthとheightを入れ替えると左右方向のフィッティングおよび上下センタリングにも対応できます。
- 投稿日:2020-06-19T17:54:22+09:00
【Google Apps Scriptとスプレッドシートで在庫管理】選択範囲の中で取得した値を使って、別シートで計算する
はじめに
この動画を見ていただけると、大体何をやるのか理解していただけるかと。。
Google Apps Scriptで在庫管理】選択範囲の中で取得した値を使って、別シートで計算する pic.twitter.com/wDrpmCA9w3
— げん げんと (@gento34165638) June 19, 2020経緯
Google Apps Script 入力された文字を別シートにて検索し、その隣にある文字を書き出す
こちらの続きスプレッドシートでアクセサリーの在庫管理をしていると、とてもめんどくさいのが以下の作業!
在庫として保有している材料の数 ー 今回使った材料の数 = 現在の在庫の数アクセサリー作りで使う材料の数は多いようで、それを毎回手動でスプレッドシートを使い引き算するのが面倒だということですね。。。
用意するもの
在庫をまとめたシート
作ったアイテムをまとめたシート
全体の流れ
①「作ったアイテム」のシートで使った材料を範囲選択(動画参照)
②カスタムメニューでボタンを作っておいて、それを押すことで発動し、①で選択した範囲を2次元配列で取得
③「在庫」シートにて同じ名前の材料を検索し、2つ横のセルの値(各材料の在庫数)を取得
④②と③で取得した在庫数を引き算して、残った在庫数を書き出す大体の処理の流れはこんな感じ。
全てのコード
コード.gs// Spreadsheetが開かれた時に自動的に実行されます. function onOpen() { // 現在開いている、スプレッドシートを取得します. var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); // メニュー項目を定義します. var entries = [ {name : "在庫から使った数を減らす" , functionName : "menuItem1"}, ]; // 「Custom」という名前でメニューに追加します. spreadsheet.addMenu("Custom", entries); } // menuItem1をクリックした時の処理 function menuItem1() { //現在アクティブなシートの名前を取得 var nowSheet = SpreadsheetApp.getActiveSheet().getName(); //シート「作ったアイテム」以外で実行したらアラート if (nowSheet === '作ったアイテム') { var data = getData(); matchData(data); } else { alert(); } } function getData() { //現在のスプレットシートを取得する var sheet1 = SpreadsheetApp.getActiveSheet(); var values = sheet1.getActiveRange().getValues(); var numRows = sheet1.getActiveRange().getNumRows(); return [values, numRows]; } function matchData(data) { //現在のスプレッドシートを取得 var ss = SpreadsheetApp.getActiveSpreadsheet(); //シート「在庫」を取得 var stockSheet = ss.getSheetByName('在庫'); //使った材料の配列「values」をdataの中から取り出す var items = data[0]; // ループする回数「numRows」をdataの中から取り出す var dataNumRows = data[1]; var stopSetCount = dataNumRows - 1; for(var i = 0; i <= stopSetCount; i++){ var item = items[i]; var itemName = item[0]; var usedCount = item[2]; //シート「在庫」で材料名を探す var textFinder = stockSheet.createTextFinder(itemName); var ranges = textFinder.findAll(); //材料名のセル範囲を取得 var textPos = ranges[0].getA1Notation(); //材料名の位置を取る var textPosCell = stockSheet.getRange(textPos); //材料名の2つ右隣のセルの値を取る var targetValue = textPosCell.offset(0, 2).getValue(); //在庫の数から使った数を引く var afterUsedCount = targetValue - usedCount; textPosCell.offset(0, 2).setValue(afterUsedCount); } } function alert() { var ui = SpreadsheetApp.getUi(); var title = '「作ったアイテム」のシートで実行してよ'; var text = 'おかしくなるから!'; ui.alert(title, text, ui.ButtonSet.OK_CANCEL); }軽く解説
なぜループする回数を-1するのか?
// ループする回数「numRows」をdataの中から取り出す var dataNumRows = data[1]; var stopSetCount = dataNumRows - 1; for(var i = 0; i <= stopSetCount; i++){範囲選択した部分が何行か?を取得して、ループを回す回数を決めているんだが、なぜ−1するのか?
結論を言うと、−1しないとループする回数が1回増えてしまうからだ。もし範囲選択した部分が4行だったとする。すると4回ループしないといけない。
しかし、for分はvar i = 0;で0から始まってる。だから−1しないと、1回ループする回数が増えてしまいエラーが起こる。なので、
var i = 0;をvar i = 1;としても良い。matchDataで受け取る
dataは何?// menuItem1をクリックした時の処理 function menuItem1() { //省略 var data = getData(); matchData(data); //省略 } function matchData(data) { //省略 //使った材料の配列「values」をdataの中から取り出す var items = data[0]; // ループする回数「numRows」をdataの中から取り出す var dataNumRows = data[1]; }ここで受け取ってるのは
getData()で返されている値。
1つ目の配列(つまりvalues)が範囲選択した部分のデータが2次元配列で入っている。
2つ目の配列(つまりnumRows)が「範囲選択した部分は何行あったか?」と言うデータが入っている。function getData() { //現在のスプレットシートを取得する var sheet1 = SpreadsheetApp.getActiveSheet(); //getValueではなくgetValues。複数形のsがあるよ! var values = sheet1.getActiveRange().getValues(); var numRows = sheet1.getActiveRange().getNumRows(); return [values, numRows]; }カスタムメニューはこの部分で作られてる
// Spreadsheetが開かれた時に自動的に実行されます. function onOpen() { // 現在開いている、スプレッドシートを取得します. var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); // メニュー項目を定義します. var entries = [ {name : "在庫から使った数を減らす" , functionName : "menuItem1"}, ]; // 「Custom」という名前でメニューに追加します. spreadsheet.addMenu("Custom", entries); } // menuItem1をクリックした時の処理 function menuItem1() { //現在アクティブなシートの名前を取得 var nowSheet = SpreadsheetApp.getActiveSheet().getName(); //シート「作ったアイテム」以外で実行したらアラート if (nowSheet === '作ったアイテム') { var data = getData(); matchData(data); } else { alert(); } }
- 投稿日:2020-06-19T17:54:03+09:00
ESLint7系を使っていると「TypeError: createRequire is not a function」が出る
概要
ESLint 7 系とNode.js 12未満をつかっていると以下のエラーがでる場合がある
ESLint 7 がサポートしている Node.js バージョンは ^10.12.0 || >=12.0.0 となっています
↑だそうです。(コメントありがとうございました)
TypeError: createRequire is not a function Referenced from: /something/.eslintrc at Object.resolve (/something/node_modules/eslint/lib/shared/relative-module-resolver.js:28:20) at ConfigArrayFactory._loadExtendedShareableConfig (/something/node_modules/eslint/lib/cli-engine/config-array-factory.js:854:39) at ConfigArrayFactory._loadExtends (/something/node_modules/eslint/lib/cli-engine/config-array-factory.js:763:25) at ConfigArrayFactory._normalizeObjectConfigDataBody (/something/node_modules/eslint/lib/cli-engine/config-array-factory.js:702:25) at _normalizeObjectConfigDataBody.next (<anonymous>) at ConfigArrayFactory._normalizeObjectConfigData (/something/node_modules/eslint/lib/cli-engine/config-array-factory.js:647:20) at _normalizeObjectConfigData.next (<anonymous>) at ConfigArrayFactory.loadInDirectory (/something/node_modules/eslint/lib/cli-engine/config-array-factory.js:495:28) at CascadingConfigArrayFactory._loadConfigInAncestors (/something/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js:355:46) at CascadingConfigArrayFactory._loadConfigInAncestors (/something/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js:374:20)対策
その1
Node.jsを最新にする
その2
ESLintをダウングレードする
package.json (ESLintを6系にダウングレード)"eslint": "^6.8.0",
- 投稿日:2020-06-19T17:11:48+09:00
javascript学習(2020/06/19)
setTimeoutメソッド
sample.jssetTimeout(ファンクション, 待ち時間)待ち時間後にファンクションを一度だけ実行するメソッド。待ち時間はミリ秒で設定。ファンクション名の後ろに()はつけない。
日時を設定した状態でDateオブジェクトを初期化する
sample.jsnew Date(年,月,日,時,分,秒,ミリ秒);padStartメソッド
sample.jsある文字列.padStart(揃える文字列,埋め合わせ用の文字);ある文字列をそろえる文字数に揃えるメソッド。もし、ある文字列の文字数が揃える文字数織少ない場合は先頭に埋め合わせ用の文字をくっつけて、文字数を揃える。
Stringオブジェクト
sample.jsString(数値データ).padStart(2,'0');()内の数値データを文字列データに変換する。
URLを書き換える(新しいURLを指定する)
sample.jslocation.href = 新しいURL;cssセレクタで要素を取得する
sample.jsdocument.querySelector('CSSセレクタ');document.querySelectorメソッドは()内に書かれたセレクタにマッチする要素を取得する。
複数の要素にマッチした場合は最初にマッチした要素の一つだけを取得。selected属性の追加
sample.jsdocument.querySelector('option[value="index.html"]').selected = true;ブール属性であるselected属性をtrueにする(代入する)と、selected属性がオンになる。この場合,
<option>タグにselected属性が追加される。クッキー(cookie)
クッキーとは、ブラウザに保存される小さなデータのこと。クッキーのデータはブラウザとWebサーバーの間で送受信され、主にECサイトやSNSサイトなそでユーザーのログイン情報を管理するのに使われる。
クッキーは基本的にはブラウザとWebサーバーの間でデータをやり取りするものだが、javascriptからも読み取りと書き込みができる。javascriptでクッキーのデータを扱う場合は
・簡易的なアンケートや確認ボタンなどで過去に回答したことがあるかボタンをクリックしたことがあるかどうか
・そのwebサイトに何度訪問したか
・文字の大きさや背景色、使用言語などを変更できるサイトの場合は、その設定情報
などを保存するのに使われる。指定した条件でクッキーを保存するjs-cookieライブラリのメソッド
sample.jsCookie.set('クッキー名','値',{expires: 有効期限});js-cookieはオープンソースのライブラリ
HTML要素の削除
sample.js取得した要素.removeChild(削除する要素);取得した要素に含まれる子要素または孫要素のうち、()内のパラメータで指定した要素を削除する。
マッチする要素すべての取得
sample.jsdocument.querySelectorAll('CSSセレクタ');()内で指定されたCSSのセレクタにマッチする要素すべてを取得する。
取得した要素は配列になる。配列の繰り返し
sample.js配列.forEach(function(item, index){ 処理内容 });パラメータがitemで、これには配列の項目が一つ代入される。つまり、最初の繰り返しでは0番目の項目が代入される。2番目のパラメータはindexで配列の項目のインデックス番号が代入される。
javascriptでdata-ナンデモ属性も値を読み取る
sample.js<タグ名 data-image="img.jpg"> imageの部分は自由に決めることができるdata-ナンデモ属性はナンデモの部分を自分で自由に決めてよい属性。
data-ナンデモ属性はjavascriptでその値を読み取るのに利用する。読み取るときは次のようにする。sample.js取得した要素.dataset.ナンデモのところにつけた名前属性の値を読み取る
sample.js取得した要素.属性属性の値を書き換える
sample.js取得した要素.属性 = 値;ブール属性の値を書き換えるには、実際の値ではなくtrueまたはfalseを代入する。
配列に保存されているデータの項目数を調べる
sample.js配列.length
- 投稿日:2020-06-19T17:03:42+09:00
Vue CLI+ Laravel 5.8 で、チャット作成 firebase Cloud Messaging対応
概要
前の 、クロスドメイン構成の
Vue-CLI + Laravel 関連となり。チャット機能の作成です・会員制となり、Google認証で
ログインに対応しています。
・PWAも対応しています
・ 前のLaravel版チャットを、Vue CLIに移植した形です構成
PWA
firebase Cloud Messaging / FCM
Notification API
Vue CLI
vue-router
vue/cli-service : 4.4.0
・API サービス:
Laravel 5.8
nginx
mysql
・フロント設置ドメイン ,Vue-CLI:
netlify / ホスティングサービス
画面
・web push 受信時の、通知API で。タスクバー表示
・チャット画面、詳細
Vue components
・index
https://github.com/kuc-arc-f/vue_spa3a_4chat/blob/master/src/components/Chats/Index.vue・create
https://github.com/kuc-arc-f/vue_spa3a_4chat/blob/master/src/components/Chats/new.vue・show
https://github.com/kuc-arc-f/vue_spa3a_4chat/blob/master/src/components/Chats/show.vuepackage.json
https://github.com/kuc-arc-f/vue_spa3a_4chat/blob/master/package.json
参考のページ
https://knaka0209.hatenablog.com/entry/lara58_31cross_chat
・Vue CLI+ Laravel 5.8で、クロスドメイン SPA構成/PWA対応のCRUD作成する。
https://qiita.com/knakaqi/items/1bae7a540aa13ce8233b
- 投稿日:2020-06-19T16:55:09+09:00
bootstrap5を使ってみたよ
bootstrap5をちょいつまみぐい(GUI)
現在、自社システムを絶賛開発中で、UI周りはbootstrapにしようかと思っており、
バージョン4で作っていたのですが、先日アルファー版公開されたってことで早速突っ込んでみた。
これは実験的なもので、システムもデモ版なので安定性より動作優先なので、
今後のメンテナンスなどによるバージョンアップを考慮して、導入検討したものとなります。
つまみぐいなので、紹介コードは「アラート」と「トースト」のみです。時期みて、バージョン対応していこうかと思っていますので
今回は、バージョン別で書くのではなく、実行環境バージョンを調べ、
自動処理による未来処理コードで対応してます。では早速、jsのコードを書いていきます。
ちなみに5系では、実際にjQueryの読み込みなしで動きました。
なお、注意して欲しいことは5系の場合、
この状態でjQueryが読み込まれているとバッディングしてうまく動かないかもしれません。ただ、4系までは、jQueryの読み込みは必須ですので、
実際に試す際はしっかりと各バージョンにあった公式ドキュメントを読んで実装方法に従ってお試しください。main.js/*---------------------------------------------------- format ---------------------------------------------------------------*/ /** @:description 文字列フォーマットです。 * String.prototype.format * @param ex:) '{year}/{month}/{day}'.format({'year':year,'month':month,'day':day}) or '{year}/{month}/{day}'.format({'2017','11','01'}) * @return {Object} or {String} */ // 存在チェック if (String.prototype.format == undefined) { /** * フォーマット関数 */ String.prototype.format = function(arg) { // 置換ファンク var rep_fn = undefined; // オブジェクトの場合 if (typeof arg == "object") { rep_fn = function(m, k) { return arg[k]; } } // 複数引数だった場合 else { var args = arguments; rep_fn = function(m, k) { return args[ parseInt(k) ]; } } return this.replace( /\{(\w+)\}/g, rep_fn ); } } /*---------------------------------------------------- $ ---------------------------------------------------------------*/ /** @:description ID と Classを変換する関数。これはjQueryもどきで大幅な変更しないための策。 * $('#id') or $('.className') or $('+name') * @param ex:) {String} - let id = $('#id') or let classname = $('.className') or let name = $('+name') * @return {Object} - id OR class OR name Object. */ if (String.prototype.$ == undefined){ $ = (x) => { const nem = /^[+]/g if (typeof x == "string") { if(x.match(nem)) return document.querySelector("input[name='{0}']".format(x.replace(nem, ""))) return document.querySelector(x) } return x } } /*---------------------------------------------------- alertMessage ---------------------------------------------------------------*/ /** アラートメッセージを表示させる関数 * alertMessage("Title", "Message", flag, delay) * @param ({String},{Number},{Number}) - ex:) alertMessage("Message", flag, delay) * @return "undefined" */ let alertMessage = (msg, f, delay) => { let alertFlag = ["alert-primary","alert-secondary","alert-success","alert-danger","alert-warning","alert-info","alert-light","alert-dark"] let alertTag = $("#alert_tag") let alertLen = "alert_{0}".format(((parseInt(bootstrap.Tooltip.VERSION)>4)? alertTag.querySelectorAll("alert").length:$(".alert").length)) let tagMsg = `<div class="alert {1} alert-dismissible fade show {2}" role="alert" data-delay="{3}"> {0} <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> `.format(msg.toString(), alertFlag[f], alertLen, delay) if((parseInt(bootstrap.Tooltip.VERSION)>4)){ alertTag.insertAdjacentHTML('beforeend',tagMsg) var alertNode = document.querySelector('.{0}'.format(alertLen)) var alert = new bootstrap.Alert(alertNode) setTimeout(() => {alert.close()}, delay); }else{ $(tagMsg).prependTo(alertTag) $("."+alertLen).toast('show') } } /*---------------------------------------------------- toastMessage ---------------------------------------------------------------*/ /** トーストを表示させる関数 * toastMessage("Title", "Message", flag, delay) * @param ({String},{String},{Number},{Number/min:500[ms]}) - :) toastMessage("Title", "Message", 1, 3000) * @return "undefined" */ let toastMessage = (title, msg, f, delay) => { let toastTag = $("#toast_tag") let toastLen = "toast_{0}".format(((parseInt(bootstrap.Tooltip.VERSION)>4)? toastTag.querySelectorAll("toast").length:$(".toast").length)) let tagMsg = `<div class="toast {2}" role="alert" aria-live="assertive" aria-atomic="true" data-animation="true" data-delay="{3}"> <div class="toast-header"> <strong class="mr-auto">{0}</strong> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="toast-body">{1}</div> </div> `.format(title.toString(), msg.toString(), toastLen, delay) if((parseInt(bootstrap.Tooltip.VERSION)>4)){ toastTag.insertAdjacentHTML('beforeend',tagMsg) var toastNode = document.querySelector('.{0}'.format(toastLen)) var toast = new bootstrap.Toast(toastNode) toast.show() }else{ $(tagMsg).prependTo(toastTag) $("."+toastLen).toast('show') } }未来のじぶんへの解説
jQueryの
prependToに変わるコードを実装しました。
insertAdjacentHTMLをbeforeendすることで指定タグに追加される。
イメージは、最初に追加されたのが下の方へ流れる感じです。なおアラートは、5系の場合表示され続けるため、close処理をいれる必要がある。
そのほかにも書き方があるので追加表示ではなく、
静的なページの場合はそちらを選ぶといいと思う。以上、また何かあれば更新しようと思う。
- 投稿日:2020-06-19T16:36:35+09:00
JAWS-UG CLI専門支部に捧ぐ - 手順実行の際にコピペを楽にするブックマークレット
JAWS-UG CLI専門支部 楽しいですよね。ご存じない方は 総合案内 あたり参照してみてください。
手順を実行する際に Web から CLI コマンドをコピペするのですが、ここで選択範囲をミスってコマンドエラーになると少し悲しいです。私は手先が器用ではないので、たまにありますw
というわけで、自分用のクイックハックとして「CLI コマンドをワンクリックで全選択する JavaScript コード」を書いてみました。ブックマークレット書くなんて何年振りだろう…
JavaScript コード
対象のページ をみると、CLI コードが記載されている HTML Element は以下の3種の CSS セレクターで選択できそうです。
- pre.setenv
- pre.checkenv
- .highlight-sh .highlight
であればコードは簡単で、HTML 要素を選択して、onClick イベントを仕込めばok。たとえば以下のようになります。
document.querySelectorAll("pre.setenv, pre.checkenv, .highlight-sh .highlight").forEach(e => { e.addEventListener('click', function(){ var r = document.createRange(); r.selectNodeContents(this); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(r); }); });JAWS-UG CLI の手順ページでコンソールを開き、上記のコードをコピペして実行すれば、CLI コードをワンクリックで選択できるようになります。
あとは Ctrl-C Ctrl-V などで、コンソールにコピペすればok。一応、Chrome と Firefox ブラウザで動作を確認してあります。
ブックマークレット化してみる
毎回 Web ブラウザのコンソールで実行するのも面倒なので、ブックマークレットにしておきましょう。ワンクリックで機能が使えるようになります。
Chrome の場合、まずは適当にブックマークしてタイトル(名前)を入力します。
「その他」をクリックして詳細設定の画面に移動して
以下のコードを URL 欄に設定すればokです。javascript:document.querySelectorAll("pre.setenv, pre.checkenv, .highlight-sh .highlight").forEach(e => {e.addEventListener('click', function(){var r = document.createRange();r.selectNodeContents(this);var sel = window.getSelection();sel.removeAllRanges();sel.addRange(r);});});後は手順ページで、この作成したブックマークレットをクリックすれば、ワンクリックでの CLI コード選択が使えるようになります。
これで手順をスピードアップし、もっと雑談チャットに参加しましょうw
- 投稿日:2020-06-19T15:55:52+09:00
8 Features of AngularJS that make it the Perfect Choice for Dynamic Web Apps
Angular has been around for about a decade. It is popular as a web application framework that allows users to build quality and easy maintenance web apps. Especially for applications that need to be designed to a large scale and require top-level performance. This overview will discuss some of the main features that make AngularJS a good choice for web development services.
Templates:
Angular uses standard HTML for a template. No changes are made by Angular or manipulation through strings. The input in this case is browser DOM. Not HTML string. The way this works is as follows: Templates are parsed into the DOM by the browser. This then goes on to be the input for the compiler. The DOM template provides any rendering instructions (directives). After that, all the directives go into the setup for data-binding, which gives you the application view.
For Angular, the data-bindings have nothing to do with innerHTML changes but are actually DOM transformations. The key difference to notice here is that instead of strings we are using DOM as the input. This means you can take the directive vocabulary and further expand on it to design your own rendering instructions. If you are a developer or designer, this offers a much closer workflow between you and your team.
MVC Use:
Model-View-Controller. The implementation of this model covers a few key elements. First, the data in the application. The model is objects or data in the script. This eliminates the need for a few things including special methods to access the objects, use any proxies, or go into framework classes. The system is straightforward and easy to use. No extra complications.
Second, the view is what you have after everything is compiled. This is the HTML parsed with any bindings or rendering.
Finally, the controller. The controller controls behavior. The main job is to set up the initial state and augment the scope. Here, the state is not stored by the controller and doesn’t do anything with remote services. By doing this, AngularJS gives you a better starting point to build your app. All together you can get to the data you need, define all behavior, and allows the controller to respond during interaction properly.
AngularJS requires Less Code:
Everyone wants to minimize the workload they have to do. For Angular, you won’t be writing more code for the MVC. You can attribute this to the simplicity of HTML when building the interface for your app. Everything is kept short. Removing the complexity from the elements and the attributes or tags that go into it. Manual moving of the data won’t matter as much because of the data binding helps handle that work. Plus, when it comes to the rendering instructions and application code, they are separate. This is really important because it means you can work on both at the same time. Dividing up the labor as effectively as possible.
When you start to get into application code you won’t need to be rewriting multiple parts of code for each device. You can easily rely on one code that goes with just one application. Then, you can move to other platforms with the one application you have ready.
Better Data-Binding:
Another feature that makes sure you don’t have to spend as much time putting together code. Many web apps will use most of their code on just interacting with the DOM. For Angular, you get to forget about that big chunk of code. Which is great because the majority of your effort should go into the actual app design.
With the data-binding features, you can take your model, which asks as a hub for all application updates or processes, and goes straight to the app view. No effort, and super easy. The old way was to go in and change the DOM yourself to meet whatever attributes required it. In this case, everything affects each other. The DOM makes the changes to the model, and the model moves changes to the DOM. Of course, the person behind the program has to handle all this and understand what is going on in order to update as necessary. The more size and complexity, the worse it will become. AngularJS removes any work in this department.
Better Communication
In a non-context sensitive system, you can run into trouble when messages need to be read by specific controllers with each node. This can get tricky when you are dealing with messages that could be read by parent or child controllers, but you need to go through one but not the other. Angular changes this up by having broadcast default all messages to child controllers. While emit will do the opposite.
You can also do get context-aware communication through the data-binding. As the controller moves a message to look at an update, it will also give the view of the update with changes to the properties. These properties will be on only one side of the controllers, and the other controllers do not always have to get the same properties. They could adjust them before they inherit the new properties. This whole process updates automatically by the other controllers as well.
Dependency Injection:
A lot of the features on this list are focused on things that make your life easier. Well, the included dependency injection is no different. It takes everything about the application and makes it more developer-friendly. The process eliminates you from having to make dependencies yourself or go out and discover them. You simply can ask for what you want, making understanding the process, and testing a whole lot easier. The Dependency Injection will take on the task of building and providing what you need.
All need to do is take service and go add it as a parameter. The instance will become available once the system realizes you requested that service. Or if you’d like, you can put together custom service requests for the dependency injection process. The more automation, the easier your life becomes!
Directives:
We mentioned these before, but the directives are a great feature that on their own that allows for much more control during design. It can be difficult to implement, but can easily become a rewarding process.
If you want to design your own HTML tags to build custom functions, you can use directives. For the creative developer, you can get quite unique with elements and DOM attributes. Manipulating the behavior to new results. If you follow the model of building your app with individual parts, you can make changing functionality much easier. Adding and deleting parts as needed, or making updates as you go.
Simple Testing:
Another streamlined and simplified process. You may be used to build a test page each time you needed to do testing. Then having the page test the results of the component. Well, not anymore. AngularJS has DI to manage the controllers for you and allow data to pass. The DI will generate the needed data to figure out the behavior of each controller.
You will also find a full and unit test runner built-in. The documentation allows the user to quickly see how everything should work. Figuring out issues or how your app is performing is now a breeze. It makes the whole process much easier and encourages better practices in writing testable applications.
Summary:
There is a lot to discover with AngularJS. These features are just the tip of the iceberg in what kind of functionality you can expect. A program is definitely worth checking out for anyone with web development services in mind.
- 投稿日:2020-06-19T14:20:35+09:00
Nodejs用のモデルライブラリを作ってみた【Express】
リポジトリ
https://github.com/kbc18a11/oreoreExpress
ライブラリ本体はoreoreExpress/database/AbstractModel.jsです。解説用の構成や注意点
- FW:Express
- ディレクトリ構成:WebStormの新規Expressプロジェクトの状態に
プロジェクト名/databaseとプロジェクト名/modelというディレクトリを生成- 本記事はチーム開発のメンバーのマニュアルとしても書いていますので、ライブラリには直接関係ないことも書いています。
今回利用しているtestsテーブルは以下の構成になっています
id text created_at updated_at 使い方
リポジトリのものを利用するのであれば、以下のコマンドを実行してください。
npm install mysql2
ライブラリだけを使用するのであれば、
mysql2とdate-utlisをインストールします
npm install mysql2
npm install date-utlis
そして以下のとおり、mysql2の設定ファイルを作ります。
プロジェクト名/database/mysqlConnectionconst mysql = require('mysql2'); //MySQLの接続設定 const connection = mysql.createConnection({ host: 'localhost', user: '', password: '', database: '' }).promise(); module.exports = connection;下記の通り、作りたいモデルクラスに設定します。
また以下のメソッドをオーバーライド、オーバーロードします。
abstractTABLE_NAME
abstractVALIDATIONRULES
updateメソッド
abstractTABLE_NAMEは、連携しているテーブル名のです。
メソッドabstractVALIDATIONRULESは、バリエーションルールを記述する所です。
バリエーションのルールはvalidatorjsのものになります。
https://www.npmjs.com/package/validatorjsプロジェクト名/model/Testsconst AbstractModel = require('./AbstractModel'); const connection = require('../database/mysqlConnection'); require('date-utils'); class Tests extends AbstractModel { constructor() { super(); } /** * テーブル名 * @override * @returns {string} */ static get abstractTABLE_NAME() { return 'tests'; } /** * バリデーションルール * @override * @return {Object} */ static get abstractVALIDATIONRULES() { return { get:{ rule: { id: 'required|integer' }, errorMessage: { required: '必須項目です。', integer: '数値で入力してください' } }, //POSTリクエスト用 post: { rule: { text: 'required' }, errorMessage: { required: '必須項目です。', } }, //PUTリクエスト用 put: { rule: { id: 'required|integer', text: 'required' }, errorMessage: { required: '必須項目です。', integer: '数値で入力してください' } }, //DELETEリクエスト用 delete: { rule: { id: 'required|integer' }, errorMessage: { required: '必須項目です。', integer: '数値で入力してください' } } }; } /** * UPDATE文の準備を行って、親クラスのupdate()に実行をさせる * @param {Object} insertParam */ static async update(insertParam) { //UPDATE文 const sql = `UPDATE ${this.abstractTABLE_NAME} SET text = ?,updated_at = ? WHERE id = ?`; //create_at用の日付時間取得 insertParam.updated_at = new Date().toFormat('YYYY-MM-DD HH:MI:SS'); //SQLの実行 await super.update(insertParam, sql); } } module.exports = Tests;機能
基本的にライブラリの機能を使う場合は、Expressのルーティングファイルの無名関数に
asyncを付与します。プロジェクト名/routes/ルーティングファイルrouter.get('/tests', async (req, res, next) => {そして、ルーティングファイルで以下のライブラリを取り組みます。
プロジェクト名/routes/ルーティングファイルconst express = require('express'); const router = express.Router(); const validator = require('validatorjs'); const Tests = require('../model/Tests');ライブラリでデータベースを操作するメソッドは、Promiseオブジェクトがかかわっているため、呼び出しの際には、
awaitを付与します。プロジェクト名/routes/ルーティングファイル//レコードをすべて取得 await Test.all() //引数idのカラム取得 await Test.find(id) //引数idの存在確認を行う await Test.existId(id) //引数paramの値で新規登録を行う await Test.insert(param) //引数paramの値で更新を行う await Test.update(param) //引数idのレコードを削除する await Test.delete(id)all() レコードをすべて取得
プロジェクト名/routes/test.js/** * @GET * testsのレコードをすべて取得 */ router.get('/tests', async (req, res, next) => { try { //レコードをすべて取得 const allRows = await Tests.all(); //レコードを返す return res.send(allRows); } catch (error) { //レコードの取得失敗時 console.log(error); res.status(500); return res.send({'error': 'サーバー側でエラーが発生しました'}); } });http://localhost:3000/tests に
GETでアクセスします。http[ { "id": 1, "text": "これはテストです", "created_at": "2020-06-18T16:50:45.000Z", "updated_at": null }, { "id": 2, "text": "これはテストです", "created_at": "2020-06-18T16:50:50.000Z", "updated_at": null }, { "id": 3, "text": "これはテストです", "created_at": "2020-06-18T16:50:51.000Z", "updated_at": null } ]find(id) 引数idのカラム取得
プロジェクト名/routes/test.js/** * @GET * 指定されたidのカラムを取得 */ router.get('/tests/:id', async (req, res, next) => { //バリデーションの検証を受ける値 const verificationValue = { id: req.params.id } //バリデーションの結果にエラーがあるかのチェック const validation = new validator( verificationValue, Tests.abstractVALIDATIONRULES.get.rule, Tests.abstractVALIDATIONRULES.get.errorMessage ); if (validation.fails()) { //エラーを422で返す return res.status(422).send({errors: validation.errors.all()}); } try { //レコードを取得 const row = await Tests.find(verificationValue.id); //レコードを返す return res.send(row); } catch (error) { //レコードの取得失敗時 console.log(error); res.status(500); return res.send({'error': 'サーバー側でエラーが発生しました'}); } })http://localhost:3000/(testのidを指定) に
GETでアクセスします。http[ { "id": 1, "text": "これはテストです", "created_at": "2020-06-18T16:50:45.000Z", "updated_at": null } ]また、URIのidを指定する所が以下のとおり数値以外の場合は、エラーメッセージを返します。
http://localhost:3000/aaaa77http{ "errors": { "id": [ "数値で入力してください" ] } }insert() 引数paramの値で新規登録を行う
プロジェクト名/routes/test.js/** * @POST * testsに新しいレコードを挿入 */ router.post('/test', async (req, res, next) => { //バリデーションの検証を受ける値 const verificationValue = { text: req.query.text } //バリデーションの結果にエラーがあるかのチェック const validation = new validator( verificationValue, Tests.abstractVALIDATIONRULES.post.rule, Tests.abstractVALIDATIONRULES.post.errorMessage ); if (validation.fails()) { //エラーを422で返す return res.status(422).send({errors: validation.errors.all()}); } try { //レコードの挿入開始 await Tests.insert({text: req.query.text}); return res.send({'insertResult': true}); } catch (error) { //レコードの挿入失敗時 console.log(error); return res.status(500).send({'insertResult': false}); } });http://localhost:3000/test?text=テストやりたい に
POSTでアクセスします。http[ { "id": 1, "text": "これはテストです", "created_at": "2020-06-18T16:50:45.000Z", "updated_at": null } ]リクエストのボディにtextがない場合は、エラーメッセージを返します。
http{ "errors": { "text": [ "必須項目です。" ] } }update(param) 引数paramの値で更新を行う existId(id) 引数idの存在確認を行う
プロジェクト名/routes/test.js/** * @PUT * レコードの更新 */ router.put('/test/:id', async (req, res, next) => { //バリデーションの検証を受ける値 const verificationValue = { id: req.params.id, text: req.query.text } //バリデーションの結果にエラーがあるかのチェック const validation = new validator( verificationValue, Tests.abstractVALIDATIONRULES.put.rule, Tests.abstractVALIDATIONRULES.put.errorMessage ); if (validation.fails()) { //エラーを422で返す return res.status(422).send({errors: validation.errors.all()}); } //idは存在しないか? if (!await Tests.existId(verificationValue.id)) { //エラーを422で返す return res.status(422).send({ errors: { id: ['idが存在しません'] } }); } try { //レコードの更新開始 await Tests.update(verificationValue); return res.send({'updateResult': true}); } catch (error) { //レコードの更新失敗時 console.log(error); return res.status(500).send({'updateResult': false}); } });http://localhost:3000/test?text=テストやりたい に
PUTでアクセスします。http{ "updateResult": true }URIのidを指定する所が以下のとおり数値以外やリクエストのボディにtextがない場合は、エラーメッセージを返します。
http://localhost:3000/test/aaahttp{ "errors": { "id": [ "数値で入力してください" ], "text": [ "必須項目です。" ] } }そして、
find(id)の引数のidがレコードに存在しない場合、このようなエラーを返しますhttp{ "errors": { "id": [ "idが存在しません" ] } }delete(id) 引数idのレコードを削除する
プロジェクト名/routes/test.js/** * @DELETE * レコードの削除 */ router.delete('/test/:id', async (req, res, next) => { //バリデーションの検証を受ける値 const verificationValue = { id: req.params.id, } //バリデーションの結果にエラーがあるかのチェック const validation = new validator( verificationValue, Tests.abstractVALIDATIONRULES.delete.rule, Tests.abstractVALIDATIONRULES.delete.errorMessage ); if (validation.fails()) { //エラーを422で返す return res.status(422).send({errors: validation.errors.all()}); } //idは存在しないか? if (!await Tests.existId(verificationValue.id)) { //エラーを422で返す return res.status(422).send({ errors: { id: ['idが存在しません'] } }); } try { //レコードの削除開始 await Tests.delete(verificationValue.id); return res.send({'deleteResult': true}); } catch (error) { //レコードの削除失敗時 console.log(error); return res.status(500).send({'deleteResult': false}); } })http://localhost:3000/(testのidを指定) に
DELETEでアクセスします。http[ { "id": 1, "text": "これはテストです", "created_at": "2020-06-18T16:50:45.000Z", "updated_at": null } ]URIのidを指定する所が以下のとおり数値以外の場合は、エラーメッセージを返します。
http://localhost:3000/aaaa77http{ "errors": { "id": [ "数値で入力してください" ] } }そして、
find(id)の引数のidがレコードに存在しない場合、このようなエラーを返しますhttp{ "errors": { "id": [ "idが存在しません" ] } }
- 投稿日:2020-06-19T13:48:32+09:00
GraphQLクライアント周り調べて設定した
雑多な感じです
TypeScript編
サンプルリポジトリ: https://github.com/sisisin-sandbox/try-gql/pull/1
(業務で使ってる構成の再現構成となっています)クライアントライブラリ選び
多分選択肢はこんな
- apollo client
- relay
- 生http client
観測範囲でapollo使ってるのを多く見ていた/relayはReact特化っぽい/apolloのclient cacheを使うケースがありそうなどでapollo clientを選択
(といいつつrelayはろくに調べてないです)apollo client設定
apollo clientについて
doc: https://www.apollographql.com/docs/
apolloはreact向けのクライアント以外にもサーバー実装・iOSクライアント・graphqlの運用サポートSaaSという具合に色々提供してくれてるすごいやつ(小並感)
この記事ではreact向けクライアントについて書きますapollo clientはサーバーへのqueryはもちろんgraphql schemaを用いたローカルの状態管理の仕組み、middleware的なレイヤー(apollo-link)など様々な機能があります
この辺多少調べて設定したのでそれについて書いていきますやったこと
- ApolloClientをapollo-boostを使わずに設定(apollo-boostは設定を細かく出来ないので
- apollo client:codegenコマンドによる型定義コード生成
- vscodeのapollo clientプラグイン設定
ApolloClientをapollo-boostを使わずに設定
ドキュメントにapollo-boost/apollo-clientの例が入り混じってて大変紛らわしいやつ
apollo-boostはエントリー用パッケージのようなので実戦投入するなら自前でapollo-clientを設定するのが良い
(apollo-client v3ではapollo-boostぐらいのお手軽設定から始められるようになるっぽい)初期設定自体はapollo-boostからのマイグレーションというドキュメントがあるのでこれを見ればいい
参考リンク: https://www.apollographql.com/docs/react/migrating/boost-migration/apollo client:codegenコマンドによる型定義コード生成
typescript使うなら型定義の生成は人権
選択肢としては他にもいくつかあったが、apollo-clientを利用することにした他の選択肢
- graphql code generator
- ts-graphql-plugin
- 後なにか他のも見かけたけど忘れた・・・
選んだ理由としては、
- 一通りの機能をサポートしている
- 例えばts-graphql-pluginは別名に対応してないなどがある
- 同じエコシステムにしとくほうが無難な気がした(完全に個人の感想)
- apollo client v3でcustom scalarのdeserializeが書けるようになりそうで、もしそこと生成する型定義が上手くつなぎ込めるなら・・・?という希望もあった
- graphql-code-generator乗り換えは検討しても良いかも?とは思っている
- 機能はこちらのほうが豊富(後述)
その他細々と以下のような設定をしたりしました
globalTypes.tsは/__generated__ではなく/src配下へ配置
- create-react-appなどを利用する場合に取り回しやすいように
- コード生成時に
--customScalarsPrefixを付け、global.d.tsのようなファイルを配置してそのファイル内でdeclare type GitHubDate=stringのように定義
- まあサンプル通りですが
- introspectionを使わずschema.graphqlをダウンロードしてきてコード生成に利用
- 諸般の事情によりサーバーがVPN内にいてCIからエンドポイントを叩けないのでこのような構成に
vscodeのapollo clientプラグイン設定
インストールするだけっちゃするだけ
fragmentとかへ定義ジャンプ出来たりフィールド名のサジェストが出てきたりして便利
これがないとschemaを手書きすることになるので大変ですやってない(やれてない)こと
- apollo-link-scalarsを用いたデータ取得時のcustom scalarのdeserialize
- vscodeのgraphql プラグインによるエディタからのquery実行
- graphql code generatorによるコード生成
- ApolloClientのラッパー実装
apollo-link-scalarsを用いたデータ取得時のcustom scalarのdeserialize
custom scalarとして定義された
Date型(ISO 8601形式文字列であるとする)をfetchしてきて受け取った時点でnew Date(val)しておきたい、というのが人情だと思うんですよ(?)
サーバーサイドでは実際そういう仕組はあるけれど、apollo clientではcustom scalarに対してそういった統一的なfetch時のdeserialize処理を挟むことが出来ませんそこでapollo-linkの仕組みを利用して、fetch時にcustom scalarをdeserializeする処理を挟み込んでやってしまおう、というのが apollo-link-scalars というライブラリのアイデアです
が、問題点があり、
apollo-cache-persistなどのhttpを経由しない値の取得で意図通り動かない
- https://github.com/apollographql/apollo-client/issues/585#issuecomment-400777797
- apollo-linkの仕組みはあくまでもhttpリクエストを飛ばすとき・レスポンスを受け取るときに解決されるので、メモリキャッシュからの読み込みでは適用されないのが原因
apollo-link-scalarsの実装ではサーバーサイド向けの実装に依存している( Example of loading a schema参照のこと)
- 結果、bundleサイズがやべえことになる
- 仮にそこを無視できるとしても、 サーバーサイド実装はユニバーサルなjsとしてメンテされる保証がないのでリスキー
- 現時点で
graphql-toolsはfsに依存してたりしてcreate-react-appなどではそのままでは動かない- ついでにschema.graphql相当の文字列を突っ込むだけでも結構サイズが膨れる
custom scalar対応のfeature requestはあるのでapollo client v3に期待・・・?
vscodeのgraphql プラグインによるエディタからのquery実行
ちょっと触って詰まったのでちゃんと調べてない・・・(後で試したい)
graphql code generatorによるコード生成
先述したようにapollo client:codegenを利用してるのでこちらはやってません
機能的にはこちらのほうが出来ることは多いので再検討しても良いかもとは考えてます
- cameCaseの別名で一括解決してくれる
- custom scalar向けの型を独自定義できる
- 生成した型を利用するreact hookも一緒にコード生成してくれる
ただコード生成って機能が豊富になっても初手の手数が減るだけなのでよっぽど魅力的じゃないとわざわざ乗り換えるほどでもない、とはなりそう・・・
ApolloClientのラッパー実装
例えばreduxのmiddlewareレイヤーでクライアントを使いたい場合に欲しいはずなのでそのうちやるつもりです
具体的には、graphql errorのときにrejectedなPromiseが返ってくるためにanyで推論されてしまって、せっかくGraphQLError型があるのに使いにくい、というような点が挙げられます
他にはqueryメソッドの型引数が省略可能(デフォルトany)になるなど、微妙な点があるのでその辺をラップした自前のクライアントクラスを作ろうかなとは考えてます
この辺は追々。Scala編
sbt-graphqlの話を書こうと思ったけどタイムアップ
- 投稿日:2020-06-19T13:40:25+09:00
Shift + tab キーでフォーカス逆移動が利かない
Shift + tab キーでフォーカス逆移動が利かない
「Shift」+「Tab」でフォーカス逆移動は、デフォルト設定なのに
押下してもそのまま次の要素へ移動する事象が発生。原因
JSファイルの中でエンターキーとTabキー押下制御の処理を書いており、
それが優先されていたため、Shift + tab キーの処理が反映されていなかった!js/*エンターキー、タブキーの場合*/ if (e.keyCode == 13 || e.keyCode == 9 && event.shiftKey == false) { if(e.keyCode == 9){ /*やりたい処理*/ }else if((e.keyCode == 13 && !foucsInput.is(':focus'))){ /*やりたい処理*/ } }「 if (e.keyCode == 13 || e.keyCode == 9 && event.shiftKey == false) { 」
&&event.shiftKey == false を書き加えると動きました。
- 投稿日:2020-06-19T13:35:29+09:00
UnityのWebGLで作ったサイトにドラッグ&ドロップでVRMを出現させる
VRMの切り替えをドラッグ&ドロップでやりたい
VRMをPartyParrot風に表示できるPartyParrotVRMを作ってみたという記事にまとめている「PartyParrotVRM」を作っている際に、VRMの切り替えをドラッグ&ドロップでやりたいと思いました。
途中で固まってしまうのがネックなのですが別スレッドで実行するなどの処理がうまくいかず現時点ではこのようになっています…一応切り替えれているのでいいかなと…
drag&dropを検知してUnityにデータを渡す
VRMのSDKを入れたプロジェクトを作成しておきます。
そして、以下のようなC#を定義してGameObjectに追加します。using System; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine; using VRM; namespace Provider { public class VrmProvider : MonoBehaviour { private int length; private int index; private List<byte> byteContent = new List<byte>(); public void ByteLength(int length) { this.length = length; index = 0; byteContent.Clear(); } public void AddRange(string byteString) { var decode = Convert.FromBase64String(byteString); byteContent.AddRange(decode); index += 1; if (length != index) return; Spawn(); } public void Spawn() { var vrmBytes = byteContent.ToArray(); var context = new VRMImporterContext(); context.ParseGlb(vrmBytes); context.Load(); context.ShowMeshes(); context.EnableUpdateWhenOffscreen(); } } }上記のcsのメソッドに対してjsからデータを送り込んでいきます。
UnityのWebGLTempleteを編集して以下のjsを追加して、index.htmlにもscriptタグで追加しました。
dragAndDrop.jsdocument.addEventListener('DOMContentLoaded', function () { var dropArea = document.getElementById('screen') function confirmVrmFile(files) { let length = files.length, file for (let i = 0; i < length; i++) { file = files[i] let isVrmFile = file.name.endsWith('.vrm') if (isVrmFile) { // .vrmのbyteをstringに変換する let reader = new FileReader() reader.onload = function () { let source = this.result let bytes = new Uint8Array(source) let len = source.byteLength let byteString = "" for (var i = 0; i < len; i++) { byteString += String.fromCharCode(bytes[i]) } var base64String = window.btoa(byteString) sendUnity(base64String) }; reader.readAsArrayBuffer(file) break } } } function sendUnity(base64String) { // stringの容量が大きいままSendMessageに渡そうとするとエラーが起きるので文字列を分割する let splitLength = 1000 let len = parseInt(base64String.length / splitLength) unityInstance.SendMessage("VrmProvider", "ByteLength", len + 1) for (let i = 0; i < len; i++) { let next = base64String.substr(i * splitLength, splitLength) unityInstance.SendMessage("VrmProvider", "AddRange", next) } let last = base64String.substr( splitLength * len, base64String.length % splitLength ) unityInstance.SendMessage("VrmProvider", "AddRange", last) } dropArea.addEventListener('dragover', function (event) { event.preventDefault() event.dataTransfer.dropEffect = 'copy' dropArea.classList.add('dragover') }) dropArea.addEventListener('dragleave', function () { dropArea.classList.remove('dragover') }) dropArea.addEventListener('drop', function (event) { event.preventDefault() dropArea.classList.remove('dragover') confirmVrmFile(event.dataTransfer.files) }) })実際にPartyParrotVRMというので使っているものから少し変えていますがこれでドラッグ&ドロップで動くと思います。
まとめ
画面が固まらないようにするのができれば切り替えたいとは思っています。最低限の切り替え処理にはなりますがそんなにコードを書かなくてもできたので便利だなと思っています。VRMも簡単に扱えてとても便利な印象を持ちました。
- 投稿日:2020-06-19T13:15:06+09:00
JavaScriptクイズアプリ
jsコード
const quiz = [ //配列にオブジェクトを格納 { question: "ゲーム市場、最も売れたゲーム機は次のうちどれ?", answers: [ "スーパーファミコン", "プレイステーション2", "ニンテンドースイッチ", "ニンテンドーDS", ], correct: "ニンテンドーDS" //選択されたHTMLのテキストと比較する変数を指定 },{ question: "日本の初代内閣総理大臣は誰?", answers: [ "伊藤博文", "大隈重信", "安倍晋三", "高橋是清", ], correct: "伊藤博文" },{ question: "横浜ベイスターズが初めて日本一に輝いた時の監督は?", answers: [ "三浦大輔", "ラミレス", "中畑清", "権藤博", ], correct: "権藤博" } ]; const quizLength = quiz.length; let quizIndex = 0; let score = 0; const $button = document.getElementsByTagName("button"); const buttonLength = $button.length; //buttonをノードとして取得し、変数化 const setupQuiz = function(){ //quizを解いたら次のquizをセットする関数 document.getElementById("js-question").textContent = quiz[quizIndex].question; let buttonIndex = 0; let buttonLength = $button.length; while(buttonIndex < buttonLength){ $button[buttonIndex].textContent = quiz[quizIndex].answers[buttonIndex]; buttonIndex++ } } setupQuiz(); const clickHandler = function(e){ if (quiz[quizIndex].correct === e.target.textContent){ //イベントをオブジェクトとして取得 + テキストの内容と解答を比較 window.alert("正解"); score++; //正解ならスコアを+1 } else { window.alert("不正解"); } quizIndex++; //次のクイズをセットするためにインデックスを+ if(quizIndex < quizLength){ setupQuiz(); }else{ window.alert("終了!あなたの正解数は"+score+"/"+quizLength+"です。"); } } let handlerIndex = 0; while(handlerIndex < buttonLength){ $button[handlerIndex].addEventListener("click",function(e){ clickHandler(e); }); handlerIndex++ }HTMLコード
・・・前略 <body> <div class="container"> <div class="jumbotron mt-5"> <div class="d-flex justify-content-center"> <div id="js-question" class="alert alert-primary" role="alert"> A simple primary alert—check it out! </div> </div> <div id="js-items" class="d-flex justify-content-center"> <div class="m-2"> <button type="button" id="js-btn-1" class="btn btn-primary">Primary</button> </div> <div class="m-2"> <button type="button" id="js-btn-2" class="btn btn-primary">Primary</button> </div> <div class="m-2"> <button type="button" id="js-btn-3" class="btn btn-primary">Primary</button> </div> <div class="m-2"> <button type="button" id="js-btn-4" class="btn btn-primary">Primary</button> </div> </div> </div> </div> <script src="main.js"></script> </body>解説
①クイズの内容とボタン等にテキストを.textContent = でjsの内容に変更する
②設定したクイズの内容が一問解いたら切り替わるようにquizIndexを関数の内部で++する。
③正解の場合スコアを記録し、alertで通知


















