20211011のJavaScriptに関する記事は20件です。

p5.js と anime.js を組み合わせた Tweenアニメーション【その2:キーフレームを使ってみる】

今回の内容は、「anime.js」と「p5.js」を組み合わせた Tweenアニメーションに関して書いた以下の記事の続きにあたるものです。 ●p5.js と anime.js を組み合わせた Tweenアニメーション - Qiita  https://qiita.com/youtoy/items/361282f1113d88ff5f05 具体的には、上記の記事の中のお試しでは使っていなかった、キーフレーム・タイムラインといった機能を試す話になります。 さっそく本題へ 前回の記事と同様に、今回も「p5.js Web Editor」でプログラムを作成していきます。 また、前回の記事の内容が試せているという前提ですので、p5.js Web Editor上のライブラリ設定の話など、そいうった部分は前回の記事をご参照ください。 キーフレームについて 前回の記事は、以下のようなシンプルな Tweenアニメーションを行う内容でした。 円が (100,100) という位置から (300, 250) という位置へ移動 円が移動する際に、大きさが大きくなる(パラメータ的には 50 から 150 に変化) が移動する際に、色が明るく変化(グレースケールの値で 100 から 200 に変化) イージングは線形な変化のもの とある状態A から別の状態B に変化するというものです。 今回用いるキーフレームは、そのような状態を複数用意して「状態A ⇒ 状態B ⇒ 状態C ⇒ 状態D ...」という変化をさせたりする時などに便利なものです。 公式の情報を見ると、以下の「Animation Keyframes」と「Property Keyframes」というものがあるようです。  ●Documentation | anime.js 【Animation Keyframes】   https://animejs.com/documentation/#animationKeyframes  ●Documentation | anime.js 【Property Keyframes】   https://animejs.com/documentation/#propertyKeyframes 今回は Animation Keyframes のほうを利用します。 キーフレームを使ったプログラム 以下にプログラムを掲載します。 let ball, count = 0; function setup() { createCanvas(500, 500); ball = { x: 100, y: 100, diameter: 50, col: 100, }; anime({ targets: ball, delay: 1000, keyframes: [ { x: 350, y: 250, diameter: 150, duration: 2000 }, { x: 100, y: 200, diameter: 20 }, { x: 150, y: 150}, { x: 450, y: 300, duration: 300 } ], easing: "linear", update: function () { console.log(count++); }, }); } function draw() { background(0); noStroke(); fill(ball.col); circle(ball.x, ball.y, ball.diameter); } 上記でポイントになるのは、以下の部分です。 初期状態では x: 100, y: 100, diameter: 50 となっていた位置・大きさが、4回変わるような設定です。 keyframes: [ { x: 350, y: 250, diameter: 150, duration: 2000 }, { x: 100, y: 200, diameter: 20 }, { x: 150, y: 150}, { x: 450, y: 300, duration: 300 } ], また、 duration はデフォルト値でない値を使いたい場合に指定します。 ここに、ミリ秒の数値を指定してやると、変化が起こるまでの時間をしていした長さにすることができます。 ちなみに、公式ドキュメントを確認すると「duration のデフォルト値」は「1000 [ミリ秒]」になっているようです。 ●Documentation | anime.js 【Duration】  https://animejs.com/documentation/#duration なお、公式ドキュメントの情報によると、上記のキーフレームの配列の中に時間情報を書かず、その外に総合の時間だけを書く、という使い方もできるようです。 その場合、総合の時間の長さが、各キーフレームに均等に割りふられるようでした。 動作させたもの 今回のプログラムを実際に動作させた時の様子がこちらです。 update の処理 今回、プログラムの中に以下を加えています。 update: function () { console.log(count++); }, 上記の処理は、以下の公式ドキュメントの処理を試してみるために入れていたもので、今回のキーフレームの処理に必須のものではありません。  ●Documentation | anime.js 【Update】   https://animejs.com/documentation/#update この関数が呼ばれる度にカウントアップする数字を出力するというのをやってみていて、どれくらいの頻度で処理が行われているかを確認しようとしたものでした(ちなみに、処理は 60fps になるようでした)。 ちなみに、公式ドキュメントの公式サンプルでは、以下のような処理が書かれていたりします。 var updates = 0; anime({ targets: '.update-demo .el', translateX: 270, delay: 1000, direction: 'alternate', loop: 3, easing: 'easeInOutCirc', update: function(anim) { updates++; progressLogEl.value = 'progress : '+Math.round(anim.progress)+'%'; updateLogEl.value = 'updates : '+updates; } }); おわりに 前回の記事の内容に続いて、p5.js と anime.js の組み合わせを試してみました。 今回使ったキーフレーム以外に、試したいものでタイムラインがあります。 これについても、別途、記事を書ければと思っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Plotly/Dash チートシート

Plotly&Dash を使う上でよく使うテクニックをまとめました。 メインPythonで少しCSS&JavaScriptです。 ※内容は随時更新していきます 散布図のカラーテーマを変更する fig = px.scatter( df, x="sepal_width", y="sepal_length", color_discrete_sequence=px.colors.sequential.Viridis, // ここでテーマ設定 ) #使用できるテーマは下記で確認可能 #colorscales = px.colors.named_colorscales() #print(colorscales) Plotly: Built-in Color Display Mode Bar非表示 Plotlyでグラフを表示したとき表示される、右上のアイコンバーを非表示にする。 Plotly (CSSで変更する場合) .chart .modebar-container{ display: none; } DashのFigureで変更する場合 dcc.Graph( id='graph', config={ 'displayModeBar':False, }, figure = figure, // 各自で用意 ), JavaScriptで編集する場合 Plotly.newPlot('myDiv', data, layout, {displayModeBar: false}); Configuration Options in JavaScript Map 右下企業情報 非表示 .chart .mapboxgl-control-container{ display: none; } グラフ周りのカラーを変更 外周エリアの白色部分とセンターのグレーカラーを透過させる。 fig.update_layout( paper_bgcolor='rgba(0,0,0,0)', # legend and axis transparent plot_bgcolor='rgba(0,0,0,0)', # graph area transparent ) グラフのMargin, Padding調整 下記はMarginとPaddingを0にするコード。 fig.update_layout( margin = dict(l=0, r=0, b=0, t=0, pad=0), ) Plotly レスポンシブ対応 Responsive / Fluid Layouts in JavaScript
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】条件分岐 v-if について

はじめに こんにちは! 松本 佑大(まつもと ゆうだい)と申します! 今回は条件分岐 v-if についてアウトプットしていきます! V-ifとは 条件分岐v-ifを利用することによって要素に表示/非表示を切り替えることができます。 書き方 基本的な書き方は以下のようになります。 HTML <div id ="app"> <p v-if="toggle"> Good morning! </p> </div> Vue.js var app = new Vue({ el:'#app', data:{ toggle: true, } プロパティtoggle(任意)を定義し、真偽値trueorfalseを書きます。 (trueなら表示、falseなら非表示になります。) 次にp要素にvーifディレクティブを設定し、値にはプロパティtoggleを指定します。 上記にはtoggle:trueに指定してありますので、Good morning!が表示されています。 ※v-ifでは表示/非表示がdisplay:noneではなく、p要素そのものが削除される仕組みになっており、描画コストが高くなる恐れがあります。頻繁に表示/非表示を繰り返すのならばv-showディレクティブを使用することを推奨します。(別記事で解説します) まとめ ・要素にv-if="プロパティ"を指定。 ・data:{プロパティ:真偽値}を設置。 プロパティは任意になります! 最後に 今回はv-ifについてアウトプットしました。 もし認識の仕方が間違っていましたら、コメント等頂けると幸いです。 今後ともQiitaにてアウトプットしていきます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RustでWebAssemblyを書いてみる

Rustとは 効率性、安全性などに優れた言語で、元々はMozillaの中の人によって作られました。 コンパイルしてネイティブコードに落とせるので、CやC++の代替として期待されています。 後発なので言語仕様な他の言語の美味しいところを集めてもいますが、なかなか独特です。 学習が難しいと言われつつも、エンジニアに最も愛される言語(Stack Overflow調べ)では連覇を重ねています。 私もまとまったものを書くのは初めてですので、拙いところは見逃してくださいませ。 WebAssemblyとは WASMと略します。 MDNによれば: WebAssembly はモダンなウェブブラウザーで実行できる新しいタイプのコードです。ネイティブに近いパフォーマンスで動作するコンパクトなバイナリー形式の低レベルなアセンブリ風言語です。さらに、 C/C++ や Rust のような言語のコンパイル対象となって、それらの言語をウェブ上で実行することができます。 WebAssembly は JavaScript と並行して動作するように設計されているため、両方を連携させることができます。 実験 JavaScriptで書きにくいバイナリ処理をRust=>WASMで書くようなケースを想定して、今回は簡単な画像処理をRustで書いてWasmにコンパイルし、JavaScriptから呼びだして実行するようなサンプルプログラムを書いてみようと思います。 ソースコード一式はこちらに起きました。 ワークスペースの作成 vue-cliとwasm-packを使ってワークスペースを作成します。 $ vue create wasm_test $ cd wasm_test $ wasm-pack new wasm Rust側の実装 imageクレート(≒パッケージ)を使用して画像変換を行うコードを実装します。 JS側にexportするメソッドは#[wasm_bindgen]というattributeを付けます。 src/lib.rsの中身は以下のようにしました。 use wasm_bindgen::prelude::*; use image::*; enum ConvertMode { Grayscale, FlipHorizontal, FlipVertical, RotateLeft, RotateRight, Blur, } fn convert(mode: ConvertMode, width: u32, height: u32, raw_data: Vec<u8>) -> Vec<u8> { let rgba = RgbaImage::from_raw(width, height, raw_data).expect("error"); let di = DynamicImage::ImageRgba8(rgba); let converted = match mode { ConvertMode::Grayscale => { let gray = di.into_luma8(); // 再度rgbaに戻す DynamicImage::ImageLuma8(gray) }, ConvertMode::FlipHorizontal => di.fliph(), ConvertMode::FlipVertical => di.flipv(), ConvertMode::RotateLeft => di.rotate270(), ConvertMode::RotateRight => di.rotate90(), ConvertMode::Blur => di.blur(3.0), }; converted.into_rgba8().to_vec() } #[wasm_bindgen] pub fn grayscale(width: u32, height: u32, raw_data: Vec<u8>) -> Vec<u8> { convert(ConvertMode::Grayscale, width, height, raw_data) } #[wasm_bindgen] pub fn flip_horizontal(width: u32, height: u32, raw_data: Vec<u8>) -> Vec<u8> { convert(ConvertMode::FlipHorizontal, width, height, raw_data) } #[wasm_bindgen] pub fn flip_vertical(width: u32, height: u32, raw_data: Vec<u8>) -> Vec<u8> { convert(ConvertMode::FlipVertical, width, height, raw_data) } #[wasm_bindgen] pub fn rotate_left(width: u32, height: u32, raw_data: Vec<u8>) -> Vec<u8> { convert(ConvertMode::RotateLeft, width, height, raw_data) } #[wasm_bindgen] pub fn rotate_right(width: u32, height: u32, raw_data: Vec<u8>) -> Vec<u8> { convert(ConvertMode::RotateRight, width, height, raw_data) } #[wasm_bindgen] pub fn blur(width: u32, height: u32, raw_data: Vec<u8>) -> Vec<u8> { convert(ConvertMode::Blur, width, height, raw_data) } wasm-pack build でビルドすると、wasm/pkgの下に関連ファイル一式が出力されます。 JavaScriptから使う WASMのロード このようなメソッドでimportを使ってロードします。 async loadWasm() { const wasm = import("../wasm/pkg"); this.wasm = await wasm; } WASM内のメソッドを呼ぶ 通常のJavaScriptオブジェクトのように呼び出すことができます。 this.wasm.grayscale(...) ソース全体 App.vueの全体はこのようになります。 <template> <div class="container"> <div class="row my-2"> <input type="file" ref="file" @change="loadImage"> </div> <div class="row my-2"> <div class="col "> <button class="btn btn-primary" type="button" @click="original">オリジナル</button> </div> <div class="col"> <button class="btn btn-primary" type="button" @click="grayscale">グレースケール</button> </div> <div class="col"> <button class="btn btn-primary" type="button" @click="flip_horizontal">左右反転</button> </div> <div class="col"> <button class="btn btn-primary" type="button" @click="flip_vertical">上下反転</button> </div> <div class="col"> <button class="btn btn-primary" type="button" @click="rotate_left">左回転</button> </div> <div class="col"> <button class="btn btn-primary" type="button" @click="rotate_right">右回転</button> </div> <div class="col"> <button class="btn btn-primary" type="button" @click="blur">ぼかし</button> </div> </div> <div class="row my-2"> <div class="col"> <h4>Original</h4> <img ref="original" :style="original_style"> </div> <div class="col"> <h4>Converted</h4> <canvas ref="converted" :style="style"></canvas> </div> </div> </div> </template> <script> const ConvertMode = { Grayscale: 1, FlipHorizontal: 2, FlipVertical: 3, RotateLeft: 4, RotateRight: 5, Blur: 6, }; const MAX_IMAGE_SIZE = 500; export default { name: 'App', data () { return { wasm: null, original_width: 0, original_height: 0, width: 0, height: 0, }; }, created () { this.loadWasm(); }, computed: { style () { return { width: this.width + "px", height: this.height + "px", }; }, original_style () { return { width: this.original_width + "px", height: this.original_height + "px", }; }, }, methods: { async loadWasm() { const wasm = import("../wasm/pkg"); this.wasm = await wasm; }, readFile () { const input = this.$refs.file; if (!input.files.length) return; const file = input.files[0]; const reader = new FileReader(); const p = new Promise((resolve, reject) => { reader.addEventListener("load", function () { resolve(reader.result); }); reader.addEventListener("error", function () { reject("error."); }); }); reader.readAsDataURL(file); return p; }, drawOriginalImage(url) { const img = this.$refs.original; const p = new Promise((resolve) => { img.addEventListener("load", function () { resolve(); }); }); img.src = url; if (img.complete) { return Promise.resolve(); } return p; }, original () { const out = this.$refs.converted; const out_ctx = out.getContext('2d'); const img = this.$refs.original; this.width = this.original_width; this.height = this.original_height; const max = Math.max(this.width, this.height); out_ctx.clearRect(0, 0, max, max); out_ctx.drawImage(img, 0, 0, this.original_width, this.original_height); }, convert (mode) { const canvas = this.$refs.converted; const ctx = canvas.getContext('2d'); const raw = ctx.getImageData(0, 0, this.width, this.height); let ret_data; switch (mode) { case ConvertMode.Grayscale: ret_data = this.wasm.grayscale(this.width, this.height, raw.data); break; case ConvertMode.FlipHorizontal: ret_data = this.wasm.flip_horizontal(this.width, this.height, raw.data); break; case ConvertMode.FlipVertical: ret_data = this.wasm.flip_vertical(this.width, this.height, raw.data); break; case ConvertMode.RotateLeft: ret_data = this.wasm.rotate_left(this.width, this.height, raw.data); break; case ConvertMode.RotateRight: ret_data = this.wasm.rotate_right(this.width, this.height, raw.data); break; case ConvertMode.Blur: ret_data = this.wasm.blur(this.width, this.height, raw.data); break; } const buf = Uint8ClampedArray.from(ret_data); if (mode === 4 || mode === 5) { const w = this.width; const h = this.height; this.width = h; this.height = w; canvas.width = this.width; canvas.height = this.height; } const max = Math.max(this.width, this.height); ctx.clearRect(0, 0, max, max); ctx.putImageData(new ImageData(buf, this.width, this.height), 0, 0); }, grayscale () { this.convert(ConvertMode.Grayscale) }, flip_horizontal () { this.convert(ConvertMode.FlipHorizontal); }, flip_vertical () { this.convert(ConvertMode.FlipVertical); }, rotate_left() { this.convert(ConvertMode.RotateLeft); }, rotate_right() { this.convert(ConvertMode.RotateRight); }, blur() { this.convert(ConvertMode.Blur); }, async loadImage () { const url = await this.readFile(); await this.drawOriginalImage(url); const img = this.$refs.original; const w = Math.min(img.naturalWidth, MAX_IMAGE_SIZE); const h = img.naturalHeight * (w / img.naturalWidth); this.width = this.original_width = w; this.height = this.original_height = h const out = this.$refs.converted; out.width = this.width; out.height = this.height; this.original(); }, }, }; </script> <style lang="scss"> #app { img, canvas { margin: 0; padding: 0; } } </style> まとめ バイナリなど低レベル操作が必要だったりパフォーマンスが欲しい場合には、C(++)やRustの知識があれば比較的簡単にJavaScriptと連携してウェブアプリに組み込むことができます。ぜひ一度お試しください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのスコープをマスターしよう!!

はじめに 初学者です。 スコープについて学習したので備忘録。 間違っていたら教えていただけると幸いです。 スコープとは スコープとは「実行中のコードから値と式が参照できる範囲」です スコープによって呼び出せる関数や変数に違いがでます。 スコープの種類 スコープにはいくつかの種類があります 1. グローバルスコープ 1. スクリプトスコープ 1. 関数スコープ 1. ブロックスコープ 各種スコープの説明 グローバルスコープ グローバルスコープはグローバルコンテキスト内でvarやfuntionで定義した定義物です。 グローバルスコープに分類されるものはグローバルオブジェクト(windowオブジェクト)のプロパティとして値が保持されているものです。 スクリプトスコープ スクリプトスコープはグローバルコンテキスト内でletやconstで宣言された宣言物です。関数宣言を使わずに関数式で作成た関数も スクリプトスコープに分類されます。 グローバルスコープとスクリプトスコープは厳密には違うスコープですが、どこからでも呼び出すことができるという点で どちらもグローバルスコープとして扱われることが多いです window.a = 0 console.log(window.b)//省略しない呼び出し方 console.log(b)//省略した呼び出し方 グローバルオブジェクト(windowオブジェクト)に保存されている値でもwindow.を省略して呼び出すことができるため、参照の仕方+どこからでも呼び出し可能な点はスクリプトスコープと違いがありません。 厳密にスコープを分けると下記のようになりますが、どちらもグローバルオブジェクトと呼ばれることが多いです let a = 0; //Script scope var b = 0; //global scope function c() {} // grobal scope 関数スコープ function a (){ let b = 0; console.log(b)//○ } a(); console.log(b)//x //関数の中と外ではスコープが違う ブロックスコープ { let c = 1; console.log(c)//○ } console.log(c)//x //関数式以外の波括弧で囲まれた範囲をブロックスコープという //使用するための条件 let or constを使う varはx //関数式は無視されるため変数宣言で関数を作る //基本はforやif文で生成される レキシカルスコープ スコープの種類では出しませんでしたが、番外編と思ってください。 実行中のスコープからみた外部スコープがレキシカルスコープです let a = 2 function fn1() { let b = 1 function fn2() { let c = 3; console.log(b)//○ } fn2() } fn1(); //fn1はfn2のレキシカルスコープ(外部スコープなので)bを参照することができる let a = 2 function fn1() { let b = 1 } fn1(); function fn2() { let c = 3; console.log(b)//x } fn2() //こちらのコードではfn2のレキシカルスコープにある変数や関数はaと関数fnの2つ //fn2の中にあるbは参照できない まとめ グローバルスコープはグローバルオブジェクト(windowオブジェクト)に保存される変数や関数 スクリプトスコープはグローバルコンテキスト内でletやconstで宣言されたもの 関数スコープは関数の{}の中の記述(関数宣言に限る) ブロックスコープは関数宣言された{}以外の{}の中(forとかifとかの{}) レキシカルスコープは実行中のスコープからみた外側のスコープ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ml5.jsでカメラ画像から姿勢推定を行う時にハマったこと

はじめに この記事ではJavaScriptのライブラリの1つ,ml5.jsを利用した際のハマったことについて紹介したいと思います. ml5.jsとは ml5.jsを簡単に説明すると,簡単に機械学習に触れることが出来るライブラリと捉えています. 基本的な画像分類・文章生成から姿勢推定等といった様々な機械学習に触れることが出来ます. ml5.js aims to make machine learning approachable for a broad audience of artists, creative coders, and students. The library provides access to machine learning algorithms and models in the browser, building on top of TensorFlow.js with no other external dependencies. 今回はこのml5.jsを使って姿勢推定を利用した際のことについて記載しています. (もっと詳細にml5.jsについて知りたい方に関しては公式ドキュメントやQiita等の記事を見ることをお勧めします) 前提 そもそも姿勢推定とは 姿勢推定とは人の画像から関節点を識別し,その情報からどのような姿勢・状態になっているかを推定する技術です. 画像引用先:TensorFlow 「ポーズ推定 | TensorFlow Lite」 https://www.tensorflow.org/lite/examples/pose_estimation/overview 行いたいこと 今回Webカメラを通じリアルタイムに姿勢を推定したく思っています. またどのようなカメラ形式(カメラ解像度)でも正常に姿勢を推定することも必要だと考えています. 問題 どのようなカメラ形式(カメラ解像度)でも下記のようなコードにて姿勢推定を行いました. 今回のコードの特徴として,このような感じとなっています. 1. カメラの解像度に合わせてvideoタグの縦横を調整したいためwidth・heightの値を100%にしている. 2. videoタグの縦横に合わせてcanvasタグの縦横を指定したいためにresizeForAll関数にてリサイズを行っている. react let classifier; let poses; const App = () => { // useRef const videoRef = useRef(); const canvasRef = useRef(); // poseNet起動関数 const poseNetPlay = () => { classifier = ml5.poseNet(videoRef.current, () => { console.log("PoseNet Loaded!"); classifier.on('pose', result => { poses = result; }); }); resizeForAll(); }; // camara&canvasリサイズ関数 const resizeForAll = () => { // videoの大きさにcanvasを合わせる canvasRef.current.width = videoRef.current.width; canvasRef.current.height = videoRef.current.height; }; // 描画関数 const CanvasOutput = (poses) => { const context = canvasRef.current.getContext('2d'); // 初期化 context.clearRect(0, 0, context.canvas.clientWidth, context.canvas.clientHeight); // この後に描画する処理を書いていますが略します }; useEffect(() => { navigator.mediaDevices .getUserMedia({video: true, audio: false}) .then((stream) => { videoRef.current.srcObject = stream; videoRef.current.play(); }); }, []); useInterval(() => { // canvasに描画する関数 CanvasOutput(poses); }, 20); return( <div className="App"> <div className="wrapper"> <video id="video" ref={videoRef} height="100%" width="100%" /> <canvas className="canvas" ref={canvasRef} /> </div> <button onClick={poseNetPlay} >aaa</button> </div> ) } export default App; 出力結果として下記のように出力されました. フリー素材ぱくたそ(www.pakutaso.com) 姿勢推定が出来ていない…というよりはなぜか上に小さく姿勢推定が行われているように見えます. ただこれだと本来の目的を満たしていないため,この問題を解決しなければいけません. 原因 原因究明 今回canvasの大きさをvideoに合わせるために下記のようにリサイズを行っています. // camara&canvasリサイズ関数 const resizeForAll = () => { // videoの大きさにcanvasを合わせる canvasRef.current.height = videoRef.current.height; canvasRef.current.width = videoRef.current.width; }; しかし,videoタグのwidth・heightにカメラの解像度は入っているのでしょうか。。。 console.log("videoRef.current.width, videoRef.current.height") を見てみます. なんとvideoタグのwidth・heightを100%指定にしているのにもかかわらず,カメラの解像度とは全く別の値が入っていることが分かります. またwidth・heightの値を利用して姿勢推定の座標を推定しているため(ここに関してはもしかしたら違うかもしれません)width・heightの値がかなり重要となっています. それではなぜwidth・heightにカメラの解像度が入っていないのでしょうか? 原因特定 要素には縦横(width・height)がありますが,複数のwidth・heightがhtmlには存在することが理由です. そしてカメラの解像度はwidth・heightには入っておらず,全く別の値に入っています.そのためその値をwidth・heightに代入する必要があります. 解決策 様々なアプローチを試した結果,videoタグのClientWidth・ClientHeightにカメラの解像度が入ってることが分かりました. そのためClientWidth・ClientHeightの値をwidth・heightに代入することで正常に表示することが出来ます! // camara&canvasリサイズ関数 const resizeForAll = () => { // 追加した部分 videoRef.current.width = videoRef.current.clientWidth; videoRef.current.height = videoRef.current.clientHeight; // videoの大きさにcanvasを合わせる canvasRef.current.width = videoRef.current.width; canvasRef.current.height = videoRef.current.height; } 書き換えた後の出力結果として下記のようになります. フリー素材ぱくたそ(www.pakutaso.com) きちんと体の姿勢(輪郭)に合わせて推定されていることが分かります! まとめ 今回はml5.jsでカメラ画像から姿勢推定を行う時にハマったこと,Webカメラを取り扱った際に起こったことについて記載しました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React初心者がReactについて適当にまとめる

都内のSESで働いている人がReactについて簡単にまとめた記事です。 なかなかプログラミングは得意じゃないですが得意じゃないなりに頑張ってますので色々ご教授いただけると幸いです。。 ※この記事ではCodeSandboxを使用して学習したものを載せています。 Reactって? Reactの公式HPを見てもらったらわかると思いますがユーザーインターフェース構築のためのJavaScriptライブラリです。 よく似たものとしてフレームワークというものがありますがフレームワークではありません。(Angularとか?) コンポーネントというものを作成してユーザーインターフェースを効率的に構築できる。 「ReactはJSでユーザーインタフェースを効率よく構築できるよ!!」ってことだと思います。笑 JSX記法 JSX記法はReactの大きな特徴だと思います。 JSX記法とはJavaScript内でHTMLを記述する方法です。 例 まずindex.htmlのどの部分にReactを差し込むかを記す目印を書いてあげます。 index.html <!DOCTYPE html> <html lang="en"> <head> <!-- 省略 --> </head> <body> <!-- どの部分にReactを差し込むかを刺す目印 --> <div id="root"></div> </body> </html> 次にindex.jsで差し込みたい内容をアロー関数で用意してあげます。 index.js import Reactdom from "react-dom"; const App = () => { return <h1>こんにちわ</h1> //・・・① }; ReactDom.render(<App />, document.getElementById("root")); //・・・② 上記の書き方で画面にはこんにちわと表示されます。 簡単な説明 ①Appという名前の関数を用意してその戻り値としてHTMLタグを戻す。 ②ReactDomのrender関数を使って指定の場所に表示する render(render対象,render箇所); 上記の例で第一引数が<App />となっていますが、Reactでは関数名をHTMLのようにタグで囲むことによってコンポーネントとして扱うことができます。 JSXのルール return文のあとが複数行になる場合は()で囲ってあげる必要があります。 index.js import react-dom from "ReactDom"; const App = () => { return (           <h1>こんにちわ</h1> <p>Hello</p> ); }; ReactDom.render(<App />, document.getElementById("root")); ()で囲うだけだとエラーになってしまいます。 ルールとして return以降は一つのタグで囲われていなければならないというのがあります。 解決方法は3通りあります。 ①divタグで囲む方法 const App = () => { return ( <div>           <h1>こんにちわ</h1> <p>Hello</p> </div> ); }; ②Reactに用意されているFragmentをimportして使う方法 import {Fragment} from "react"; const App = () => { return ( <Fragment>           <h1>こんにちわ</h1> <p>Hello</p> </Fragment> ); }; ③空のタグで囲む方法 const App = () => { return ( <>           <h1>こんにちわ</h1> <p>Hello</p> </> ); }; ②と③はdivタグとは違ってDOMが生成されないのでエラーを回避するためだけに外を囲みたい場合などは有効手段になります。 コンポーネントって? index.jsにこれまで書いていましたが何画面も書くとなると何千行とかになってしまいますよね。(想像しただけでも怖い笑) React開発では画面の要素を様々な粒度のコンポーネントに分割することで再利用性や保守性を高めるのが基本になります。 コンポーネント化ってどうやるの? まず、App.jsを作成します。 先ほど作成したindex.js内で記述した関数をApp.js内に記述します。 App.js const App = () => { return ( <>           <h1>こんにちわ</h1> <p>Hello</p> </> ); }; export default App; App.jsを他のファイルでも使えるようにexportする必要があります。 そしてApp.jsをindex.jsでimportします index.js import react-dom from "ReactDom"; import App from "./App"; ReactDom.render(<App />, document.getElementById("root")); 以前と変わらず表示されたと思います。 このように、各ファイルをコンポーネントを定義をしておいて、他のファイルから読み込んでパーツを組み合わせるように画面を構築していくことはReact開発の醍醐味だと思います。 これらがしっかりできると楽しいと思います。笑 コンポーネントのファイルは.jsでも大丈夫ですがわかりやすくするために.jsxに変更できます。 コンポーネントファイルの拡張子は.jsxに変更するといいと思います。 とりあえず。。。 とりあえず長くなりそうなのでここまでにします。 ここまで見てくださりありがとうございます。 何かあればお願いします。 まだまだ、雑魚なので精進します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのCall stack (コールスタック)

はじめに 初学者です。 コールスタックについて学習したので備忘録 コールスタックってなんぞや? 実行中のコードがたどってきたコンテキストの積み重ね JavaScriptエンジンがどのような経路を辿ってコードにたどり着いたのか記録している 実際のコードで確認しよう function a() { } function b() { a(); } function c() { b(); } c(); コールスタック aの関数コンテキスト bの関数コンテキスト cの関数コンテキスト ブローバルコンテキスト 常に最上部が実行中のコンテキストになる 処理が完了すると上から徐々に消えていく LIFO コールスタックの仕組みは「後入れ先だし」LIFO (Last In, First Out)という まとめ Call stack(コールスタック)とはコンテキストの積み重ね。 Last In, First Out(後入れ先だし)で住み重なる 処理が完了すると最上部から消えていく
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのホイスティングって何者??

はじめに 初学者です。 ホイスティングについて学んだので備忘録を残します。 ホイスティングってなに? コンテキスト内で宣言した変数や関数の定義をコード実行前にメモリに配置すること 宣言の巻き上げともよばれる 宣言した関数よりも前でその関数を呼び出しても実行できますよね。 それです いろんなケースのコードを見ていこう 関数 a(); function a() { console.log('a is called') //関数の定義が実行より後でもホイスティングによって実行可能になる } var console.log(b); //処理結果 undefined var b = 0; // // この場合のホイスティングは //変数bのメモリースペースを確保し値に特殊な値(undefined)を設定 let・const console.log(b); //処理結果 エラー let b = 0; // //letやconstはホイスティングで初期化(undefined)されない //挙動が違うためvarの使用は非推奨である ここで一度整理したい ホイスティングが行われることによって関数宣言よりも前で宣言した関数を呼び出しても使える これはJavaScriptエンジンがコンテキストを生成した際に変数や関数を発見しコード実行前にメモリに配置するからである。 変数(定数)の定義にはvar/let/constがあるが、varとlet+constの挙動には違いがある。 変数宣言よりも前でその変数を参照した場合 varの場合は変数をメモリに配置し、値にundefinedを代入する挙動 let+constの場合はホイスティングが行われずにエラーになる(厳密には行われているらしいがエラーがでる) このようにvarとlet+constには挙動の違いがある。これはホイスティングだけではなく、ブロックスコープの生成の有無など その他の面でも挙動に違いがある。これらを理由に現在varを使うのは非推奨である ホイスティングが発生するタイミングはコンテキストが生成される瞬間 function a() { console.log(z) let z = 1; } //関数aの中身は記述の順番が間違っているが、呼び出していないため //関数aのコンテキストは生成されていない //関数aのコンテキストが生成された瞬間ホイスティングが発生しエラー //になる(関数aを呼び出すとエラー) 関数式 a(); const a = function() { let c = 1; console.log(c) } //処理結果:エラー //関数式は変数の宣言と同じ挙動をするため関数宣言と異なり //宣言より前で呼び出すとエラーになる コンテキストが生成されるたびにホイスティングが発生し宣言部がメモリースペースに配置される まとめ コンテキストが生成された瞬間にホイスティングが発生する ホイスティングによって関数や変数がコード実行前にメモリに保存される メモリに保存してからコードを実行するため関数はどこからでも呼び出すことができる ホイスティングの発生はコンテキストが生成されたタイミングなので、関数スコープ内で記述が間違っていても 呼び出していなければホイスティングは行われない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

kintone 関連レコード詳細画面で遅延表示の動作検証

kintone のアップデートで、関連レコードの詳細画面が遅延表示となったので、kintone イベント処理時の動作検証をしてみました。 試しに関連レコードの文字列項目を赤表示してみます。 関連レコードの詳細画面で遅延表示の概要 関連レコードを遅延表示することで、「詳細画面で表示が開始されるまでの待ち時間が短縮」されるようです。 kintone 2021年10月版 主なアップデート 関連レコード一覧フィールドが配置されているアプリのフォーム表示開始までにかかる時間を短縮 アプリのレコード詳細・編集画面にて、読み込みに時間のかかる関連レコード一覧が配置されているとき、フォームの表示が開始されるまでの待ち時間が短縮されました。 ・レコード詳細画面の読み込み(9月版まで): レコードの内容が何も表示されない待ち時間が長く、その間、何もすることができませんでした。 ・レコード詳細画面の読み込み(10月版より): 関連レコード一覧以外のフィールドが素早く表示され、レコードの内容の閲覧や編集をすぐに開始することができます。 関連レコードのDOM 関連レコードのラベルを含んだ部分、レコードを表示する TABLE 部分があります。 kintone イベント処理時の関連レコードの状態 詳細画面表示イベントで、関連レコードのどの部分が表示されるか?されないのかを確認します。 (function() { 'use strict'; kintone.events.on(['app.record.detail.show','app.record.create.show','app.record.edit.show'], function(event) { var ref = document.querySelectorAll('.control-reference_table-field-gaia>.control-value-gaia'); var ref2 = document.querySelectorAll('.reference-subtable-gaia'); console.log(event.type, ref); console.log(ref2); return event; }); })(); 詳細画面表示イベント 詳細画面表示イベントでは、TABLE の部分が無く、その上のDIV は、ある状態です。 編集画面表示イベント 編集画面表示イベントでは、これまで通りTABLE の部分がある状態です。 関連レコード表示の監視 MutationObserverで、DOM要素の変化を監視できますので、これを使って関連レコードが表示されることを監視してみます。 (function() { 'use strict'; kintone.events.on(['app.record.detail.show','app.record.create.show','app.record.edit.show'], function(event) { var ref = document.querySelectorAll('.control-reference_table-field-gaia>.control-value-gaia'); var ref2 = document.querySelectorAll('.reference-subtable-gaia'); console.log(event.type, ref); console.log(ref2); ref.forEach(function(xx) { console.log('xx', xx); var mo = new MutationObserver(function() { console.log('xx2', xx); }); mo.observe(xx, { childList: true }); }); return event; }); })(); 詳細画面表示イベント時('XX')は、関連レコードの TABLE が無く、'XX2' のタイミングで関連レコードの TABLE が生成されているのが分かります。 関連レコードの DOM 操作 詳細画面表示の関連レコードの DOM 操作を行ってみます。 関連レコードの文字列項目を赤表示する例です。 ※kintone カスタマイズでDOM 操作は、非推奨となっています。 (function() { 'use strict'; const setReferenceStyle = function(xx) { var ele1 = xx.querySelectorAll('.control-single_line_text-field-gaia>.control-value-gaia'); ele1.forEach(function(yy) { yy.style.color = 'red'; }); } kintone.events.on(['app.record.detail.show','app.record.create.show','app.record.edit.show'], function(event) { var ref = document.querySelectorAll('.control-reference_table-field-gaia>.control-value-gaia'); var ref2 = document.querySelectorAll('.reference-subtable-gaia'); console.log(event.type, ref); console.log(ref2); ref.forEach(function(xx) { console.log('xx', xx); setReferenceStyle(xx); var mo = new MutationObserver(function() { console.log('xx2', xx); setReferenceStyle(xx); }); mo.observe(xx, { childList: true }); }); return event; }); })(); 無事に、詳細画面でも文字列項目を赤表示できました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

3kbのHTML to Reactパーサ「DOMParserReact」で、HTML文字列のaタグをNext.jsのLinkコンポーネントで表示する

作ったライブラリの宣伝記事です。 DOMParserReactとは DOMParserReactは軽量なHTML to Reactパーサーです。HTMLテキストをReactのコンポーネントに変換して表示します。また、表示の際にHTMLのタグを任意のコンポーネントに置き換えることができます。 主な使用想定は、個人ブログなどのコンテンツ部分です。コンテンツ部分は、MarkdownのファイルやヘッドレスCMSのリッチテキストで管理し、それをHTMLにしたものを表示していると思います。このライブラリを使用すれば、そのHTMLを表示する際、CSS in JS等のReactの技術を使って装飾したり、タイトルのようにaタグをSPA用の Link コンポーネントに置き換えたりすることができるようになります。 そして、このライブラリの一番の特徴は、その軽さです。minifyで3kb(v0.2.0時点)しかありません。類似ライブラリと比べて非常に軽量なのは、独自のパーサーを使わず、ブラウザJSのAPI DOMPerser を使用しているからです。 ライブラリ バンドルサイズ (minify) 備考 dom-parser-react 3kb 筆者作 html-react-parser 27kb rehype-react 42kb unifiedのプラグイン。計測時は、rehype-reactの他、unified、rehype-dom-parserを使用 ※上記は筆者調べ。rollup.jsでバンドルして計測。ただし、react、react-domのサイズは除外 使い方 それでは、実際の使用方法を紹介します。 NPMで公開していますので、インストールは、npm/yarnから行います。 npm i dom-parser-react # or yarn add dom-parser-react DOMParserReactを使用するには、コンポーネントをインポートして変換するHTML文字列を source で指定してください。 そのHTMLを表示するコンポーネントが作成されます。 import DOMParserReact from 'dom-parser-react' // import { renderToStaticMarkup as render } from 'react-dom/server' const htmlText = ` <h1>HTML Text</h1> ` const App = () => <DOMParserReact source={htmlText} /> render(<App />) // `<h1>HTML Text</h1>` 上記は dangerouslySetInnerHTML と結果がほぼ同じですので、今度はタグを置き換えてみましょう。 components プロパティに { 置換対象のタグ: 置換後のコンポーネント } という形式で指定します。下記は、h1 タグを Title コンポーネントに置き換える例です。 const htmlText = ` <h1>HTML Text</h1> ` const Title = (props) => <div className="title"> <h1 {...props} /> </div> const App = () => <DOMParserReact source={htmlText} components={{ h1: Title }} /> render(<App />) // `<div class="title"><h1>HTML Text</h1></div>` h1 が div.title で囲まれて表示されました。 今回は囲むだけでしたが、例えば、CSS in JSでスタイリングしたコンポーネントを指定したり、アンカーリンクのアイコン等を追加したりもできます。 また、複数種類のタグを置き換えたい場合は複数記載すれば対応可能です。 const components = { h1: Title, h2: SubTitle, p: Paragraph, } <DOMParserReact source={htmlText} components={components} /> Next.jsで使用する それでは、実際にNext.jsで使用してみましょう。 と言っても、上記のように components を指定するだけです。 import Link from 'next/link' import DOMParserReact from 'dom-parser-react' const components = { a: ({ href, ...props }) => ( <Link href={href}> <a {...props} /> </Link> ), } const Post = ({ htmlText }) => <DOMParserReact source={htmlText} components={components} /> もしかしたら、読者の中には本当に動くか疑問に思っている人がいるかもしれません。最初に説明したように DOMPerser はブラウザJSのAPIで、Next.jsのSSR/SSGが実行されるNode.jsにそのAPIはありません。 ですが、DOMParserReactは問題なく動作します。Node.jsでの実行時は、自動でJSDOMというライブラリをインポートし、DOMPerser をエミュレートします。これによってブラウザと同じように動作し、SSR/SSGが実行されます。もちろん、ブラウザではJSDOMはバンドルしませんので、軽量なサイズのままです。 最後に 上記の通りこのライブラリは、HTMLコンテンツを簡単に装飾することができ、ライブラリサイズも軽量でSSR/SSGにも対応しています。ぜひ、個人ブログなどに使用してみてください。 おまけ DOMParserReactを使用した、筆者のブログ Qiita記事を優先したため、最新の投稿はライブラリ使用前の去年のものになっています。そのうち、DOMParserReact作成時のこととかを書いていきたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

3kbのHTML to Reactパーサ「DOMParserReact」で、aタグをNext.jsのLinkコンポーネントで表示する

作ったライブラリの宣伝記事です。 DOMParserReactとは DOMParserReactは軽量なHTML to Reactパーサーです。HTMLテキストをReactのコンポーネントに変換して表示します。また、表示の際にHTMLのタグを任意のコンポーネントに置き換えることができます。 主な使用想定は、個人ブログなどのコンテンツ部分です。コンテンツ部分は、MarkdownのファイルやヘッドレスCMSのリッチテキストで管理し、それをHTMLにしたものを表示していると思います。このライブラリを使用すれば、そのHTMLを表示する際、CSS in JS等のReactの技術を使って装飾したり、タイトルのようにaタグをSPA用の Link コンポーネントに置き換えたりすることができるようになります。 そして、このライブラリの一番の特徴は、その軽さです。minifyで3kb(v0.2.0時点)しかありません。類似ライブラリと比べて非常に軽量なのは、独自のパーサーを使わず、ブラウザJSのAPI DOMPerser を使用しているからです。 ライブラリ バンドルサイズ (minify) 備考 dom-parser-react 3kb 筆者作 html-react-parser 27kb rehype-react 42kb unifiedのプラグイン。計測時は、rehype-reactの他、unified、rehype-dom-parserを使用 ※上記は筆者調べ。rollup.jsでバンドルして計測。ただし、react、react-domのサイズは除外 使い方 それでは、実際の使用方法を紹介します。 NPMで公開していますので、インストールは、npm/yarnから行います。 npm i dom-parser-react # or yarn add dom-parser-react DOMParserReactを使用するには、コンポーネントをインポートして変換するHTML文字列を source で指定してください。 そのHTMLを表示するコンポーネントが作成されます。 import DOMParserReact from 'dom-parser-react' // import { renderToStaticMarkup as render } from 'react-dom/server' const htmlText = ` <h1>HTML Text</h1> ` const App = () => <DOMParserReact source={htmlText} /> render(<App />) // `<h1>HTML Text</h1>` 上記は dangerouslySetInnerHTML と結果がほぼ同じですので、今度はタグを置き換えてみましょう。 components プロパティに { 置換対象のタグ: 置換後のコンポーネント } という形式で指定します。下記は、h1 タグを Title コンポーネントに置き換える例です。 const htmlText = ` <h1>HTML Text</h1> ` const Title = (props) => <div className="title"> <h1 {...props} /> </div> const App = () => <DOMParserReact source={htmlText} components={{ h1: Title }} /> render(<App />) // `<div class="title"><h1>HTML Text</h1></div>` h1 が div.title で囲まれて表示されました。 今回は囲むだけでしたが、例えば、CSS in JSでスタイリングしたコンポーネントを指定したり、アンカーリンクのアイコン等を追加したりもできます。 また、複数種類のタグを置き換えたい場合は複数記載すれば対応可能です。 const components = { h1: Title, h2: SubTitle, p: Paragraph, } <DOMParserReact source={htmlText} components={components} /> Next.jsで使用する それでは、実際にNext.jsで使用してみましょう。 と言っても、上記のように components を指定するだけです。 import Link from 'next/link' import DOMParserReact from 'dom-parser-react' const components = { a: ({ href, ...props }) => ( <Link href={href}> <a {...props} /> </Link> ), } const Post = ({ htmlText }) => <DOMParserReact source={htmlText} components={components} /> もしかしたら、読者の中には本当に動くか疑問に思っている人がいるかもしれません。最初に説明したように DOMPerser はブラウザJSのAPIで、Next.jsのSSR/SSGが実行されるNode.jsにそのAPIはありません。 ですが、DOMParserReactは問題なく動作します。Node.jsでの実行時は、自動でJSDOMというライブラリをインポートし、DOMPerser をエミュレートします。これによってブラウザと同じように動作し、SSR/SSGが実行されます。もちろん、ブラウザではJSDOMはバンドルしませんので、軽量なサイズのままです。 最後に 上記の通りこのライブラリは、HTMLコンテンツを簡単に装飾することができ、ライブラリサイズも軽量でSSR/SSGにも対応しています。ぜひ、個人ブログなどに使用してみてください。 おまけ DOMParserReactを使用した、筆者のブログ Qiita記事を優先したため、最新の投稿はライブラリ使用前の去年のものになっています。そのうち、DOMParserReact作成時のこととかを書いていきたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのコンテキスト

はじめに コンテキストについて学習したので備忘録を残します。 初学者です。 間違っている箇所がありましたら申請お待ちしております。 コンテキストってなに? JavaScriptでの意味 実行コンテキスト(コードを実行する際の文脈・状況) コンテキストには3種類ある 覚えるべきは2つ グローバルコンテキスト 関数コンテキスト evalコンテキストは不要 関数eval自体が非推奨のため グローバルコンテキスト 使用可能な値 実行中のコンテキスト内の変数・関数 グローバルオブジェクト this 関数コンテキスト 使用可能な値 実行中のコンテキスト内の変数・関数 arguments super(特殊な環境でのみ使用可能) this 外部変数 実際のコードで確認しよう コメントアウトをみてね // グローバルコンテキスト JavaScriptファイル内直下の実行環境(変数や関数) let a = 0; function b() { console.log(this, arguments, a); //これに加えてsuperと外部変数が使える(外部変数 = 関数の外の変数) // b();の関数コンテキスト } b(); // 関数コンテキスト 関数{}の中の実行環境 (波括弧の中) // thisやargumentsは実行される環境によって取得する値が違う まとめ コンテキストとはコードを実行する際の文脈・状況 グローバルコンテキスト jsファイル直下の関数や変数 - 実行中のコンテキスト内の変数・関数 - グローバルオブジェクト - this 関数コンテキスト 関数の中括弧の中 - 実行中のコンテキスト内の変数・関数 - arguments - super(特殊な環境でのみ使用可能) - this - 外部変数
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】変数と参照の振り返り④ AND条件とOR条件

はじめに Udemyの【JS】ガチで学びたい人のためのJavaScriptメカニズムの講座の振り返りです。 前回の記事 目的 変数についての理解を深める 本題 1.AND条件とOR条件 ANDの条件と仕組み // AND条件 かつ a && b // aとbがtureでないといけない const a = 1; const b = 1; // 出力結果で1が返ってくる = ture console.log(a && b) const a = 0; const b = 1; // まずaがturethyなのかどうか確認する  // aがtureの場合 => bの値を返す // aがfalseの場合  => aの値を返す // ここではaは0なのでaの値を返す console.log(a && b) // もしcの値を代入したら const a = 1; const b = 2; const c = 3; // この場合a,b,cそれそれfalseに値しないのでcの値3が出力される console.log(a && b && c) // AND条件はfalthyなものが途中であった場合は、その値を返す // もしなかった場合は一番最後の値を出力する ORの条件と仕組み // OR条件 または  // aもしくはbがture const a = 0; const b = 1; console.log(a || b) // // まずaがfalsyかturethyなのかどうか確認する  // // aがtureの場合 => aの値を返す // // aがfalseの場合  => bの値を返す const a = 1; const b = 0; // この場合aがturethyなので、1と出力される console.log(a || b) // もしa = 0, b = 1, c = 2なら const a = 0; const b = 1; const c = 2; // この場合aがfalsyなので、bの値1と出力される console.log(a || b) // 下記の場合でもbがturethyなので1が出力される // もし、b = 0(false)ならcの値2が出力される console.log(a || b || c) // OR条件はtureが見つかった時点で値を出力される // 見つからなかった場合は一番最後の値が出力される もし、AND条件とOR条件が混在していたら、 const a = 0; const b = 1; const c = 3; // 下記の条件では3が出力される  // aはfalseでOR条件なのでbの値を調べに行く // bはturethyなのでAND条件によってcの値を調べに行く // cもturethyなのでAND条件によってcの値を調べに行く console.log(a || b && c) このように書いてしまうと、わかりにくいので 基本的にはグループ化をして記述する // もしd = 0が条件に加わったら const a = 0; const b = 1; const c = 3; const d = 0; // グループ毎に演算をおこない、最後にAND条件で結果を出力される console.log((a || b) && (c || d)) // aとbはOR条件によって、tureが見つかった時点で結果を出力するので、1 // cとdはOR条件によって、3と出力される // AND条件はfalsyな値が見つかった時点で結果を出力するが今回はどちらもtureなので // 一番最後の値である右のグループの3が出力される 今日はここまで! 参考にさせて頂いた記事 【JS】ガチで学びたい人のためのJavaScriptメカニズム
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

バイバイ、サーバーサイドレンダリング。Prerender.ioーSEOを考慮したSPA

本記事は、掲載元で2日で23K「いいね」を獲得したSviat Kuzhelev氏による「Say goodby to Server Side Rendering. Prerender.io --- SPAs with SEO in mind.」(2021年9月21日公開)の和訳を、著者の許可を得て掲載しているものです。 バイバイ、サーバーサイドレンダリング。Prerender.ioーSEOを考慮したSPA image is taken from this resource はじめに 「完璧とは、付け加えるものがない時ではなく、取り除くものがない時に達成される。」アントワーヌ・ドゥ・サン=テグジュペリ 私たちは皆SPA❤️です。どんな形でも良いのです。React、Vue、Angular、さらにはVanillaコーディングがどのプラットフォームで使用されたかなど関係ありません。 顧客体験を損なうことなく、JavaScriptウェブサイトの静的HTMLをクローラーに提供することで、上位表示を実現したいですか?そのアイディアをここで紹介します。 今までとこれから サーバーサイドレンダリング(SSR)は、何十年にもわたりうまく機能していました。解析済みのHTMLデータをクライアントサイドに送信すると、ブラウザは追加のジョブなしで、UI上の内容を簡単に表示します。変更があった場合は、ページを完全にリロードして、バックエンドから再初期化する必要があります。 ?ちょっと待ってください。私はSPA開発が大好きです!SSRはSPAとうまく動作するので、それが問題なのではありません。next.jsのようなフレームワークは数多くありますし、すべてをゼロから作成することもできるため、SEO対策を考慮した静的ウェブサイトを常に構築できます。一件落着です? SSRの話を聞いて頭に浮かぶ唯一の懸念は、クライアントサイドレンダリング(CSR)に関することでしょう。そう、皆CSRが大好きです。でもここでは、SSRとCSRの違いをすべて説明するために寄り道はしません(さもなくば、この記事を読み始めなくなるでしょう)。 CSRの使用に関する共通の考え方(ワクワクします)は、複雑なUIシステムのアーキテクチャのバランスを保ちながら、認知リソースを大幅に削減できるというものです。UIで結果をすぐに確認でき、バックエンド関連の問題のデバッグに費やす時間を短縮します。どういうことか説明しましょう。 必要なもの ライブラリ/フレームワークのReactを私くらい好きだといいのですが。DOM APIはサーバー上でアクセスできないため、既知のReact.renderDOM()メソッドはここでは使えなくなります。(SSR好きの人は)「さあ、多くのコードベースを書き直してください」と言うでしょう!? 少なくともReact.hidate()に変更してアプリのクラッシュを解消し、(必ず必要になる)SSR関連の勉強をどんどん始めましょう。それから、状態管理について学ぶことになるでしょう…。 ?SSR対応のSPAベースアプリケーションの作成が必要な場合、状態管理はかなり大変です。 reduxなどの状態管理用ツールを追加しましょう。UIマークアップだけでなく、バックエンドから挿入された後、最初のUIレンダリングで追加された初期化データも同期している間に、面倒で嫌になるでしょう。 テクノロジーを使えば簡単にできるのに、なぜこのような多くの途方に暮れそうな処理をする必要があるのでしょうか。 もし、左手でCSRのコードを書き、右手で好きな飲み物を飲めるとしたら?(またはその逆??)もし、SSRが原因でアプリ内の何かがクラッシュし始めても、慌てることなくDOM APIにアクセスできるとしたら?もし…このリストはスパゲッティのように長くなるかもしれませんが、そこから解放されることにワクワクするなら、要点に進みましょう。 そろそろ変わり目だ この10年間、静的アプリがいかに人気だったかをGoogleが理解した時点で、それが現実になりました。より簡単に、より速く、処理するために需要が生まれたのです。これはウェブコーディングの大きな飛躍であり、SEOも進化すべきです。2015年発表のGoogleのクロールロボットは、動的クライアントサイドのウェブページ(静的SPAなど)を解析できるようになりました。 とはいえ、時々、落とし穴があるかもしれません。ロボットがタイムアウトする前に、ページに必要なリソース(画像、CSS、データ)を取得できる保証はありません:( Googleが、ダイナミックレンダリングと呼ばれるウェブページのスクリーンショット(.png/.jpg/.webp/.svgファイルではない;))を開発者が作成できると発表したすぐ後に、完全にプリレンダリングされたHTMLを保存できるようになりました。 ?ダイナミックレンダリングーMicrosoftが推奨するJavaScriptのレンダリング方法。詳細については、開発者向けドキュメントを参照してください。 ロボットがウェブページにアクセスすると、バックエンドミドルウェアがリクエストをハイジャックして、SEOに最適化された実際のスクリーンショットをクローラーエンジンに送り返します。エンドユーザーは、通常のHTML+CSS+JSパックを展開します。一般的に、サーバーは人間とロボットを区別できるようになり、人間には完全な体験を、ロボットには軽量のHTMLバージョンを提供します。 ダイナミックレンダリングの設定方法 基本的な設定はとても簡単です。クローラーエンジンのリストを作成し、リクエストヘッダをテストするだけです。 export const botUserAgents = [ 'googlebot', 'bingbot', 'linkedinbot', 'mediapartners-google', ]; 次に、ユーザーエージェントがデスクトップコンテンツとモバイルコンテンツのどちらをリクエストしているか判断します。ダイナミックサービングを使用して、適切なデスクトップ版またはモバイル版を提供します。以下は、判断の設定例です。 const isPrerenderedUA = userAgent.matches(botUserAgents) const isMobileUA = userAgent.matches(['mobile', 'android']) if (!isPrerenderedUA) { // serve regular, client-side rendered content } else { servePreRendered(isMobileUA) // serve the mobile version } これは、よくある仕組みの概念です。詳細については、Google公式ページを参照してください。 Prerender.ioの設定 もし自分ですべて作成するのが苦手なら、Prerender.ioの検討をお勧めします。かなり直感的なサービスで、作業が簡単です。 まず、パッケージを自分のリポジトリにインストールします。 // ./ npm install prerender-node --save 次に、NodeJSベースのアプリ(expressJS、nextJSなど)をセットアップし、ミドルウェアを追加します。 // ./server/index.js app.use(require('prerender-node')); …または、prerender.ioのアカウントを持っている場合は、トークンを使用します。 // ./server/index.js app.use(require('prerender-node').set('prerenderToken', 'YOUR_TOKEN')); ほら、とても簡単でしょう? スケールアップ さらに詳しく知りたい場合は、GithubのPrerender.io公式リポジトリを掘り下げると良いでしょう。スナップショットのテスト、カスタマイズ、スナップショット保存のための独自サーバー作成に関する高度なリソースまであります? まとめ この記事が役に立てば嬉しいです?私が何か見逃していたり、SSRはまだ素晴らしい仕事ができると思ったりしたら、議論を持ちかけてください。 いつか、このSEO関連の混乱がすべてなくなり、AIが地球を支配するようになるでしょう。それが実現するまで:)、開発者の作業を楽にしてくれるPrerender.ioなどの関係者に感謝しましょう! それまでの間、役に立つ記事があります。 ? React 2021でプログレッシブ画像セットを扱う方法 ?未来への道。速度を落とさずにReactでRetinaとWebP画像を統合する ? Reactで画像セット作成を自動化する方法 ❤️2021年デザイナー/開発者向けプログレッシブ画像セット作成の自動化フロー それではまた!? 翻訳協力 この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。 Original Author: Sviat Kuzhelev Original Article: Say goodby to Server Side Rendering. Prerender.io --- SPAs with SEO in mind. Thank you for letting us share your knowledge! 選定担当: @gracen 翻訳担当: @gracen 監査担当: - 公開担当: @gracen ご意見・ご感想をお待ちしております 今回の記事はいかがでしたか? ・こういう記事が読みたい ・こういうところが良かった ・こうした方が良いのではないか などなど、率直なご意見を募集しております。 頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に コメント欄にてご投稿ください。Twitterでもご意見を受け付けております。 皆様のメッセージをお待ちしております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCodeでJestを活用するための便利な拡張機能

Jestを少しでも簡単・便利に書くために便利な拡張機能です。 Jestとは JavaScript用のテストフレームワークです。 sample.js const sample = (param) => { return param + 1; }; export default sample; sample.test.js import sample from "./sample"; test("sample", () => { expect(sample(1)).toBe(2); }); 拡張機能 1. Jest ファイルを変更したタイミングで、自動でJestが実行される。 コード記入→yarn testとか叩く必要がなくなるので、テストコードの記入やリファクタリングの高速化が図れる。 2. Jest Runner テストメソッド上部に Run | Debug が表示され、クリックすることで個別でのテストが実行できる。 3. Jest Test Explorer Jest Runnerが一覧からも実行できるようになったもの。 個人的にはJest Runnerよりも使いやすいと思う。 4. Jest Snippets JestのMatcherをサジェストに表示してくれる。 teを選択した場合はexpect().toEqual();となる。 toEqual以外も用意されているため、コーディングが早くなれる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

npm install / yarn add したら tsc が通らなくなった話

React のカスタムフックをテストするために @testing-library/react-hooks をインストールしました。 そして tsc を実行すると types 周りがコケる現象が発生しました。 $ tsc --noEmit node_modules/@testing-library/react-hooks/node_modules/@types/react/index.d.ts(3092,14): error TS2300: Duplicate identifier 'LibraryManagedAttributes'. node_modules/@testing-library/react-hooks/node_modules/@types/react/index.d.ts(3103,13): error TS2717: Subsequent property declarations must have the same type. Property 'a' must be of type 'DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>', but here has type 'DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>'. ... 本記事はこのエラーの考察と解決方法を記録したものです。 TL;DR 違うバージョンの @types/* が解決されたので npm なら npm dedupe && npm install yarn なら npx yarn-deduplicate && yarn install1 を実行して一個にまとめること。 発生経緯 説明用のサンプルを公式から拝借しました。 こんな感じにテストケースを書いています。 useCounter.spec.ts import { renderHook, act } from '@testing-library/react-hooks' import useCounter from './useCounter' test('should increment counter', () => { const { result } = renderHook(() => useCounter()) act(() => { result.current.increment() }) expect(result.current.count).toBe(1) }) このテストケースを jest から実行するときはなんの問題もありませんが、tsc を実行したときだけコケました。 問題の原因 プロジェクトの構造はこんな感じにしています。 ├── README.md ├── src │ ├── components <- React のコンポーネントを置くディレクトリ │ │ └── MyComponent.ts │ └── models <- カスタムフックとかを置くディレクトリ │ ├── useCounter.ts │ ├── useCounter.spec.ts tests フォルダを別に切り出すのではなく、テストファイルをテストしたいもののすぐ近くに置く構成にしています。 そして tsconfig.json の include フィールドに src 以下を追加すると tsconfig { "include": [ "src/**/*" ] } tsc を実行する時ファイルパスを指定しなくてもOKになります。 $ tsc --noEmit この構成にしているため、tsc の実行時は src 以下のソースファイルとテストファイルの両方を見るようになっています。 今回のエラーメッセージ $ tsc --noEmit node_modules/@testing-library/react-hooks/node_modules/@types/react/index.d.ts(3092,14): error TS2300: Duplicate identifier 'LibraryManagedAttributes'. node_modules/@testing-library/react-hooks/node_modules/@types/react/index.d.ts(3103,13): error TS2717: Subsequent property declarations must have the same type. Property 'a' must be of type 'DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>', but here has type 'DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>'. ... はずばり @types/react/index.d.ts を読み込んだ時、型が二重定義されていたため起こったエラーです。 型の二重定義 node_modules/@testing-library/react-hooks/node_modules/@types/react/index.d.ts を見ればわかりますが、 global 以下に定義されたものがコンフリクトしています。 これが起こったのは @types/react が複数存在しているためです。 $ yarn list @types/react yarn list v1.22.11 warning Filtering by arguments is deprecated. Please use the pattern option instead. ├─ @testing-library/react-hooks@7.0.2 │ └─ @types/react@17.0.27 └─ @types/react@17.0.15 プロジェクトからは @types/react@17.0.15 を直接利用していますが、 @testing-library/react-hooks@7.0.2 からは違うバージョンの @types/react@17.0.27 が間接的に import されています。 tsc を実行するときは、プロジェクトから直接依存している types は自動的に読み込まれますが、テストファイルの import { renderHook, act } from '@testing-library/react-hooks' のこれも @testing-library/react-hooks 内の @types/react@17.0.27 を読み込んでいます。 この2つの @types/react は同時に global に型を定義しているため、同じ名前の型が二重定義されてコンフリクトしています。 複数のnode_modules npm / yarn を利用するプロジェクトはこのように、node_modules 以下にさらに node_modules を保つことができます。 ./node_modules ./node_modules/make-dir/node_modules ./node_modules/npm/node_modules/resolve/test/shadowed_core/node_modules ./node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/_/node_modules 例えば a と b から複数のライブラリのから依存されているライブラリ c はもし同じバージョンに解決できたら、共通でルート以下の ./node_modules にその共用のライブラリを置かれますが、 同じバージョンに解決されなかった場合はそれぞれのディレクトリ以下 ./node_modules/a/node_modules/c ./node_modules/b/node_modules/c に置くことになります。 今回の @testing-library/react-hooks でいうと、実は "@types/react": ">=16.9.0", を満たせば問題ないので、プロジェクトの @types/react と同じバージョンに解決することができたはずです。 解決方法 ここに解決方法が書かれています。 かいつまんで言うと、 npm なら npm dedupe && npm install yarn なら npx yarn-deduplicate && yarn install1 を実行したら、2つバージョンが違った @types/react を一個に解決できて、今回のエラーを修正できました。 $ yarn list @types/react yarn list v1.22.11 warning Filtering by arguments is deprecated. Please use the pattern option instead. └─ @types/react@17.0.27 なぜか yarn dedupe だと効かなくて npx yarn-deduplicate にしないといけないようです ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【覚書】JSDoc + ts-check で as any as を実現する

//@ts-check で JS を書いていて、 as any as とか as unknown as とか (window as any).hoge とかみたいな書き方がしたくなったので調べた。 ふつうはこうなる //@ts-check document.createElement("div").innerText = 12345; // -> Type 'number' is not assignable to type 'string'. ts(2322) TypeScript ならこれでゴリ押せる document.createElement("div").innerText = 12345 as any as string; document.createElement("div").innerText = 12345 as unknown as string; でも JSDoc では当然このような書き方はできない・・・ ts-expect-error は代案にならない TypeScript では as any as を使用することで、以下のように変数の型をそのまま変更できるが const value = 12345 as any as string; type X = typeof value; // -> type X = string @ts-expect-error はエラーを無視するだけなので、変数の型は変化しない。 const value = 12345; //-> const value: 12345 //@ts-expect-error document.createElement("div").innerText = value; だから @ts-expect-error を as any as と同じように使うことはできない。 JSDoc の @type だとエラる //@ts-check /** @type {string} */ const value = 12345; // -> Type 'number' is not assignable to type 'string'. ts(2322) document.createElement("div").innerText = value; これが JSDoc の正解 といっても、あまりスマートじゃないし裏技じみてるが。 //@ts-check /** * @template T * @param {(type: T) => any} def * @param {unknown} from * @returns {T} */ const resolveTypeAs = (def, from) => //@ts-expect-error from; const value = resolveTypeAs( /** @param {string} _ */ (_) => _, 12345 ); // -> const value: string document.createElement("div").innerText = value; resolveTypeAs という、型を強制的に書き換える関数を作る。 第一引数で @param に無名関数の引数として T となる型を渡す形にした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

requestAnimationFrameで端末負荷を測定する

背景 最近ブラウザから端末負荷を測定したい場面に出くわしました。 弊社ではwebrtcを扱っていて、ライブ中の端末負荷の変化を測定したかったのですが、残念ながらブラウザAPIは用意されていません。 そこでrequestAnimationFrameapiを使用して画面の描画回数を測定することで、間接的に端末負荷の指標とすることにしました。 Window.requestAnimationFrame() requestAnimationFrameは本来はアニメーションを実装するために使用するAPIです。 ブラウザにアニメーションを行いたいことを知らせ、指定した関数を呼び出して次の再描画の前にアニメーションを更新することを要求します。 ブラウザが画面を描画するたびにコールされ、そのタイミングで任意の関数を実行したい場合に使用するのが一般的かと思います。 今回は、ブラウザが画面を描画するたびにコールされることを利用して、描画1回あたりに要した時間を計測しています。 1回あたりに要した時間が分かれば、画面描画のfpsも計算することができます。 つまり、端末負荷が上がって画面がカクカクする状態になればfpsも下がることを利用して、端末負荷の指標としましました。 正常時の基準とべきfpsは60ですが、注意も必要です。 このコールバックの回数は、たいてい毎秒 60 回ですが、一般的に多くのブラウザーでは W3C の勧告に従って、ディスプレイのリフレッシュレートに合わせて行われます。 特にゲーミング用のディスプレイなどはリフレッシュレートが高く設定されている場合もあり、必ずしも60を基準にできるとは限りません。 そもそももっと直接計測する方法はないのか? google meetはどうやって端末負荷を計測しているのか? googleの公式ではないので確かではないですが、chrome extensions apiを使用していると思われます。 試しにgoogle meetをchrome以外のブラウザでアクセスしてみると、cpu使用率のグラフが表示されなくなっています。 かなり有力そうです。 残念ながらextensionsのapiをfrontendのコードから呼び出すことはできないので断念しました。 googleは外部に公開していない内部apiを通じて取得しているのではなかろうかと推測されます。 実装 requestAnimationFrame()のコールバック関数の中で、再起的にrequestAnimationFrame()を呼び出しています。 60回ごとに平均を計算しています。(60はあくまで目安です) ポイントはInfinityです。理由が分かっていないのですが、時々lastCalledTimeとcurrentTimeが一致してしまうことがあり、fpsがInfinityになってしまいます。 これを避けるために、Infinityを除外する処理を加えています。 const measureFps = () => { let lastCalledTime: number | undefined; let counter = 0; let fpsArray: number[] = []; const calcFps = () => { const currentTime = Date.now(); if (lastCalledTime === undefined) { lastCalledTime = currentTime; } else { const delta = (currentTime - lastCalledTime) / 1000; lastCalledTime = currentTime; const fps = 1 / delta; if (counter >= 60) { const sum = fpsArray.reduce((a, b) => a + b); const average = sum / fpsArray.length; counter = 0; fpsArray = [fps]; console.log({ average }); } else if (fps !== Infinity) { fpsArray.push(fps); counter++; } } requestAnimationFrame(calcFps); }; requestAnimationFrame(calcFps); }; 参考: https://gist.github.com/WA9ACE/d51659371a345a9327bd それほど多くもないですが、Infinityを弾いた分は誤差になるので根本解決はしたいなと思ってます... それにしてもネイティブアプリはいいなぁ。読んで頂きありがとうございました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Javascript】画像URLを入力した瞬間に画像プレビューできるテキストエリアを作ってみた

はじめに 最近業務でjavascriptでUI実装をする機会が増え、その最中今後も使いそうな機能については備忘録をのこしておこうと思っての投稿です! 「ネット検索した画像URLをそのまま画像出力をする機能が必要だったためその機能実装メモです! こんなかんじ? 完成コード hoge.blade.php <div class="inline-flex space-x-4 ..."> <div class="text-sm text-gray-600 font-semibold mt-4 ml-12 mr-7 flex-2 ...">TSVバナー画像のベースURL</div> <div class="w-96 flex-2 ..."> <textarea oninput="serviceInfomationImagePreview()" name="tsv_banner_base_url" type="url" id="service-infomation-image-url" rows="1" class="mt-1.5 max-w-sm shadow-sm block w-full focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border border-gray-300 rounded-md" placeholder="画像URLを入力してください"></textarea> </div> <div class="flex-2 ..."> <div class="item-wapper6"> <div class="img-wapper6"> <span> <div id="image-position" class="-ml-16 mr-80 -mt-5"></div> </span> </div> </div> </div> </div><br> //Javascript <script type="text/javascript"> function serviceInfomationImagePreview() { var url = document.getElementById("service-infomation-image-url"); var urlValue = url.value ; var imagePosition = document.getElementById("image-position"); imagePosition.innerHTML = '<img src="' + urlValue + '" class="mt-7 ml-16 w-full h-full object-center object-cover lg:w-8 lg:h-8" />'; } </script> id="image-position”のタグの配下領域にこのimgタグをセット。 textareaタグに入力した値がこの このimgの値に動的にセットさせる。  この処理のトリガーをtextareaタグに入力したタイミングにするため oninput="serviceInfomationImagePreview()とします。  こうすることで入力した時点でurlの画像が即時描画という流れです! 以上!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む