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

ノードがHTML内に存在するかを調べる6つの方法

はじめに ノードがHTML内に存在するとは、開発ツール(DevTools)でHTMLを見たときノードが存在するということです。 HTML内に存在しないノードは、削除したノードやcreateElementで作成したばかりのノードです。これらはプログラミング内で変数として存在し得るがHTML内に存在しません。 ReactなどのSPAなページで動的に変化するノードをブックマークレットやユーザースクリプトから操作しようとしたときノードがHTML内に存在しないことが問題になることがあります。 6つの方法 1. getBoundingClientRectを使う方法 function exist_in_html_1(node) { var bcr = node.getBoundingClientRect(); return bcr.height > 0 || bcr.bottom !== 0; } 2. closestを使う方法 function exist_in_html_2(node) { return Boolean(node.closest('html')); } 3. containsを使う方法 function exist_in_html_3(node) { return document.contains(node) } 4. compareDocumentPositionを使う方法 function exist_in_html_4(node) { return !(document.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_DISCONNECTED); } 5. compareBoundaryPointsを使う方法 function exist_in_html_5(node) { var range = document.createRange(); range.selectNode(document.body); try { var sourceRange = document.createRange(); sourceRange.selectNode(node); range.compareBoundaryPoints(Range.START_TO_START, sourceRange); } catch (error) { return false; } return true; } 6. isConnectedを使う方法 function exist_in_html_6(node) { return node.isConnected; } 最後に 最適解は最後に紹介した方法です。最適解を知らなかったので5つも自作することになりました。MDN Web Docsの英語のドキュメントに日本語訳がないので最適解をご存じない方も多いのではないでしょうか。最適解と5つの自作方法を紹介しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.jsでpdf.jsを使うときにハマったこととその解決策

Nuxt.jsでpdf.jsを使おうとしたらいくつか困るポイントがあり、またドンピシャな解決策もなかなか見つからなかったので備忘録的に残しました。 体系だった使い方は、参考にあげたGoodpatchさんの記事がよくまとまっています。 しかし、この記事通りにやってもハマると思うので、合わせて読むと良いと思います! ちなみに、この段階で私が使っていたNuxtのバージョンは2.15.7です。 また、pdf.jsはnpmでインストールできるpdfjs-distを使っています。 pdfjs-distのバージョン この記事を書いている時点で最新のpdfjs-distのバージョンは2.9.359ですが、新しいバージョンでは新しいJavascriptの記法をつかっているということだと思われ、pdf.jsが呼ばれるタイミングでSyntax Errorが起きてしまいます。 WebpackやBabelに精通しているわけではないので深追いはせず、pdfjs-distのバージョンをもどすのが手っ取り早いと思い2.5.207に戻すことで対処しました。 CMap、worker pdf.jsでPDF内の文字を表示させるには、CMapが必要です。 また、こちらは絶対必要というわけではないですが、パフォーマンス的に処理をメインスレッドから逃すためにWeb Workerを使うためのスクリプトも使うことができると望ましいです。 ブラウザがこれらのファイルを読みにいけるようになっている必要があります。Nuxtを使っている場合は、staticディレクトリの中に配置してしまうのが手っ取り早いと思います。(参考で挙げたように、クロスオリジン関連には気をつけましょう) これらはnode_modules/pdfjs-dist/のなかに含まれているので、 cp -r node_modules/pdfjs-dist/cmaps/ static/cmaps/ cp node_modules/pdfjs-dist/build/pdf.worker.min.js static/ とstatic/の中にコピーしておいて、コンポーネントの中でそれぞれの位置を指定します。 <template> ... </template> <script> import * as PDFJS from 'pdfjs-dist' PDFJS.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.js' // ← workerスクリプトを配信するパスを指定 export default { ... async mounted() { // 別にmountedである必要はないです this.pdf = await PDFJS.getDocument({ data: <PDFファイルのデータ>, cMapUrl: '/cmaps/', // ←ここでCMap関連のファイルを配信するパスを指定 cMapPacked: true }).promise }, ... } </script> CMapについて詳しくは↓ レンダータスクのキャンセル 最初はこんな感じで、表示するページを表すthis.currentPageNum が変化するたびに同じcanvasを使いまわして新しいページを表示するようにしていました。 参考のGoodpatchさんの記事と同じ内容です。 以下はmethodsの中に書くメソッドです。 async renderPage() { const page = await this.pdf.getPage(this.currentPageNum) const canvas = this.$refs.previewZone const viewport = page.getViewport({ scale: 1 }) const context = canvas.getContext('2d') const renderingTask = page.render({ canvasContext: context, viewport: viewport, }) await this.renderingTask.promise }, だいたいこれで動くのですが、ページ切替を早く行うとpdfjs Cannot use the same canvas during multiple render() operationsというエラーが起きていました。 まだ前のrenderが終わっていないのに次のrenderが呼ばれるとダメなようです。 そこで、renderingTaskをrender処理が終わっていない間はコンポーネントのプロパティに持たせるようにして、次のrenderが来たときに前のが終わっていなかったらキャンセルするようにしました。 async renderPage() { if (this.renderingTask) { await this.renderingTask.cancel() this.renderingTask = null } const page = await this.pdf.getPage(this.currentPageNum) const canvas = this.$refs.previewZone const viewport = page.getViewport({ scale: 1 }) const context = canvas.getContext('2d') this.renderingTask = page.render({ canvasContext: context, viewport: viewport, }) this.renderingTask._internalRenderTask.callback = () => { this.renderingTask = null } await this.renderingTask.promise }, 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptで扱う連想配列のあれこれ(GET / CREATE系)

はじめに 連想配列であんなことやこんなことをしたい人向けのきじになります。 バックエンドで整形して返すのが一番ですが、javascriptで処理したいという方は参考までに。 (GET系)連想配列から特定の値を含む配列を取得 index.js var array = [ {id:1, name:"hoge",price:3000, status:1}, {id:2, name:"test",price:300, status:1}, {id:3, name:"hello",price:1500, status:1}, {id:4, name:"world",price:3000, status:2}, {id:5, name:"hoge",price:200, status:1}, {id:6, name:"test",price:400, status:1}, {id:7, name:"hoge",price:4000, status:2} ]; // 1つの検索条件で取得 var select_id = 3; const callback = item => item.id == select_id; // 検索条件 var result = array.filter(item => callback(item)); console.log(result); // {id:3, name:"hello",price:1500, status:1} // 複数の検索条件で取得 var select_name = "hoge"; var select_price = 1000; const callback = item => item.name == select_name && item.price >= select_price; var result = array.filter(item => callback(item)); console.log(result); // {id: 1, name: "hoge", price: 3000, status: 1} {id: 7, name: "hoge", price: 4000, status: 2} // 配列内のインデックスを取得(単体) var select_id = 4; const index = array.findIndex(item => item.id === 4); console.log(index); // 3 // 配列内のインデックスを取得(複数) var select_name = "hoge"; var result = []; var exam_array = array; for (var i = 0; i < exam_array.length; i++){ var index = exam_array.findIndex(item => item.name == select_name); // 複数条件は&&でつなげる if(index != -1){ console.log(index); result.push(index); exam_array.splice(index,1,""); } else { console.log("complete"); break; } } console.log(result); // [0, 4, 6] (CREATE系)配列に新しい値を追加 index.js var array = [ {id:1, name:"hoge",price:3000, status:1}, {id:2, name:"test",price:300, status:1}, {id:3, name:"hello",price:1500, status:1}, {id:4, name:"world",price:3000, status:2}, {id:5, name:"hoge",price:200, status:1}, {id:6, name:"test",price:400, status:1}, {id:7, name:"hoge",price:4000, status:2} ]; // 配列の最後に追加 var new_data = {'id':7, 'name':"new_data",'price':5000, 'status':1}; array.push(new_data); console.log(array); // (8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] // 配列の最後に追加(auto_inclement版) var last_id = array[array.length -1].id; var new_data = {'id':last_id + 1, 'name':"new_data",'price':5000, 'status':1}; array.push(new_data); console.log(array); // (8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] <- id:8のitem // 任意の場所に値を追加(ユニークじゃなくてもいい場合) var put_position = array[5 - 2]; // 前から5番目に追加したい場合,先に4番目の配列(直前の配列)を取得する var put_id = put_position.id; var new_data = {'id':put_id + 1, 'name':"new_data",'price':5000, 'status':1}; array.splice(put_id,0,new_data); console.log(array); // ...{id: 4, name: "world", price: 3000, status: 2} // {id: 5, name: "new_data", price: 5000, status: 1} // {id: 5, name: "hoge", price: 200, status: 1} // {id: 6, name: "test", price: 400, status: 1}... // 任意の場所に値を追加(後の値が自動調整版) var put_position = array[5 - 2]; // 前から5番目に追加したい場合,先に2番目の配列(直前の配列)を取得する var put_id = put_position.id; // 直前の配列のidを取得する var new_data = {'id':put_id, 'name':"new_data",'price':5000, 'status':1}; array.splice(put_id,0,new_data); var exam_array = array; const new_index = exam_array.findIndex(item => item.id === new_data.id); var before_array = exam_array.slice(0,new_index + 1); var adjust_array = exam_array.slice(new_index + 1, exam_array.length); for(var j = 0; j < adjust_array.length; j++){ adjust_array[j].id = adjust_array[j].id + 1; } var update_array = before_array.concat(adjust_array); array = update_array; console.log(array); // ...{id: 4, name: "world", price: 3000, status: 2} // {id: 5, name: "new_data", price: 5000, status: 1} // {id: 6, name: "hoge", price: 200, status: 1} // {id: 7, name: "test", price: 400, status: 1} // {id: 8, name: "hoge", price: 4000, status: 2}... 次回はDELETEとUPDATEも紹介します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WebAssembly で画像のリサイズ処理をやってみたら JavaScript + Canvas API より遅かった話

WebAssembly(WASM) は JavaScript より計算処理が速いという話題がしばしば聞かれます。 では、単純な数値計算ではないけれど、JavaScript で時間がかかる処理を移植するとどうなるのでしょうか1。本記事ではその一例として、巨大画像(4K~)を指定したピクセル数までリサイズする処理を書いてみました。 リポジトリ: https://github.com/yokra9/wasm-image-resizer JavaScript でリサイズしてみる まず、比較のため JavaScript でリサイズ処理を書いておきましょう。とはいえ、TypeScript からコンパイルして生成します。 const url = "./img/sample.jpg"; const resp = await fetch(url); const b = await resp.blob(); // JavaScript でリサイズ const blob = await resizeImageLegacy(b, 512, 512); // 画面上に処理結果を表示する describeImageFromBlob(blob, "sample"); /** * resize image(JS-native) * @param {Blob} file image * @param {number} width width * @param {number} height height * @returns {Promise<Blob>} image */ function resizeImageLegacy(file: Blob, width: number, height: number): Promise<Blob> { return new Promise((resolve, reject) => { const image = new Image(); console.log(`Original: ${file.size} Bytes`); const objectURL = URL.createObjectURL(file); image.onload = () => { const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); if (ctx == null) { reject('cannot get context.'); return; } ctx.drawImage( image, 0, 0, image.naturalWidth, image.naturalHeight, 0, 0, canvas.width, canvas.height ); canvas.toBlob((blob) => { if (blob == null) { reject('cannot convert canvas to blob.'); return; } console.log(`Resized: ${blob.size} Bytes`); resolve(blob); }, "image/jpeg", 0.8); }; image.src = objectURL; }); } 流れとしては以下の通りです: リサイズ後の大きさで Canvas を作成する Blob を URL.createObjectURL() で ObjectURL に変換する 作成した ObjectURL を image にセットする Canvas に画像を描画する HTMLCanvasElement.toBlob() で Blob にして返却する 実行結果(JS) 試行 サンプル1 (17446953 Bytes) サンプル2 (14725447 Bytes) サンプル3 ( 6985698 Bytes) 1 467.768 425.427 267.401 2 475.222 430.402 267.401 3 484.113 424.918 243.935 4 484.113 424.918 243.935 5 484.113 438.421 248.328 平均(ms) 479.066 428.817 254.200 16MB の画像を処理するのに 0.5 秒かかっています。メインスレッドがブロックされる秒数として微妙なところですね。console.time() を仕掛けてボトルネックになっている箇所を調べてみます: 実行時間 URL.createObjectURL(): 0.117919921875 ms load image from ObjectURL: 29.1630859375 ms Document.createElement(): 0.05419921875 ms HTMLCanvasElement.getContext(): 0.070068359375 ms CanvasRenderingContext2D.drawImage(): 222.634033203125 ms HTMLCanvasElement.toBlob(): 212.616943359375 ms ##### resizeImageLegacy #####: 465.1611328125 ms Canvas に対する操作に要する時間が大きいようです。WebWorker で別スレッドで動かそうにも、OffscreenCanvas は Chromium 系でしか動作しません。果たして WASM を利用すれば、さらなるパフォーマンスを引き出すことはできるのでしょうか?(タイトルでオチていますが…) WASM(Rust) でリサイズしてみる 流れとしては以下の通りです2: Blob を Uint8Array に変換する Uint8Array とリサイズ後の大きさを WASM に渡す Uint8Array をバッファ(Vec<u8>)にコピーする image::load_from_memory() で画像を読み込む image::resize_exact() でリサイズする image::write_to() で結果をバッファ(Vec<u8>)に書き込む Vec<u8> を Uint8Array に変換して JS に渡す Uint8Array を Blob に変換する Rust の世界で Blob を扱うのが若干面倒だったので Uint8Array 経由で画像を受け渡ししています。Rust 側では image クレートを利用して画像の読み込み・リサイズを行っています: extern crate console_error_panic_hook; extern crate serde; extern crate wasm_bindgen; use image::*; use js_sys::*; use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = console)] pub fn time(s: &str); #[wasm_bindgen(js_namespace = console)] pub fn timeEnd(s: &str); } #[wasm_bindgen] pub fn resize_image(arr: Uint8Array, width: usize, height: usize, fmt: &str) -> Uint8Array { console_error_panic_hook::set_once(); // Uint8Array から Vec にコピーする let buffer = arr.to_vec(); // バッファから画像を読み込む let img = load_from_memory(&buffer).expect("Error occurs at load image from buffer."); // 指定サイズに画像をリサイズする let resized = img.resize_exact(width as u32, height as u32, imageops::FilterType::Triangle); // バッファに画像を書き出す let result = save_to_buffer(resized, fmt); // バッファから Uint8Array を作成 Uint8Array::new(&unsafe { Uint8Array::view(&result) }.into()) } // バッファに画像を書き出す fn save_to_buffer(img: DynamicImage, fmt_str: &str) -> Vec<u8> { console_error_panic_hook::set_once(); let fmt = match fmt_str { "png" => ImageOutputFormat::Png, "gif" => ImageOutputFormat::Gif, "bmp" => ImageOutputFormat::Bmp, "jpg" => ImageOutputFormat::Jpeg(80), unsupport => ImageOutputFormat::Unsupported(String::from(unsupport)), }; // バッファを確保して画像を書き出す let mut result: Vec<u8> = Vec::new(); img.write_to(&mut result, fmt) .expect("Error occurs at save image from buffer."); result } 呼び出し側のコードは以下のような形です: import type * as WASM from "wasm-image-resizer" type Wasm = typeof WASM; // WASM の Shim を動的インポートする const js = import("wasm-image-resizer"); js.then(async wasm => { const url = "./img/sample.jpg"; const resp = await fetch(url); const b = await resp.blob(); // WASMでリサイズ const blob = await resizeImageWasm(b, 512, 512, "jpg", wasm); // 画面上に処理結果を表示する describeImageFromBlob(blob, "sample"); }); /** * resize image(WASM) * @param {Blob} file image * @param {number} width width * @param {number} height height * @param {string} format format * @param {Wasm} wasm WASM * @returns {Promise<Blob>} image */ async function resizeImageWasm(file: Blob, width: number, height: number, format: string, wasm: Wasm): Promise<Blob> { console.log(`Original: ${file.size} Bytes`); const arr = new Uint8Array(await file.arrayBuffer()); const result = wasm.resize_image(arr, width, height, format); const blob = new Blob([result]); console.log(`Resized: ${blob.size} Bytes`); return blob } 実行結果(WASM) 試行 サンプル1 (17446953 Bytes) サンプル2 (14725447 Bytes) サンプル3 ( 6985698 Bytes) 1 3906.262 4072.797 2876.642 2 3903.539 4072.797 2854.539 3 3992.005 4092.231 2918.119 4 3917.787 4073.364 2865.451 5 3917.787 4077.866 2877.314 平均(ms) 3927.476 4077.811 2878.413 JS の 10 倍遅いです3。こ、こんなはずでは…。今回も console.time() を仕掛けてボトルネックになっている箇所を調べてみます: 実行時間 Blob to Uint8Array: 9.274169921875 ms Uint8Array to Vec<u8>: 33.488037109375 ms image::load_from_memory(): 3275.39013671875 ms image::resize_exact(): 685.47216796875 ms save_to_buffer: 39.794921875 ms Vec<u8> to Uint8Array: 0.174072265625 ms Uint8Array to Blob: 0.496826171875 ms ##### WebAssembly #####: 4045.272705078125 ms メイン処理となる image::resize_exact() だけでも JS より遅いので論外ですが、ボトルネックは image::load_from_memory() だったようです。 まとめ 重い処理を WASM に持っていけばなんでも早くなるわけではない、という結果になりました。当然ではありますが、問題の箇所が本当に高速化できるのか、コードを書いて実測するべきですね。 残念ながら速度は出ませんでしたが、 WASM は WebWorker 上で動作できるので、メインスレッドをブロックしたくない場合(そして互換性を重視する場合)には有用でしょう。 また、WASM 側で利用するライブラリの選定やコードの修正により JavaScript より高速になる可能性もあるはずです。本稿を読んだ方で、WASM 版の処理をもっと速くできるよ! という方はコメント欄などで教えてくださると幸いです。 参考リンク Can't get image::load_from_memory() to work when compiled to WebAssembly WebAssemblyをちょろっと触って速度測ってみる。 WebAssemblyとJavaScriptで浮動小数点演算の速度を比較する WebAssemblyは本当に速いのか? [数値計算編] OffscreenCanvas ブラウザ上で利用可能な各種WebAPI(CanvasAPI等)は利用するものとします。従って、JavaScriptの計算処理によって画像のデコード・エンコード処理やリサイズ処理を行うという意味ではありません。 ↩ 他にもWebAssembly.Memory()でWASMインスタンスのメモリを掴み生ポインタを触る方法もあります。試してみましたが、複雑な割に速度は大きく変わりませんでしたので紹介しません。 ↩ wasm-pack build --releaseでリリースフラグを立てた状態でこの速度です。え…私のコード、遅すぎ?! ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WebAssembly で画像のリサイズ処理をやってみたら JavaScript より遅かった話

WebAssembly(WASM) は JavaScript より計算処理が速いという話題がしばしば聞かれます。 では、単純な数値計算ではないけれど、JavaScript で時間がかかる処理を移植するとどうなるのでしょうか。本記事ではその一例として、巨大画像(4K~)を指定したピクセル数までリサイズする処理を書いてみました。 リポジトリ: https://github.com/yokra9/wasm-image-resizer JavaScript でリサイズしてみる まず、比較のため JavaScript でリサイズ処理を書いておきましょう。とはいえそれだけでは面白くないので、TypeScript からコンパイルして生成します。 const url = "./img/sample.jpg"; const resp = await fetch(url); const b = await resp.blob(); // JavaScript でリサイズ const blob = await resizeImageLegacy(b, 512, 512); // 画面上に処理結果を表示する describeImageFromBlob(blob, "sample"); /** * resize image(JS-native) * @param {Blob} file image * @param {number} width width * @param {number} height height * @returns {Promise<Blob>} image */ function resizeImageLegacy(file: Blob, width: number, height: number): Promise<Blob> { return new Promise((resolve, reject) => { const image = new Image(); console.log(`Original: ${file.size} Bytes`); const objectURL = URL.createObjectURL(file); image.onload = () => { const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); if (ctx == null) { reject('cannot get context.'); return; } ctx.drawImage( image, 0, 0, image.naturalWidth, image.naturalHeight, 0, 0, canvas.width, canvas.height ); canvas.toBlob((blob) => { if (blob == null) { reject('cannot convert canvas to blob.'); return; } console.log(`Resized: ${blob.size} Bytes`); resolve(blob); }, "image/jpeg", 0.8); }; image.src = objectURL; }); } 流れとしては以下の通りです: リサイズ後の大きさで Canvas を作成する Blob を URL.createObjectURL() で ObjectURL に変換する 作成した ObjectURL を image にセットする Canvas に画像を描画する HTMLCanvasElement.toBlob() で Blob にして返却する 実行結果(JS) 試行 サンプル1 (17446953 Bytes) サンプル2 (14725447 Bytes) サンプル3 ( 6985698 Bytes) 1 467.768 425.427 267.401 2 475.222 430.402 267.401 3 484.113 424.918 243.935 4 484.113 424.918 243.935 5 484.113 438.421 248.328 平均(ms) 479.066 428.817 254.200 16MB の画像を処理するのに 0.5 秒かかっています。メインスレッドがブロックされる秒数として微妙なところですね。console.time() を仕掛けてボトルネックになっている箇所を調べてみます: 実行時間 URL.createObjectURL(): 0.117919921875 ms load image from ObjectURL: 29.1630859375 ms Document.createElement(): 0.05419921875 ms HTMLCanvasElement.getContext(): 0.070068359375 ms CanvasRenderingContext2D.drawImage(): 222.634033203125 ms HTMLCanvasElement.toBlob(): 212.616943359375 ms ##### resizeImageLegacy #####: 465.1611328125 ms Canvas に対する操作に要する時間が大きいようです。WebWorker で別スレッドで動かそうにも、OffscreenCanvas は Chromium 系でしか動作しません。果たして WASM を利用すれば、さらなるパフォーマンスを引き出すことはできるのでしょうか?(タイトルでオチていますが…) WASM(Rust) でリサイズしてみる 流れとしては以下の通りです1: Blob を Uint8Array に変換する Uint8Array とリサイズ後の大きさを WASM に渡す Uint8Array をバッファ(Vec<u8>)にコピーする image::load_from_memory() で画像を読み込む image::resize_exact() でリサイズする image::write_to() で結果をバッファ(Vec<u8>)に書き込む Vec<u8> を Uint8Array に変換して JS に渡す Uint8Array を Blob に変換する Rust の世界で Blob を扱うのが若干面倒だったので Uint8Array 経由で画像を受け渡ししています。Rust 側では image クレートを利用して画像の読み込み・リサイズを行っています: extern crate console_error_panic_hook; extern crate serde; extern crate wasm_bindgen; use image::*; use js_sys::*; use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = console)] pub fn time(s: &str); #[wasm_bindgen(js_namespace = console)] pub fn timeEnd(s: &str); } #[wasm_bindgen] pub fn resize_image(arr: Uint8Array, width: usize, height: usize, fmt: &str) -> Uint8Array { console_error_panic_hook::set_once(); // Uint8Array から Vec にコピーする let buffer = arr.to_vec(); // バッファから画像を読み込む let img = load_from_memory(&buffer).expect("Error occurs at load image from buffer."); // 指定サイズに画像をリサイズする let resized = img.resize_exact(width as u32, height as u32, imageops::FilterType::Triangle); // バッファに画像を書き出す let result = save_to_buffer(resized, fmt); // バッファから Uint8Array を作成 Uint8Array::new(&unsafe { Uint8Array::view(&result) }.into()) } // バッファに画像を書き出す fn save_to_buffer(img: DynamicImage, fmt_str: &str) -> Vec<u8> { console_error_panic_hook::set_once(); let fmt = match fmt_str { "png" => ImageOutputFormat::Png, "gif" => ImageOutputFormat::Gif, "bmp" => ImageOutputFormat::Bmp, "jpg" => ImageOutputFormat::Jpeg(80), unsupport => ImageOutputFormat::Unsupported(String::from(unsupport)), }; // バッファを確保して画像を書き出す let mut result: Vec<u8> = Vec::new(); img.write_to(&mut result, fmt) .expect("Error occurs at save image from buffer."); result } 呼び出し側のコードは以下のような形です: import type * as WASM from "wasm-image-resizer" type Wasm = typeof WASM; // WASM の Shim を動的インポートする const js = import("wasm-image-resizer"); js.then(async wasm => { const url = "./img/sample.jpg"; const resp = await fetch(url); const b = await resp.blob(); // WASMでリサイズ const blob = await resizeImageWasm(b, 512, 512, "jpg", wasm); // 画面上に処理結果を表示する describeImageFromBlob(blob, "sample"); }); /** * resize image(WASM) * @param {Blob} file image * @param {number} width width * @param {number} height height * @param {string} format format * @param {Wasm} wasm WASM * @returns {Promise<Blob>} image */ async function resizeImageWasm(file: Blob, width: number, height: number, format: string, wasm: Wasm): Promise<Blob> { console.log(`Original: ${file.size} Bytes`); const arr = new Uint8Array(await file.arrayBuffer()); const result = wasm.resize_image(arr, width, height, format); const blob = new Blob([result]); console.log(`Resized: ${blob.size} Bytes`); return blob } 実行結果(WASM) 試行 サンプル1 (17446953 Bytes) サンプル2 (14725447 Bytes) サンプル3 ( 6985698 Bytes) 1 3906.262 4072.797 2876.642 2 3903.539 4072.797 2854.539 3 3992.005 4092.231 2918.119 4 3917.787 4073.364 2865.451 5 3917.787 4077.866 2877.314 平均(ms) 3927.476 4077.811 2878.413 JS の 10 倍遅いです2。こ、こんなはずでは…。今回も console.time() を仕掛けてボトルネックになっている箇所を調べてみます: 実行時間 Blob to Uint8Array: 9.274169921875 ms Uint8Array to Vec<u8>: 33.488037109375 ms image::load_from_memory(): 3275.39013671875 ms image::resize_exact(): 685.47216796875 ms save_to_buffer: 39.794921875 ms Vec<u8> to Uint8Array: 0.174072265625 ms Uint8Array to Blob: 0.496826171875 ms ##### WebAssembly #####: 4045.272705078125 ms メイン処理となる image::resize_exact() だけでも JS より遅いので論外ですが、ボトルネックは image::load_from_memory() だったようです。 まとめ 重い処理を WASM に持っていけばなんでも早くなるわけではない、という結果になりました。当然ではありますが、問題の箇所が本当に高速化できるのか、コードを書いて実測するべきですね。 残念ながら速度は出ませんでしたが、 WASM は WebWorker 上で動作できるので、メインスレッドをブロックしたくない場合(そして互換性を重視する場合)には有用でしょう。 また、WASM 側で利用するライブラリの選定やコードの修正により JavaScript より高速になる可能性もあるはずです。本稿を読んだ方で、WASM 版の処理をもっと速くできるよ! という方はコメント欄などで教えてくださると幸いです。 参考リンク Can't get image::load_from_memory() to work when compiled to WebAssembly WebAssemblyをちょろっと触って速度測ってみる。 WebAssemblyとJavaScriptで浮動小数点演算の速度を比較する WebAssemblyは本当に速いのか? [数値計算編] OffscreenCanvas 他にもWebAssembly.Memory()でWASMインスタンスのメモリを掴み生ポインタを触る方法もあります。試してみましたが、複雑な割に速度は大きく変わりませんでしたので紹介しません。 ↩ wasm-pack build --releaseでリリースフラグを立てた状態でこの速度です。え…私のコード、遅すぎ?! ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ちょっとHTML, CSS, JavaScriptを試したいときに使えるChromeの拡張機能をご紹介!

はじめに こんにちは。東京のコロナ感染者すごい増えてますね... 重傷者が少ないことを祈るばかり さて、今回は知人に紹介した拡張機能なのですが、これいいぞ!ってあまり宣伝している人がいなかったので、宣伝しようと思います! 結論 これです↓ なにができるのか ちょっとHTML, CSS, JavaScriptを試せるエディタが出現します! コンソールも出るので、JavaScriptのデバッグもできます ライブラリも簡単に追加できます おわりに 今回は低糖質な記事ですが、とても便利な拡張機能のご紹介でした。 ぜひインストールしてみてはいかがでしょうか! それでは!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

多人数でのじゃんけん勝敗判定

Q&Aで3人でのじゃんけんの勝敗処理についての質問を見かけたので、自分なりに考えてみました。 多人数での勝敗判定は 出された手の種類が2種類の場合のみ勝敗が決定、それ以外はあいこ 勝敗が決定する場合はどの手が勝ちか調べ、同じ手を出していたプレイヤーは勝ち、それ以外は負け こんな感じで判定するのが簡単そうかな、とJavaScriptで作成してみたのが以下。 (Q&AはJavaでの質問でしたが、私はJavaは分かりませんので) index.html <!DOCTYPE html> <html lang='ja'> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width,initial-scale=1'> <title>多人数じゃんけんsample</title> <style> #computersArea { display: inline-flex; flex-wrap: wrap; } #computersArea div{ width: 150px; margin: 2px; padding: 4px; background-color: #ddd; } .main { display: none; } #player0 { margin-top: 8px; } </style> </head> <body> <div class='npSelect'> <p>自分を除いた相手プレイヤー(コンピューター)数</p> <select id='np'> <option value='1'>1</option> <option value='2' selected>2</option> <option value='3'>3</option> <option value='4'>4</option> <option value='5'>5</option> <option value='6'>6</option> <option value='7'>7</option> <option value='8'>8</option> <option value='9'>9</option> </select>人 <button id='start'>開始</button> </div> <div class='main' > <div id='computersArea'> </div> <div> <div id='player0'> プレイヤー<br> &emsp;出した手: <span class='hand'></span><br> &emsp;結果: <span class='result'></span><br> &emsp;勝数: <span class='winCount'>0</span><br> <button value='0'>グー</button> <button value='1'>チョキ</button> <button value='2'>パー</button> </div> </div> </div> <script> 'use strict'; const obj = { players: [], }; // 相手人数選択 ~ 初期設定 document.getElementById('start').addEventListener('click', () => { const np = + document.getElementById('np')?.value ?? 1; obj.players = []; for(let i = 0; i <= np; i++) { obj.players[i] = { winCount: 0, }; } document.querySelector('.npSelect').style.display = 'none'; document.querySelector('.main').style.display = 'block'; const comArea = document.getElementById('computersArea'); comArea.innerHTML = ''; for(let i = 1; i <= np; i++) { document.getElementById('computersArea').insertAdjacentHTML('beforeend', ` <div id='player${i}'> コンピューター ${i}<br> &emsp;出した手: <span class='hand'></span><br> &emsp;結果: <span class='result'></span><br> &emsp;勝数: <span class='winCount'>0</span><br> </div> `); } for(const button of document.querySelectorAll('#player0 button')) { button.addEventListener('click', () => { judge(+button.value); }) } }); // 勝敗判定 const judge = n => { const handsCheck = [0, 0, 0]; // 自分の出した手 obj.players[0].hand = n; handsCheck[n]++; // コンピューターの手をランダムに決定 for(let i = 1; i < obj.players.length; i++) { const hand = Math.floor(Math.random() * 3); obj.players[i].hand = hand; handsCheck[hand]++; } // 全プレイヤーで出された手の種類数 const numberOfHandTypes = handsCheck.filter(v => v !== 0).length; // 2種類の場合は勝敗決定 if(numberOfHandTypes === 2) { const hands = []; for(const n in handsCheck) { if(handsCheck[n] > 0) hands.push(+n); } // '勝ち'の手を決定 const winHand = (3 + hands[1] - hands[0]) % 3 === 1 ? hands[0] : hands[1]; // 勝敗結果を各プレイヤーに反映 for(let i = 0; i < obj.players.length; i++) { const p = obj.players[i]; const flag = p.hand === winHand; p.result = flag ? 1 : 2; if(flag) p.winCount++; } } // 2種類以外の場合は引き分け else { for(let i = 0; i < obj.players.length; i++) { obj.players[i].result = 0; } } // 表示反映 for(let i = 0; i < obj.players.length; i++) { const d = document.getElementById('player' + i); d.querySelector('.hand').textContent = ['グー', 'チョキ', 'パー'][obj.players[i].hand]; d.querySelector('.result').textContent = ['あいこ', '勝ち', '負け'][obj.players[i].result]; d.querySelector('.result').style.color = ['black', 'blue', 'red'][obj.players[i].result]; d.querySelector('.winCount').textContent = obj.players[i].winCount; } }; </script> </body> </html> 実動デモ 想定通り動いているようですが、多人数での勝敗判定を考えるのが目的でしたので、負けたプレイヤーが脱落する勝ち抜きや、最初に何勝したプレイヤーが優勝、といった処理はありません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptで自分のQiita記事一覧を得る(3)

前回の記事のアップデートです。 出現回数が多いタグの順にソートするようにしました。 動作デモ 前回同様ローカル環境でも動作します。 結果: 名前順にソートしたい場合は sortButtons(true); をfalseにします。 [myindex.html] <!DOCTYPE html> <html><head><title>Qiita index</title> <style type="text/css"> .cOnButton { color: #111; background-color: #fc8; } .cOffButton { color: #888; } button{ margin: 1px; height: 30px; font-size: 1rem;} </style> <script> var USER_ID='ELIXIR'; // 自分のuser_idに変えてください const PAR_PAGE=100; // Qiita API の上限 var resArr = []; var existTagArr = []; var TmGetUrlVarByKeys = function(){ var vars = {}; var param = location.search.substring(1).split('&'); for(var i = 0; i < param.length; i++) { var keySearch = param[i].search(/=/); var key = ''; if(keySearch != -1) key = param[i].slice(0, keySearch); var val = param[i].slice(param[i].indexOf('=', 0) + 1); if(key != '') vars[key] = decodeURI(val); } return vars; } function onLoad(){ var vars = TmGetUrlVarByKeys(); if(!(typeof vars["uuid"] === "undefined")) { document.getElementById('iUuid').value = vars["uuid"]; }; } function onSearch(){ resArr = []; USER_ID = document.getElementById('iUuid').value; document.title=USER_ID; getUser(); } function apiCall(_api, _callback, _userData=0){ var req = new XMLHttpRequest(); req.responseType="json"; req._userData=_userData; req.addEventListener("load", _callback); req.open('GET', 'https://qiita.com/api/v2/'+_api, true); req.send(); } function getUser(){ apiCall('users/'+USER_ID,function(){ if(this.response["items_count"]==undefined) document.getElementById('iResult').innerHTML = 'エラーまたはapi使用上限(60回/時間)に達しました。'; else getData(Math.floor((this.response["items_count"]+(PAR_PAGE-1))/PAR_PAGE)); }); } function getData(_page=1){ apiCall('users/'+USER_ID+'/items?per_page='+PAR_PAGE+'&page='+_page,function(){ resArr=resArr.concat(this.response); if(this._userData>1) getData(this._userData-1); else dispData(resArr); },_page); } function sortButtons(_sortByCount=false){ var buttonsEle = document.getElementById('iButtons'); var btnArr = Array.from(document.getElementById('iButtons').children); btnArr.sort( function(v0,v1){ if(_sortByCount){ return (v0.existCnt > v1.existCnt) ? -1:1; }else{ return (v0.innerText < v1.innerText) ? -1:1; } } ); buttonsEle.innerHTML=""; for(var i=0;i<btnArr.length;++i){ buttonsEle.appendChild(btnArr[i]); } } function createButton(_name,_tag){ existTagArr.push(_name); var btn = document.createElement('button'); btn.innerHTML=_name; btn.className=(_name==_tag)?'cOnButton':'cOffButton'; btn.onclick=function(){dispData(resArr,_name);} btn.id = 'i_btn'+_name; btn.existCnt=1; return btn; } function createButtons(_resArr,_tag){ var buttonsEle = document.getElementById('iButtons'); existTagArr=[]; buttonsEle.innerHTML = ''; for(var i=0;i<_resArr.length;++i){ var tagArr=_resArr[i]['tags']; for(var j=0;j<tagArr.length;++j){ if(!existTagArr.includes(tagArr[j]['name'])){ buttonsEle.appendChild(createButton(tagArr[j]['name'],_tag)); }else{ document.getElementById('i_btn'+tagArr[j]['name']).existCnt+=1; } } } sortButtons(true); //false if sort by name } function articleContainsTag(_article,_tag){ for(var i=0;i<_article['tags'].length;++i) if(_article['tags'][i]['name']==_tag) return true; return false; } function dispData(_resArr,_tag=""){ createButtons(_resArr,_tag); var str=""; for(var i=0;i<_resArr.length;++i){ if((_tag=="")||(articleContainsTag(_resArr[i],_tag))) str += '<a href="'+_resArr[i]['url']+'" target="_blank">'+ _resArr[i]['title']+'</a><br/>'; } document.getElementById('iResult').innerHTML = str; } </script> </head> <body onload="onLoad()"> user_id@<input id="iUuid" type="text"> <input type="button" value="search" onclick="onSearch()"><div id="iButtons"></div><div id="iResult"></div></body> <br/> <!-- ad --> <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> <!-- WebCardGame00 --> <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-3504902108000381" data-ad-slot="6987101050" data-ad-format="auto" data-full-width-responsive="true"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> <!-- ad --> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

特定ユーザーのQiita記事一覧を得る(3)

前回の記事のアップデートです。 出現回数が多いタグの順にソートするようにしました。 動作デモ 前回同様ローカル環境でも動作します。 結果: 名前順にソートしたい場合は sortButtons(true); をfalseにします。 [myindex.html] <!DOCTYPE html> <html><head><title>Qiita index</title> <style type="text/css"> .cOnButton { color: #111; background-color: #fc8; } .cOffButton { color: #888; } button{ margin: 1px; height: 30px; font-size: 1rem;} </style> <script> var USER_ID='ELIXIR'; // 自分のuser_idに変えてください const PAR_PAGE=100; // Qiita API の上限 var resArr = []; var existTagArr = []; var TmGetUrlVarByKeys = function(){ var vars = {}; var param = location.search.substring(1).split('&'); for(var i = 0; i < param.length; i++) { var keySearch = param[i].search(/=/); var key = ''; if(keySearch != -1) key = param[i].slice(0, keySearch); var val = param[i].slice(param[i].indexOf('=', 0) + 1); if(key != '') vars[key] = decodeURI(val); } return vars; } function onLoad(){ var vars = TmGetUrlVarByKeys(); if(!(typeof vars["uuid"] === "undefined")) { document.getElementById('iUuid').value = vars["uuid"]; }; } function onSearch(){ resArr = []; USER_ID = document.getElementById('iUuid').value; document.title=USER_ID; getUser(); } function apiCall(_api, _callback, _userData=0){ var req = new XMLHttpRequest(); req.responseType="json"; req._userData=_userData; req.addEventListener("load", _callback); req.open('GET', 'https://qiita.com/api/v2/'+_api, true); req.send(); } function getUser(){ apiCall('users/'+USER_ID,function(){ if(this.response["items_count"]==undefined) document.getElementById('iResult').innerHTML = 'エラーまたはapi使用上限(60回/時間)に達しました。'; else getData(Math.floor((this.response["items_count"]+(PAR_PAGE-1))/PAR_PAGE)); }); } function getData(_page=1){ apiCall('users/'+USER_ID+'/items?per_page='+PAR_PAGE+'&page='+_page,function(){ resArr=resArr.concat(this.response); if(this._userData>1) getData(this._userData-1); else dispData(resArr); },_page); } function sortButtons(_sortByCount=false){ var buttonsEle = document.getElementById('iButtons'); var btnArr = Array.from(document.getElementById('iButtons').children); btnArr.sort( function(v0,v1){ if(_sortByCount){ return (v0.existCnt > v1.existCnt) ? -1:1; }else{ return (v0.innerText < v1.innerText) ? -1:1; } } ); buttonsEle.innerHTML=""; for(var i=0;i<btnArr.length;++i){ buttonsEle.appendChild(btnArr[i]); } } function createButton(_name,_tag){ existTagArr.push(_name); var btn = document.createElement('button'); btn.innerHTML=_name; btn.className=(_name==_tag)?'cOnButton':'cOffButton'; btn.onclick=function(){dispData(resArr,_name);} btn.id = 'i_btn'+_name; btn.existCnt=1; return btn; } function createButtons(_resArr,_tag){ var buttonsEle = document.getElementById('iButtons'); existTagArr=[]; buttonsEle.innerHTML = ''; for(var i=0;i<_resArr.length;++i){ var tagArr=_resArr[i]['tags']; for(var j=0;j<tagArr.length;++j){ if(!existTagArr.includes(tagArr[j]['name'])){ buttonsEle.appendChild(createButton(tagArr[j]['name'],_tag)); }else{ document.getElementById('i_btn'+tagArr[j]['name']).existCnt+=1; } } } sortButtons(true); //false if sort by name } function articleContainsTag(_article,_tag){ for(var i=0;i<_article['tags'].length;++i) if(_article['tags'][i]['name']==_tag) return true; return false; } function dispData(_resArr,_tag=""){ createButtons(_resArr,_tag); var str=""; for(var i=0;i<_resArr.length;++i){ if((_tag=="")||(articleContainsTag(_resArr[i],_tag))) str += '<a href="'+_resArr[i]['url']+'" target="_blank">'+ _resArr[i]['title']+'</a><br/>'; } document.getElementById('iResult').innerHTML = str; } </script> </head> <body onload="onLoad()"> user_id@<input id="iUuid" type="text"> <input type="button" value="search" onclick="onSearch()"><div id="iButtons"></div><div id="iResult"></div></body> <br/> <!-- ad --> <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> <!-- WebCardGame00 --> <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-3504902108000381" data-ad-slot="6987101050" data-ad-format="auto" data-full-width-responsive="true"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> <!-- ad --> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptを基本からまとめてみた【7】【機能】【随時更新】

はじめに 学習するに至った経緯 2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。 入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、 卒業後、フロントエンドのエンジニアを目指す事に。 Javascriptの学習した事を言語化し、認識の深化による備忘録として記載。 appendChild()の使い方 appendChild()は、特定の親ノードの子ノードリストの末尾にノードを追加する。 親要素.appendChild(追加したい要素) という形で使われ、親要素の末尾に要素が追加される。 例:ulタグにliタグを追加する場合、下記のようにulタグの末尾に追加される。 <ul> <li>①</li> <li>②</li> </ul>    ⬇️ <ul> <li>①</li> <li>②</li> <li>③追加</li> </ul> appendChild()を使ったデモ 親要素がul、追加したい要素をliというデモで、フォームに入力したテキストが、liとして追加される。 onchangeで入力欄を変更するとイベントが発火。 createElementで生成されたli要素に、入力テキストがセットされ、appendChildによってulタグ末尾に追加される。 <span>ここに追加されます↓</span> <ul id="frame"> </ul> <input type="text" id="input_form" onchange="addList()"> <input type="button" value="削除" onclick="deleteList()"> li { margin-left: 10px; } input { display: block; margin-top: 5px; } #frame { background-color: #A9E2F3; padding: 10px; max-width: 300px; } function addList() { var input_area = document.getElementById('input_form'); var input_value = input_area.value; var li_text = document.createElement('li'); li_text.innerHTML = input_value; var parent = document.getElementById('frame'); parent.appendChild(li_text); input_area.value = ''; } function deleteList() { var parent = document.getElementById('frame'); parent.innerHTML = ''; } 参考サイト appendChild()の使い方とデモ【JavaScript】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactNativeでカスタムフォントを使用する

備忘録 関数コンポーネントでカスタムフォントを使っている記事が少なかったのでメモ スマホで見るとこんな感じ 環境 使用プラットフォーム:ReactNative + Expo 使用フォント:Yomogi(assetsフォルダ内に入れています) カスタムフォントの導入 App.js import { StatusBar } from 'expo-status-bar'; import React from 'react'; import { StyleSheet, Text, View } from 'react-native'; import { createDrawerNavigator } from '@react-navigation/drawer'; import { NavigationContainer } from '@react-navigation/native'; import { HomeScreen } from './src/Home'; export default function App() { const Drawer = createDrawerNavigator(); return ( <NavigationContainer> <StatusBar hidden /> <Drawer.Navigator initialRouteName="Home"> <Drawer.Screen name="Home" component={HomeScreen} /> </Drawer.Navigator> </NavigationContainer> ); } DrawerNavigatorを使って初期状態にHomeScreenを表示しています。 HomeScreenの特定箇所のテキストにカスタムフォントを適用したいので、Home.jsで作業していきます。 home.js import React, { useEffect, useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import * as Font from 'expo-font'; export const HomeScreen = () => { const navigation = useNavigation(); const [fontLoaded, setFontLoaded] = useState(false); useEffect(() => { const subscription = navigation.addListener('focus', async () => { await Font.loadAsync({ 'Yomogi': require('../assets/Yomogi-Regular.ttf') }).then(() => { setFontLoaded(true); }) }); return subscription; }, []); return ( <View style={styles.container}> {fontLoaded && <Text style={styles.hello}>こんにちは</Text> } </View> ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: 'pink', alignItems: 'center', justifyContent: 'center', }, hello: { fontSize: 70, fontFamily: 'Yomogi', } }); カスタムフォントをロードするためにはexpo-fontのloadAsyncを使います。今回の場合は、HomeScreenにfocusした際にロードを行うようにしました。また、フォントがしっかりロードされてからテキストが描画される必要があるため、fontLoadedがtrueの時にテキストが表示されるようにしています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

YellowfinのRESTAPIで保存済みのRefreshTokenを使用してLoginTokenを発行する(HTMLでJavaScript使用)

ログイントークンを取得するまでの流れ 既存のアプリにYellowfinを埋め込んで利用する際は、リフレッシュトークンをアプリ内で保持し、それを使用して短時間有効なアクセストークンを発行する流れが多いと思います。今回はリフレッシュトークン取得→アクセストークン取得→シングルサインオンでダッシュボード表示といった流れをコードで解説していきます。 最後には埋め込みでダッシュボード表示ではなくログイン後のページへの遷移方法についても記載しています。 早速ですがサンプルコードです ここではリフレッシュトークンを取得するところからコードで記載しているので、POSTMANなどであらかじめリフレッシュトークンを取得している場合にはvar securityTokenに取得済みのトークンを代入してxhr2を宣言しているところから参考にしてください。 ポイントとしては、レスポンスの値のsecurityTokenの意味が3つのAPIでそれぞれ違うため混乱しがちなところです。 embed.html <html> <body> <meta charset="utf-8" /> <p><font size="3" color = "blue">ダッシュボードの埋め込み</font></p> <script src="https://code.jquery.com/jquery-3.5.1.js"></script> <div id="dashboard"></div> <script> var authUserId = 'TEST002@yellowfin.bi'; var authUserPass = 'dammy'; var adminId = 'admin@yellowfin.com.au'; var adminPassword = 'test'; var body = { "userName": adminId, "password": adminPassword }; var body2 = { "signOnUser":{ "userName": authUserId, "password": authUserPass }, "noPassword": true }; //encode to JSON var json_text = JSON.stringify(body); var json_text2 = JSON.stringify(body2); var xhr = new XMLHttpRequest(); xhr.open('POST', 'http://localhost:8940/api/refresh-tokens'); xhr.responseType = 'json'; xhr.setRequestHeader('Authorization', 'YELLOWFIN ts=' + new Date().getTime() + ', nonce=123'); xhr.setRequestHeader('Accept', 'application/vnd.yellowfin.api-v1.2+json'); xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); xhr.onload = () => { var securityToken = xhr.response.securityToken; var xhr2 = new XMLHttpRequest(); xhr2.open('POST', 'http://localhost:8940/api/access-tokens'); xhr2.responseType = 'json'; xhr2.setRequestHeader('Authorization', 'YELLOWFIN ts=' + new Date().getTime() + ', nonce=123, token=' + securityToken); xhr2.setRequestHeader('Accept', 'application/vnd.yellowfin.api-v1.2+json'); xhr2.onload = () => { var accessToken = xhr2.response.securityToken; var xhr3 = new XMLHttpRequest(); xhr3.open('POST', 'http://localhost:8940/api/login-tokens'); xhr3.responseType = 'json'; xhr3.setRequestHeader('Authorization', 'YELLOWFIN ts=' + new Date().getTime() + ', nonce=123, token=' + accessToken); xhr3.setRequestHeader('Accept', 'application/vnd.yellowfin.api-v1.2+json'); xhr3.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); xhr3.onload = () => { var loginToken = xhr3.response.securityToken; let url = 'http://localhost:8940/JsAPI/v3?token=' + loginToken; let tp = 'text/javascript'; let sc = document.createElement('script'); sc.src = url; sc.type = tp; document.body.appendChild(sc); setTimeout(function () { yellowfin.showLoginPrompt = false; yellowfin.init().then(() => { yellowfin.loadDashboard({ dashboardUUID: '571493b1-1945-4a49-abe2-86df376f51e9', element: document.querySelector('div#dashboard') }); }); }, 1000); }; xhr3.send(json_text2); }; xhr2.send(); }; xhr.send(json_text); </script> </body> </html> SSOでログインしてそのままログイン後のページに遷移させる場合 ログイントークンを取得した後にonloadの中でURLを指定してJavaScriptで遷移させるだけです。 ここではxhr3.onloadの中身のみ変更します。 href.js xhr3.onload = () => { var loginToken = xhr.response.securityToken; let url = 'http://localhost:8940/logon.i4?LoginWebserviceId=' + loginToken; window.location.href = url; }; xhr3.send(json_text2);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript HTML input dateのデフォルトを当日に設定+今日以前選択不可設定

Every Qiita #22 のんびり独学初学者投稿 22日目 今回は・・・ HTMLのinput dateで初期値を当日に設定+今日以前を選択できないように設定した時の備忘録です。 処理フローを考える 当日の日付を取得 input dateの形式に変換(形式:YYYY-MM-DD) valueをjavascriptで出力 minをjavascriptで出力 上記のフローを1つずつ分解して実装していきましょう! 1.当日の日付を取得 javascriptでDateオブジェクトを使うと当日の日時を取得できます。 date.js const today = new Date(); 出力結果 Tue Jul 27 2021 13:45:38 GMT+0900 (日本標準時) 2.input dateの形式に変換 input dateのvalueはYYYY-MM-DDで出力されるので、javascriptで成形していきます。dateオブジェクトには年・月・日をそれぞれ取得できるメソッドがありますので順番に取得しreplace関数を使って整えていこうと思います。 replace関数は、第一引数に変換対象の文字列、第二引数に変換する文字列を配置します。 date.js function dateFormat(today, format){ format = format.replace("YYYY", date.getFullYear()); format = format.replace("MM", ("0"+(date.getMonth() + 1)).slice(-2)); format = format.replace("DD", ("0"+ date.getDate()).slice(-2)); return format; } 独自関数の第二引数にYYYY-MM-DDを配置します。(formatで受け取っています) getFullYearで年を4桁で取得し、YYYYを変換します。 getMonth + 1で月を取得します。 月は0〜11で出力されますので、+1をすれば正確な月を取得できます。 2桁で取得したいので0を前に付け、末尾から2文字を取得するようにします。 getDateで日付を取得します。上記と同様に2桁で取得できるようにします。 出力結果 2021-07-27 value・minをjavascriptで出力 最後に成形したデータを初期値と最小値に設定すれば完了です。 date.js const data = dateFormat(new Date(),'YYYY-MM-DD'); const field = document.getElementById(dateに付与した任意ID); field.value = data; field.setAttribute("min", data); 関数を呼び出し、取得した要素のvalueとminを設定しています。 これで当日が初期値として認識され、以前の日付を選択できないようにできます。 date.js const today = new Date(); function dateFormat(today, format){ format = format.replace("YYYY", date.getFullYear()); format = format.replace("MM", ("0"+(date.getMonth() + 1)).slice(-2)); format = format.replace("DD", ("0"+ date.getDate()).slice(-2)); return format; } const data = dateFormat(today,'YYYY-MM-DD'); const field = document.getElementById(dateに付与した任意ID); field.value = data; field.setAttribute("min", data);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptの||と&&について理解する

||(パイプライン二つ)の意味 左側がfalseなら右側を返す const num = null; const fee = num || "金額未設定です"; console.log(fee); // 金額未設定です javascriptのfalsyな値の一覧 false 0 -0 "" null undefined Nan &&(アンパサンド二つ)の意味 左側がtrueなら右側を返す const num2 = 100; const fee2 = num2 && "何か設定されました"; console.log(fee2); //"何か設定されました"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mapやfilterを使用した配列の処理

map 繰り返し処理を行うメソッド 配列を受け取って、新しい配列を作る const nameArray = ['ゆう', '吉田', '佐藤'] const nameArray2 = nameArray.map((name) => { return name; }) console.log(nameArray2); // ['ゆう', '吉田', '佐藤'] 配列を順番に表示する const nameArray = ['ゆう', '吉田', '佐藤']; //一行で済む場合はブラケット{}を省略できる。 nameArray.map((name) => console.log(name)); // ゆう, 吉田, 佐藤 map関数に順番の概念を持たせる 第二引数を指定することで、インデックス番号を代入できる nameArray.map((name, index) => console.log(`${index + 1}番目は、${name}です`)); /*1番目は、ゆうです *2番目は、吉田です *3番目は、佐藤です*/ map関数内に条件分岐を実装する const newNameArray = nameArray.map((name) => { if (name === "ゆう") { return name } else { return `${name}さん` } }) console.log(newNameArray); /*ゆう *吉田さん *佐藤さん*/ filter 条件付きの繰り返し処理を行い新しい配列を作るメソッド //奇数の値のみを取り出して新しい配列を作る const newNumArray = numArray.filter((num) => { return num % 2 === 1; }) console.log(newNumArray); //[1, 3, 5]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptのスプレッド構文

スプレッド構文とは 配列を展開したり、まとめたりすることができる構文 配列を展開する const array = [1, 2] console.log(array); // [1, 2] //スプレッド構文 console.log(...array); // 1, 2 //↑は↓と同じことを行っている console.log(array[0], array); // 1, 2 分割代入の際に配列を部分的にまとめる const array2 = [1, 2, 3, 4, 5]; const [num1, num2, ...array3] = array2 console.log(num1); // 1 console.log(num2); // 2 console.log(...array3); // [3, 4, 5] 配列のコピーや結合 const array4 = [10, 20]; const array5 = [30, 40]; const array6 = [...array4]; console.log(array6); //[10, 20] array6[0] = 100; //配列の中身を変更 console.log(array6); //[100, 20] 変わってる! console.log(array4); //[10, 20] 参照先の配列は変わっていない! const array7 = [...array4, ...array5]; console.log(array7); //[10, 20, 30, 40] スプレットじゃなくてそのまま代入すればいいじゃん! const array8 = array4; console.log(array8); //[10, 20] 確かに代入されてる!けど... const array4 = [10, 20]; const array8 = array4; array8[0] = 100; console.log(array8); //[100, 20] 変わってる! console.log(array4); //[100, 20] 参照先の中身まで変わってる! 中身を変更すると、参照先の配列の中身も変わっている!!(参照わたし) だから、配列のコピーや結合を行う際はスプレッド構文を使用する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者のための静的サイト・ジェネレーター・ガイド②

初心者のための静的サイト・ジェネレーター・ガイド①の続きです。①を読んでいない方はそちらから読むことをおすすめします 静的サイトの世界は、HTMLやCSS、時にはJSを超えて発展していますが、静的サイトジェネレーターの進歩がこのことに大きく貢献しています。 著者:Thom Krupa@thomkrupa 2020年11月18日 原文:https://bejamas.io/blog/static-site-generators/ 3. Gatsby 最終更新日:2020年10月27日 Gatsbyは、一番人気のあるJavascriptフレームワークのReact.jsの上に構築された、最も人気のある静的サイトジェネレーターです。カイル・マシューズ氏は、後にサム・バグワット氏と一緒に「ウェブサイトを楽しく作る」というミッションを掲げ、Gatsby, Inc.を設立しました。 3-1 Gatsbyストーリー GatsbyJSは、2015年にシンプルな静的サイトジェネレーターとして誕生し、この5年間で、市場で最も人気のあるソリューションとなりました。Gatsby のおかげでJamstackは今のように成長しました。 2019年、Gatsby, Inc.はシリーズAで1500万ドルの資金を確保し、最初の商用製品であるGatsby Cloud、高速なインクリメンタルビルドに特化したCIプラットフォームを構築しました。これについては、後で少しお話します。まず、SSGの概要から説明します。 3-2 コンテンツメッシュ Gatsbyで最も意見が多いのは、データの取得です。Gatsbyは、内部でGraphQL APIを作成しています。これは、コンテンツソースとフロントエンドテンプレートの間の一種のミドルウェアです。 Gatsbyは、人気のあるあらゆるヘッドレスCMSと接続することができます。 Gatsbyソースのプラグインが豊富に揃っているため、簡単に接続することができます。1つのGraphQLクエリでソースをメッシュ化できます。すべてが1つの大きなグラフになります。 メッシュコンセプトを楽しむためには、GraphQLとGatsby APIを学ぶ必要があります。シンプルな静的Webサイトでは無理があるかもしれません。GraphQLでの作業は痛みを伴いますし、エラーが出ることもあります。デバッグは大変です。好きな人もいれば、そうでない人もいる。 私に言わせれば、GraphQL APIは、私たちのような代理店で製品を作る方法を統一し、標準化するのに適しています。どのCMSを選んでも、ページの生成やデータの取得方法は同じです。 例えば、Shopifyから商品を、Contentfulからコンテンツを取得する方法を見てみましょう。 どこからでもデータを取得できます。ヘッドレスCMS、ローカルファイルシステム、外部のGraphQLやREST API、Google Spreadsheet、Airtableなどなど。独自のソースプラグインを作成して、バックエンドとのカスタム統合を行うことができます。 GraphQLクエリはビルド時にのみ実行されます。CMSで何かを変更した場合、プロジェクトを再構築する必要があります。他のReactアプリのように、クライアントでデータを取得することもできます。例えば、SWRを使ったり、簡単な取得でも、プリレンダーにはなりません。 3-3 ファイル構成 ファイル構造の例を以下に示します。 最初の4つのファイルは、Gatsby APIにアクセスしています。つまり、Gatsbyがボンネットの中で行っていることを変更できるのです。プラグインを追加したり、APIからページを生成したり、SSRを調整したり、ブラウザでのinit renderで起こっていることを管理したり。公式ドキュメントでは、APIの全機能が紹介されています。 3-4 エコシステム Gatsby開発者のコミュニティは、長年にわたって何千ものテーマ、プラグイン、スターターを作成してきました。GatsbyはJamstackの中でも最大のエコシステムを持っています。前回、プラグインライブラリを確認したところ、2400以上のパッケージがすぐにインストールできる状態になっていました。 プラグインが好きな人もそうでない人もいるでしょう。しかし、誰かがあなたと同じ問題を抱えていて、その解決策を公開している可能性が大いにあります。 最も人気のあるプラグインは、gatsby-plugin-react-helmet、gatsby-plugin-sharp、そしてgatsby-imageです。gatsby-plugin-preactのように、ページのパフォーマンスを向上させるプラグインはたくさんあります。以前、パフォーマンス問題を解決するために作られたGatsbyプラグインについてもお話しました。 大規模なエコシステムは、開発者の経験を向上させます。つまり、サイトマップの生成やコンテンツ・セキュリティ・ポリシーの追加などの退屈な作業には、すでに解決策があるということです。 3-5 どうやって始めるの? コンピュータにGatsbyをグローバルにインストールするには、コマンドを実行します。 Gatsbyを始めるには、多くのスターターが用意されていますので、それを利用するのが一番早い方法です。 スターターからプロジェクトを作成するには、ギャラリーから1つ選び、そのGithub URLをコピーして実行します。 Gatsbyプロジェクトをローカルに起動するには、次のように実行します。 あなたの開発環境は http://localhost:8000 で利用できます。 3-6 Gatsbyの展開 ギャツビーはどこでも展開できます。実行する必要のあるコマンドはこれだけです。 Publicフォルダには、公開可能なすべての静的ファイルが格納されています。 プロジェクトに少量のGraphQLクエリと画像が含まれている場合、すべてがうまくいきます。数百枚のような大規模な画像セットでは、構築に非常に時間がかかる傾向があり、CI/CDツールでタイムアウトエラーが発生する場合があります。 構築時間を改善したい場合、最も簡単な解決策はGatsby Cloudに切り替えることです。高速なIncremental Buildに対応し、キャッシュ管理も強化されます。 構築の部分はGatsbyクラウドが担当しますが、ウェブサイトをユーザーに届けるためには、どこかでホストする必要があります。一般的なホスティングプロバイダーであれば、簡単にプロジェクトを接続することができます。Gatsbyは、Netlify、Vercel、Fastly、Google Storage、Azure、Firebase、およびAWS S3と統合します。既存のインフラに最もマッチしたものを選んでください。 3-7 結論 Gatsby.jsは、多くのデータソースを使用しており、1つの内部APIを使用してすべてを管理したい場合に適しています。GatsbyはNext同様、Reactをベースにしています。インタラクティブなコンポーネントを簡単に作ることができますが、それにはJSのコストがかかります。クライアントサイドのルーティングは、特にハイエンドのデバイスにおいて、知覚的なパフォーマンスを最適化します。 JavaScriptを大量に読み込む必要があるため、最も軽量な静的サイトジェネレーターとは言えません。Lighthouseのパーフェクトスコアを達成するのは難しいかもしれませんが、時にそれは重要ではありません。非常に高速で、視聴者が気持ちよく利用できるウェブサイトを作ることができます。 4. Eleventy 最終更新日:2020年10月26日 Zach Leathermanが2018年にEleventyを作成して以来、特にJSを多用した静的サイトジェネレーターに飽きた人たちからますます多くのフォロワーを獲得しています。Eleventyはデフォルトではゼロコンフィグで、どんなプロジェクトの構造にも対応しています。フレームワークに縛られることなく、最大11種類のテンプレート言語をサポートしています。Eleventy is simple(r)です。Eleventyはあなたに適応します。 最近の静的サイトジェネレーターの多くは、何らかのJavaScriptフレームワークを使用しており、React、Vue、Angularなどがよく知られています。Eleventyでは、JavaScriptを使って静的なページを生成しています。クライアントサイドのフレームワークは必要ありません。 どのフレームワークを使ってはいけないということではありません。それは、オプションであり、使用するかどうかはあなた次第ということです。 4-1 プログレッシブ・エンハンスメント GatsbyやNext.jsのようなJSベースの静的生成ツールでは、TTI(Time To Interactive)が長いことがよく問題になります。ブラウザは、アプリケーションをハイドレードさせるために、大きなJSの塊をダウンロードし、解析し、実行する必要があります。それにはコストがかかるため、結局TTIになってしまい、入力できるまでの遅延が起きます。 一般的なウェブサイトでは、多くのコンポーネントがあまりインタラクティブではないにもかかわらず、ハイドレーションを必要とします。Reactチームは、この問題を解決するためにパーシャルハイドレーションに取り組んでいますが、完成の時期は明確ではありません。 Eleventyでは、まず必要なコンテンツを提供することに集中し、段階的に動的な機能を追加していくことができます。アニメーションやスライダーなどを作るには、バニラJavaScriptとピュアCSSだけで十分な場合もあります。 4-2 ニード・フォー・スピード 優れたパフォーマンスは、現代のあらゆるウェブサイトに不可欠な要素です。Eleventyを使った開発者がこのスピードを気に入っています。そして、スピードはEleventyを愛しています。JavaScriptの大きな負荷がかからないため、EleventyのウェブサイトはLighthouseで素晴らしい結果を出しています。Eleventyの作者であるザック氏が作成したリーダーボードを見てみましょう。 リーダーボードは2週間ごとに更新され、ベストスコアを獲得したプロジェクトのリストが表示されます。そのほとんどは、非常にシンプルな個人のウェブサイトです。だから、これは大目に見てください。つまり、Eleventyが市場で最も速いSSGであることを本当に意味しているわけではないのです。これらのページの多くは、他のスタティックサイトジェネレーターでも同じようなスコアになると思います。 4-3 ファイル構成 というようなシンプルなもので構いません。  npx 11ty/eleventyコマンドを実行すると、次のようになります。 _siteは出力フォルダを展開する準備ができています。 簡単なブログを作ってみましょう。以下の例は、公式のeleventy-base-blog starterに基づいています。 4-4 データソース Eleventyでは、複数のソースからデータを取得しています。一番わかりやすいのは、ページファイルやテンプレートファイル内のフロントマターです。 4-5 ヘッドレスCMSからデータを取得する 現実のプロジェクトには、何らかのCMSが必要ですが、コンテンツチームのすべてがmdファイルを編集してgitでプッシュすることに満足しているわけではありません。 APIからデータを取得するには、_dataフォルダ内にJSファイルを作成する必要があります。これにより、データはグローバルに利用可能になります。 データを取得して特定の範囲で使用したい場合は、テンプレートフォルダやディレクトリフォルダ内に*.11tydata.jsファイルを作成する必要があります。 ディレクトリ フォルダー内にファイルを作成する必要があります。 4-6 エコシステム Eleventyは、主にコミュニティによって作成されたスターターリストを持っています。クールなのは、毎日更新される各スタート地点のLighthouseスコアを見ることができる点です。 Eleventyには、すぐに使えるプラグインはあまりありませんが、RSS、PWA、Tailwind、Table of Contentsなどの機能を追加するプラグインがいくつかあります。 4-7 どうやって始めるの? Eleventyをグローバルにインストールするには、コマンドを実行します。 次に、フォルダとindex.mdファイルを作成します。 Eleventyプロジェクトをローカルに起動するには、次のように実行します。 http://localhost:8080 を開いて、ウェブサイトを表示します。 4-8 結論 Eleventyは、Jamstackの世界で冒険を始めるための最良の選択かもしれません。柔軟性があり、ReactやVueのような特定のJSフレームワークを学ぶ必要がありません。 5. Hugo 最終更新日:2020年10月26日 SSG市場で最速のビルドタイムを実現するために、Goでは書かれています。Steve FranciaとBjørn Erik Pedersenによって作成され、2013年7月5日に初めて公開されました。現在、Bjørn氏がメンテナンスを行っています。Hugoのファンが多いのは、そのスピードと、大規模なウェブサイトを簡単に作成できる柔軟性にあります。 Eleventyと同様に、ヒューゴも完全なJSハイドレーションではなく、段階的な強化を推奨しています。JavaScriptのフレームワークとは一切関係ありません。 5-1 データ取得 Hugoはmarkdownのようなローカルのフラットファイルしかサポートしていません。綺麗なユーザーインターフェースでコンテンツを編集したい方は、GitベースのCMSをご利用ください。人気があるのは、NetlifyCMSとForestineです。 ヘッドレスCMSなどの外部APIからデータを取得するには、getJSON関数を使用します。 5-2 ファイル構成 Hugoにはアーキタイプのようなハイレベルなコンセプトがいくつかあります。テンプレートやコンテンツスキーマのようなもので、ウェブサイトのセクションにフィールドを定義できる場所だと考えてください。 特定のフォルダについて詳しく知りたい場合は、公式ドキュメントをご覧ください。 5-3 エコシステム その性能の高さとシンプルさから、Hugoは長年にわたって多くのファンを獲得してきました。問題が発生した場合は、公式のパブリックフォーラムですぐに回答を得ることができます。 Hugoは数多くのテーマを集めています。それらは正式なプロジェクトではありません。そのほとんどが、世界中のHugoの開発者によって作られています。MITライセンスで提供されており、自由に使用、変更することができます。シンプルなブログ、ドキュメント、ランディングページ、履歴書など、さまざまなカテゴリーが用意されています。 5-4 どうやって始めるの? インストール方法はお使いのOSによって異なりますので、インストールドキュメントをご確認ください。GoをインストールしていなくてもHugoを使うことができます。 Hugoをインストールしたら、実行してみましょう。 これで、テーマを選択するか、新しいテーマを作成することができます。 コンテンツを追加するには、実行します。 開発サーバーを起動するには、次のように実行します。 あなたの開発環境は、http://localhost:1313/ 5-5 結論 Hugoは、ReactやVue、SvelteなどのモダンなJSフレームワークを好んで使っている人にとっては、少し古めかしく感じるかもしれません。しかし、ヒューゴの年齢は強みでもあります。有名企業の多くのビッグプロジェクトで実証されているので、明日にでも消えてしまうことはないと確信しています。 初心者のための静的サイト・ジェネレーター・ガイド③に続きます 読んで下さり、ありがとうございました Jamstackに関心がある方はこちらまでお問合せください! 株式会社ヒューマンサイエンス https://www.science.co.jp/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

命を燃やすために出勤を記録したら部屋をアチアチにする【Nature Remo×kintone】

みなさん、命を燃やして仕事をしていますか? 私は、命を燃やして仕事をしています。 命を燃やすには、部屋がアチアチのほうがいいですよね。 というわけで、kintoneのタイムカードアプリで出勤を記録したら、Nature Remoを操作して部屋をアチアチにする外部連携をやってみます。 やりたいこと kintoneのタイムカードアプリで出勤を記録 Nature RemoをAPIで操作し、エアコンを温度MAXの暖房で始動 アチアチの部屋で心を燃やしながらお仕事 やりたいことはただひとつ。命を燃やしたい。それだけです。 Nature Remo Cloud API トークン生成 次の記事の「Nature Remo Cloud API トークン発行」を参考にして、トークンを発行しておきます。 kintoneアプリ作成 続いて、kintoneアプリの作成です。 今回はアプリストアの「タイムカード」アプリをそのまま利用しました。 タイムカードアプリは、デフォルトでは退勤時刻の初期値にレコード作成日時が設定されます。 今回のカスタマイズだと初期値が設定されるのは都合が悪いので、フォームの設定から初期値のチェックを外します。 以上でアプリの作成は完了。設定を保存しておきます。 Nature Remo上のエアコン情報を取得 今度は、APIを実行するために必要な、Nature Remo上のエアコン情報を取得します。curlを使って次のコマンドを実行。 curl -X GET -H 'Authorization: Bearer トークン' https://api.nature.global/1/appliances | jq すると、Nature Remoと連携しているデバイスの情報が入ったオブジェクトの配列が得られるはず。nicknameなどでエアコンの情報が入ったオブジェクトを特定して、IDをメモしておきます。 [ { "id": "ここにエアコンのIDがある", "device": { ・・・ }, "model": {         ・・・ }, "type": "AC", "nickname": "エアコン", "image": "ico_ac_1", "settings": { "temp": "28", "temp_unit": "c", "mode": "cool", "vol": "auto", "dir": "auto", "dirh": "", "button": "", "updated_at": "2021-07-27T00:25:44Z" }, コードをかきかき 最後に外部連携のキモとなるコードを書いていきましょう。 (function() { 'use strict'; const RTOKEN = トークン; const AIRCONID = エアコンのID; // レコード保存時にイベント発火 kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], event => { // 退勤時刻が設定されていない場合に処理開始 const clockOut = event.record['退勤時刻'].value; if(clockOut === undefined) { // 部屋をアチアチにするためにNature Remoに投げるパラメータ設定 const url = `https://api.nature.global/1/appliances/${AIRCONID}/aircon_settings`; const headers = { 'Authorization': 'Bearer' + RTOKEN, 'Content-Type' : 'application/x-www-form-urlencoded' }; const data = 'button=&temperature=30&operation_mode=warm'; // kintone.proxyでNature Remo Cloud APIを実行 kintone.proxy(url, 'POST', headers, data) .then(resp => { return event; }).catch(resp => { event.error = resp.message; return event; }) } }) })(); 今回は簡略化のため、トークンなどの秘匿情報をコード上に直接記述しています。 実際に外部のトークンを扱う場合は、プラグイン化などを行ってトークンを秘匿化してください。 書いたコードは、アプリの設定からJavaScriptカスタマイズとして適用しておきます。 いざ動作確認! アプリで新しくレコードを作成し、出勤時刻を確認して「保存」をクリックすると... エアコンから「ピッ」という音が聞こえました。Nature Remoのアプリで確認すると、ばっちりアチアチの設定になっています。 アチアチの部屋で命を燃やしながら仕事ができるようになりましたね! 〆 記事の趣旨はふざけきっていますが、Nature Remoの連携先としてkintoneはけっこう面白い選択肢だと思います。 Nature Remoのデータ保存先としても使えるし、保存したデータを元にNature Remoを操作することもできる。 他にも便利な使い方があると思うので、ガンガン連携していきましょう。 告知 kintoneカスタマイズの便利さ・幅広さを、 時に真面目に、時に面白おかしく伝える「デベロッパーマーケティング」のお仕事について、 気軽にいろいろ聞けるイベント「キャリアBar」が開催されます! Barなんで、気軽にふらっと立ち寄ってみてください。 ではでは〜ノシ この記事は、次の記事の内容と同一のものですm(_ _)m
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptの分割代入

分割代入を使用すると const MyProfile = { name: "ゆう", age: 18, } const test1 = `名前は${MyProfile.name}です。年齢は${MyProfile.age}です` console.log(test1); //名前はゆうです。年齢は18歳です。 これを const MyProfile = { name: "ゆう", age: 18, } const {name, age} = MyProfile const test2 = `名前は${name}です。年齢は${age}です` console.log(test2); //名前はゆうです。年齢は18歳です。 のように書くことができる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JS React useStateで取得する関数に関数を渡す場合の注意

知っておくべき動作があったのでメモとして残しておきます。 もともとは次の記事に記載されていた挙動を自分でも確認しようと思って いろいろ試したときに発見しました。 破壊的メソッドとミュータブル・イミュータブル [React,Javascript] - Qiita 動作確認コード create-react-app で作成したあとに次のように記載します。 App.js import logo from './logo.svg'; import './App.css'; import { useState } from "react"; function App() { const [num, setNum] = useState([0]); // // 1.正常動作 // const addNum = () => { // num.push(1); // setNum([...num]) // }; // // 2.正常動作 // const addNum = () => { // setNum((preNum) => { // const newNum = [...preNum]; // newNum.push(1); // return newNum; // }); // }; // 3.異常動作 const addNum = () => { setNum((preNum) => { preNum.push(1); console.log({preNum}) return [...preNum]; }); }; return ( <> <button onClick={addNum}>1増えるよ</button> <p>{num}</p> </> ); } export default App; useState取得関数にuseState取得値を指定する方法と、useState取得関数に関数を渡す方法とがあります。Reactのドキュメントのここに記載があります。 関数型の更新 フック API リファレンス – React 正常動作、とコメントに記載したコードは正常に動作します。動作結果は次のようになります。 0 0 1 0 1 1 0 1 1 1 異常動作、とコメントに記載したコードは次のようになります。ボタンを押すごとに2回pushが実行されてしまいます。 0 0 1 0 1 1 1 0 1 1 1 1 1 しかも内部に仕込んでいるconsole.logが1回しか呼び出されていないのに、2回pushされている動作になっていて「何これ?」と非常に驚きました。 React useState の所に解説がありました。 QiitaのQAで聞いて教えてもらいました。 [Q&A] ReactのuseStateを使ったstate更新関数に関数を渡したときの変な挙動について知りたい - Qiita Reactのドキュメントのこちらに説明があります。 strict モード – React strict モードでは自動的には副作用を見つけてはくれませんが、それらの副作用をほんの少し決定的にすることによって特定できる助けになります。これは、以下の関数を意図的に 2 回呼び出すことによって行われます。 補足 React 17 以降で、React は console.log() のようなコンソールメソッドを自動的に変更し、ライフサイクル関数の 2 回目のコールでログが表示されないようにします。これにより特定のケースで意図しない動作を引き起こすことがありますが、回避策も存在します。 なるほどそういう仕組なんですね。 回避策は const console_log = console.log; として、内部で console.log ではなく console_log を使えという単純なもの。 これを踏まえて調べてみると、create-react-app で作成したプロジェクトの index.js は次のようになっていました。 index.js import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode> , document.getElementById('root') ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals reportWebVitals(); 確かに StrictMode というのが適用されていて、こんな挙動になったというわけ。 React.StrictMode を解除すれば[3.異常動作]の書き方をしても発生しなくなっていた。がそのままでいいこともなく[1.正常動作][2.正常動作]のどちらかの書き方にしておいたほうがよいです。 [3.異常動作] での書き方は、渡された関数の引数は更新前のstateの値なので、その値に対してpushして変更をかけると、よくない副作用を起こすということらしい。 気をつけてコード書いておきましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む