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

【Javascript】Strings(2)学習ノート

初めに Stringについて学習した内容のoutput用記事です。 ※内容に間違いなどがある場合はご指摘をよろしくお願いします。 ※こちらの記事はあくまでも個人で学習した内容のoutputとしての記事になります。 前回の記事: https://qiita.com/redrabbit1104/items/d68a8f79420a2a0320a6 文字列の大小 英語の文字列に大文字と小文字が混ざっている場合、文字列を小文字にするにはtoLowerCase()、大文字にするにはtoUpperCase()を使います。 const bank = "Mitsubishi UFJ Bank"; console.log(bank.toLowerCase()); //mitsubishi ufj bank console.log(bank.toUpperCase()); //MITSUBISHI UFJ BANK これを応用すれば名前が大小混ざっている文字列を最初の文字だけ大文字にし、残りの部分を小文字にすることができます。 const accountName = "toMaS"; const accountNameCorrect = accountName[0].toUpperCase() + accountName.toLowerCase().slice(1); console.log(accountNameCorrect); //Tomas trim()メソッドで要らない空白を削除 emailが正しいかどうかをチェックする場合に、trim()メソッドを使い空白を取り除くことができます。 const email = "test@yahoo.co.jp"; const inputEmail = "  Test@YahoO.co.Jp \n"; const lowerEmail = inputEmail.toLowerCase(); const trimmedEmail = lowerEmail.trim(); console.log(trimmedEmail); //test@yahoo.co.jp コンソールで確認するとemailと入力したinputEmailが一致しています。 console.log(email === trimmedEmail); //true 文字列の変更 replace()メソッドを使えば、対象の文字列を変更することができます。 const pages = "The page you were looking for doesn't exist."; console.log(pages.replace("doesn't exist", "exists")); //The page you were looking for exists exist. また、replaceAll()で対象の文字列を全て変更できます。 const replaceTest = "Home Home Home Home"; console.log(replaceTest.replaceAll("Home", "School")); //School School School School Boolean 文字列が存在するか調べるにはincludes()メソッド、対象の文字列で始まるかどうかを調べるにはstartsWith()メソッド、対象の文字列で終わるかを調べるにはendsWith()メソッドを使います。結果はtrueかfalseになります。 const pages = "The page you were looking for doesn't exist."; console.log(pages.includes("you")); //true console.log(pages.startsWith("page")); //false console.log(pages.endsWith("exist.")); //true 参考サイト https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/trim https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replace https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MediaRecorderとffmpeg.wasmでp5.jsの超簡単録画ツール(p5.MovRec)を作った話

サマリ? MediaRecorderとffmpeg.wasmを使って、p5.js上で、簡単にスケッチの録画ができるツール(p5.MovRec)を開発しました。既存のp5.jsのスケッチを汚すことなく、HTMLファイルに2行追加して、キーボードを押すだけで爆速で動画が生成される簡単ツールとなっています。TwitterとかYouTubeとかに作品をアップされている方にお勧めです。 成果物 p5MovRec: A simple movie recording tool for p5.js 2021/6/5 現在のversionは0.5.0のalpha releaseです。 きっかけ 僕はよく自分のp5.jsの作品を動画にしてTwitterにアップすることが多いのですが、以前にp5.jsで作ったジェネラティブアートをtwitterに動画で貼り付ける際のメモにも書いたように、きれいな動画作品としてTwitterへ投稿するのは割と大変です。 静止画を切り出して、PC上のffmpegで連結して数分間レンダリングを待つ・・・という大変手間と時間がかかることをしていたわけです。 2021年の現在も、SNS上でもどうやって動画化していますか?という問いは定期的にみられますが、あまり的確なソリューションはでてきていないなぁという実感がありました。 また、特にTwitterとかが顕著ですが、アップしてもエンコードされてしまうので、個人的には画像がそこまできれいでなくてもよいから、もっと手間も時間もかからない動画化方法はないだろうか?とずっと考えていました。 で、最近WebAssemblyという技術に興味を持っていて、その中にffmpeg.wasmという『ffmpegをブラウザ上で実行できるツール』があることを知りました。もともとJSにはMediaRecorderでcanvasの録画ができたはずで、これをTwitterの要求するmp4/h264にトランスコードできないかな?と着手したのがきっかけです。 簡単な使い方 実行環境 PC(Win/Mac) 環境のみを想定 MediaRecorderとffmpeg.wasmはたぶん結構いろんなブラウザで動くのかなーとは思いつつ、Google Chromeのみを対象とさせてください。 なお、このツールはP5 EditorとOpenProcessingの両方でも正しく動作します!Webのサービスの中から、即時動画が生成される様子は『未来が来たな』という印象を受けます。 ライブラリのインポート p5.jsスクリプトの後に、以下の2行を追加するだけです。終了。 <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script> <!-- INSERT HERE --> <script src="https://unpkg.com/@ffmpeg/ffmpeg@0.10.0/dist/ffmpeg.min.js"></script> <script src="https://tetunori.github.io/p5MovRec/dist/0.5.0/basic/p5MovRec.js"></script> もう、すぐRecできる rキーを押すと録画開始です。デバッグコンソールに?Start Recording.のテキストが表示されるはずです。画面の中から勝手にcanvasを探してそこだけrecしてくれています。 次に、wキーを押すと録画が終了し、即時webmフォーマットの動画が生成されます!文字通り、秒で生成されます。こちらもコンソールに✅Recorded.のテキストが表示されます。このwebmフォーマットは聞きなれないかもしれないのですが、Chromeブラウザでは確実に再生ができると思います。 終了時にmキーを押して録画を終了させると、今度は(少し時間がかかりますが)mp4フォーマットの動画が生成されます。この動画形式でTwitterなどにアップすることができるようになります。Chromeですと、ブラウザ下部に、ダウンロードされたファイル(YYYYMMDDhhmmss.webm/mp4という名前)が表示されます。 このツールは内部でkeyPressed()を使っているので、もしすでにスケッチ内でkeyPressed()を使っている場合はそちらで上書きされてしまいます。よって、本ツールを使うときは、以下のように録画開始終了の関数をkeyPressed()内で呼んでください。インスタンスp5MovRecとそのメソッドstartRec()、 stopRec()とsetMovType()を使用します。 function keyPressed() { switch (keyCode) { case 49: //1: Start record p5MovRec.startRec(); break; case 50: //2: set webm, stop p5MovRec.setMovType(P5MovRec.movTypeId.webm); p5MovRec.stopRec(); break; case 51: //3: set mp4, stop p5MovRec.setMovType(P5MovRec.movTypeId.mp4); p5MovRec.stopRec(); break; default: break; } } キー操作の表 キー 説明 r (Record) 録画開始。 w (Webm) 録画終了。Webmフォーマットの動画を生成。 m (Mp4) 録画終了。mp4フォーマットの動画を生成。 サンプル Basic Sample On GitHub Basic Sample On P5 Web Editor Basic Sample On OpenProcessing 注意! canvasについて このツールはcreateCanvas()で生成されたcanvasのstreamから動画を作成するため、#つぶやきProcessingや#p5tの作品においては、createCanvas()をsetup()の模範的な位置に動かす必要あります。例えば、下記のように。 Starndard #つぶやきProcessing code: t=0 draw=_=>{ createCanvas(w=720,w) noStroke(fill('#つぶやきProcessing')) ... Please change as: t=0 w=720 setup=_=>{ createCanvas(w,w) } draw=_=>{ noStroke(fill('#つぶやきProcessing')) ... 動画の品質について ちょっと長くなるので、下のほうにTipsの一部として挙げておきます。 もっと高画質にもできるし、微調整も可能 上記はとにかく簡単に使える方法のご紹介でしたが、Twitterにアップすることを目的としていたので、コーデックはh264が選択される様になっていました。実は、このツールでは、vp9コーデックもサポートしており、こっちのほうがかなりきれいな動画になります。それ以外にも録画開始終了のタイミングや各種パラメータを設定できたりするので、ご紹介します。 ライブラリのインポート こちらもp5.jsスクリプトの後に、以下の2行を追加するだけで終了ですが、URLの一部が異なっていることに注意してください。 <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script> <!-- INSERT HERE --> <script src="https://unpkg.com/@ffmpeg/ffmpeg@0.10.0/dist/ffmpeg.min.js"></script> <script src="https://tetunori.github.io/p5MovRec/dist/0.5.0/advanced/p5MovRec.js"></script> p5.jsスケッチの中での使い方 createCanvas()直後にインスタンス生成 codecIdを指定して、P5MovRec()をnewしましょう。このcodecIdでコーデックが指定できます。 P5MovRec.codecId.vp9とP5MovRec.codecId.h264が選べるようになっています。advancedではデフォルトはvp9がデフォルトで設定されています。 let myP5MovRec; // Please prepare instance in global function setup() { createCanvas( 720, 720 ); myP5MovRec = new P5MovRec(); // P5MovRec.codecId.vp9 is selected by default. // myP5MovRec = new P5MovRec(P5MovRec.codecId.vp9); // Same as above. // myP5MovRec = new P5MovRec(P5MovRec.codecId.h264); // For h264 codec. } 録画の開始・停止 これは、basic側の使い方とそれほど変わりません。movTypeのデフォルト値はP5MovRec.movTypeId.webmになっています。 function keyPressed() { switch (keyCode) { case 49: //1: Start record myP5MovRec.startRec(); break; case 50: //2: set webm, stop // myP5MovRec.setMovType(P5MovRec.movTypeId.webm); // webm is default value myP5MovRec.stopRec(); break; case 51: //3: set mp4, stop myP5MovRec.setMovType(P5MovRec.movTypeId.mp4); // for mp4 container myP5MovRec.stopRec(); break; default: break; } } サンプル Advanced Sample On GitHub Advanced Sample On P5 Web Editor Advanced Sample On OpenProcessing Tips 動画品質について コーデック このツールのh264コーデックでの出力動画品質はvp9に比べて若干低めです。もし動画の品質で悩むようでしたら、ぜひvp9設定にトライしてください。ただし、繰り返しの注意ですがTwitterでは、vp9コーデックの動画がサポート外ですので、ご注意ください。YouTubeはアップロードできると思います。たぶん。 フレームレート 出力動画のフレームレートが気になる場合もあると思います。PCなどの環境都合で、フレームレートが速くなりすぎたりということもままありました。そのような場合は、p5.jsのスケッチ上で、 frameRate()を調整してみてください。frameRate(30)、frameRate(40)、frameRate(60)とかで上限を設定してあげると所望の動画が手に入りやすくなります。 Canvasのサイズ これはTwitter向けの話ですが720がベストにエンコードされる値かと思います。ブラウザの拡縮機能で大きさが変わっていると、この拡縮値が結果に影響することもありますので、動画のクオリティが気になる際は、Zoomを100%にもどしたり、80%に小さくしたりなどで改善する可能性があります。出力動画の縦横サイズもチェックしてみてください。 動画のビットレート 作品によっては、出力動画のビットレートがすごく高い値になってしまうことがあると思います。(画面全体でひっきりなしに動く動画など。) 特にTwitterでは高ビットレートの動画はエンコード結果が悪いので、動画の時点でなるべく下げておきたいところです。 今回のツールでは、P5MovRec()の第2引数に、videoBPSという録画時のビットレート指定ができるようにしています。ただし、この設定値通りになるわけではなく、わりとやんわりとそれらしい値に近づく、くらいの機能だと思ってください。使用方法は以下の通りですが、Advanced Sampleで実際に見ることができます。 なお、ビットレートについても、canvasのサイズやフレームレートが影響する場合があるので、こちらも調整すると効果が出る場合があります。 function setup() { createCanvas( 720, 720 ); const vbps = 2000000; // 2000kbps myP5MovRec = new P5MovRec(P5MovRec.codecId.vp9, vbps ); ... } リファレンス ffmpeg.wasm https://github.com/ffmpegwasm/ffmpeg.wasm https://github.com/ffmpegwasm/ffmpeg.wasm/blob/master/examples/browser/webcam.html MIT License, Copyright (c) 2019 Jerome Wu Screen Capture on your browser with ffmpeg.wasm https://dannadori.medium.com/screen-capture-on-your-browser-with-ffmpeg-wasm-b9ce333067aa MediaRecorder https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

センター試験の数学をJSで解く【数学Ⅰ・A】_1

var r = (function* f(a) { while(a < 100){ ((a ** 2 - 2 * a - 8) === 0 ? (yield a) : "") a += 1 } })(-100) for(var v of r) console.log(v)//-2,4 ア - イ 2 ウ 4
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

保育園児の折り紙あるある「これ折って!」に応える機械学習型WEBアプリを初心者がプロトタイピング

保育園で作った謎の折り紙を嬉しそうに持って帰ってくるうちの保育園児。 子:「先生にこれ作ってもらった!もう一つ作って!」 父:「え、、、」(なにこれ、、、飛行機か?) 子:「早く早く!」 父:「お、おう、、」(どうしよう。。) このようなシチュエーションありますよね?! 折しも、機械学習とやらに挑戦しているところなので、この身近な「保育園児あるある」を機械学習の技術を使って解決してみたいとおもいます。というわけで以下の通りプロトタイピングしてみました。 動作の様子 機能の説明が難しいので、まずはプロトタイプの動画をご覧いただきイメージアップください。 機能概要 子供が保育園で作ってきた折り紙(作品)をスマホのカメラで撮影すると以下の情報を提供するWEBアプリです。 折り紙の作品名(鶴とか蝉とか、メダルとか) 作り方説明動画 さらにここが素敵!(自画自賛) 途中で作り方が分からなくなったら、その時点の折り紙をスマホのカメラで撮影。なんと、そこからの手順を動画で解説!楽々リスタート! 作品にまつわる現実の画像を表示!「本物はこんななんだね!」と子供と盛り上がること間違いなし! 機能構成 Teachable Machineで折り紙の形を機械学習し、カメラで撮影した「作品」および「作品の制作過程」を判定できるようにします。 判定結果に応じ「作成手順を説明した動画」を再生します。作品に応じた実世界の画像をPixabayから取得して表示します。 準備するもの 折り紙の形を機械学習したモデル GoogleのTeachable Machineを利用しました。 こちら様のドキュメントを参考にさせていただきました。 Teachable Machineを用いたトレーニングのイメージ ブラウザとカメラだけで簡単にトレーニングができます。コーディングなどしません。この手軽さは本当に驚きです。 とても簡単かつ面白いので、ぜひチャレンジしていただくことをおススメいたします! 今回は、うちの保育園ではおなじみの「蝉」と「メダル」の折り紙を学習させました。実際に使えるアプリケーションにするためには、もっとたくさんの種類の「折り紙」を学習させる必要があります。 折り紙手順が分かる動画(最初から最後まで、途中から最後まで) 最初から完成までの作成手順を記録した動画に加えて、途中からリスタートする場合の動画を作成(切り出し)します。 機械学習させたクラスと相対するよう動画を準備します。 なかなか面倒です。 プログラム 以下のライブラリを利用して、Javascriptで開発しました。 利用したライブラリ vue.js および ml5.js 利用したWEBAPI Pixabay Developer API Pixabayについては、こちら様のドキュメントを参考にさせていただきました。 作成したプログラムについては以下のセクションを参照ください サンプルプログラム index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>これ折っちゃおう!</title> </head> <body> <h1>折り紙を折ろう!</h1> <div id="app"> <p>処理状況:{{ modelState }}</p> <p>この折り紙は:{{ detectedName }}ですね</p> <button v-on:click="modelhantei">折り紙の写真を撮る</button><BR> <video id="myvideo" width="320" height="240" muted autoplay playsinline></video> <br /> <h2>作り方!</h2> <video id="videoplay" width="320" height="240" ></video> <br /> いろいろ見てみよう!<BR> <button v-on:click="getsamplepic">サンプル画像取得</button><BR> <div id = "result"></div> </div> <!-- ml5.jsとVue.jsをCDNから読み込みます --> <script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // 作成したモデルのURL const imageModelURL = 'https://teachablemachine.withgoogle.com/models/hc5kqu-hB/'; let classifier; const app = new Vue({ el: '#app', data: { modelState: '準備しています。ちょっとまってね・・・', detectedName: '', }, async mounted() { // デバイス(カメラ・マイク)からのデータ取得を試みる // 映像や音声が使えるデバイスが確定するまで時間がかかるためawaitを使う const devices = await navigator.mediaDevices.enumerateDevices(); console.log(devices); // カメラからの映像取得 //私のスマホの背面カメラは1番 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: { deviceId: devices[1].deviceId }, }); // IDが"myvideo"であるDOMを取得 const video = document.getElementById('myvideo'); // videoにカメラ映像をセット video.srcObject = stream; // 自作モデルのロード classifier = ml5.imageClassifier(imageModelURL + 'model.json', video, () => { // ロード完了 app.modelState = '折り紙モデルのロードが完了しました。'; console.log('Model Loaded!'); }); }, methods: { modelhantei: function () { classifier.classify((err, results) => { console.log(results); if (results[0]) { let res = ''; res = results[0].label; if (res == 'semi'){ app.detectedName = '蝉'; //Debug・Demo用 } //判定した結果を受けてセミの作り方動画を再生 **ここはモデル名とファイル名を同じにしておく。** vp = document.getElementById("videoplay"); vp.src = "./" + res + ".mp4"; vp.play(); } else { app.detectedName = 'わからない...'; } }); }, getsamplepic:function(){ //画像を取得するためのURLを生成する let URL = this.createURL(app.detectedName); console.log(URL); fetch( URL ) .then( function( data ) { return data.json(); //JSONデータとして取得する }) .then( function( json ) { console.log(json); //直書きする///////////////// let result = document.getElementById('result'); result.innerHTML = ''; //検索するごとに画面をリセットする //該当する画像があれば if( json.totalHits > 0 ) { json.hits.forEach( function( value ) { let img = document.createElement('img'); let a = document.createElement('a'); a.href = value.pageURL; //ダウンロード用のサイトURL a.target = '_blank'; img.src = value.previewURL; //プレビュー用の画像URL a.appendChild( img ); result.appendChild( a ); }) } else { alert('該当する写真がありません'); } }) }, //リクエスト用のURLを生成する createURL:function( value ) { let API_KEY = '*****取得したAPI KEY*****'; let baseUrl = 'https://pixabay.com/api/?key=' + API_KEY; let keyword = '&q=' + encodeURIComponent( value ); let option = '&orientation=horizontal&per_page=3'; let URL = baseUrl + keyword + option; return URL; }, }, }); </script> </body> </html> 今後に向けて 今回はプロトタイプということで、学習した折り紙は「蝉」と「メダル」の2種類のみ。途中からリスタートするためのクラスも「蝉」の一部のみとしました。使い物になるアプリケーションとするためには、折り紙のバリエーションを増やす必要があります。また、途中からリスタートするための説明動画を複数準備しましたがこれもなかなか面倒な作業です。この点については、動画の中身を解析してくれるAI(GoogleのCloud Video Intelligence API)というものがあるようなので、それを使うことで、最初から完成までの動画一つで、どこからでもリスタートできるようになりそうです。機械学習ってすごいですね!そんなわけで、次はGoogleのソレを使ってみたいなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AIは増えすぎたガンダムを見分けることができるのか?Teachable Machineで作った分類モデルで検証してみた

この3体のガンダム、見分けつきますか? さて、いきなりですがクイズです。 上の画像の3体のガンダム。あなたは見分けることができますか? 正解は左から「ガンダム」「ガンダム7号機」「オーガンダム」です。 ガンダム好きにとっては簡単でしたかね? しかしながら、日々増えていくガンダム。 このままいくと本気で見分けがつかないものも出てくるでしょう。 (というか、私も突然見せられたら全部分かる自信無いです。) このままではマズいっ! ということで、今回はAIの力を借りてガンダムを見分けるアプリケーションに挑戦しました。 そうして完成した「教えて!アストナージさん」 そうしてできあがったのが「教えて!アストナージさん」というWebアプリケーションです。 名前を知りたいガンダムの画像をアップロードすると、我らがアストナージさんが名前を教えてくれます。 (アストナージさんについて知らない人はこちらを見て下さい。) ↓ここから実際に触れます。 こんな感じの仕組みでできてます。 使っているライブラリやツールは以下の通り。 ■ml5.js HTML上で簡単に機械学習モデルを扱えるようにするライブラリ。 プリセットされたモデルの他、自分で用意した学習済みモデルも使用できる。 ■Teachable Machine Googleが提供しているWebアプリ形式の機械学習プラットフォームです。 画像や音声の分類モデルなどをブラウザ上で簡単に作成できます。 ■Python3 + OpenCV3 学習に使う画像を増やすために使用。 OpenCVを使って元画像に変化を付けながら画像を増やしました。 アストナージさんに覚えてもらったガンダムたち 今回アストナージさんに覚えていただいたのは以下のガンダムたちです。 (1体あたり300枚程度の画像データで学習しました。) 機体 登場作品 ガンダム 機動戦士ガンダム 陸戦型ガンダムガンダムEz8 機動戦士ガンダム 第08MS小隊 ガンダムNT-1 機動戦士ガンダム0080 ポケットの中の戦争 ガンダム7号機 機動戦士ガンダム戦記 ガンダム試作1号機ガンダム試作3号機 機動戦士ガンダム0083 STARDUST MEMORY ガンダムMk-IIZガンダム 機動戦士Zガンダム ZZガンダム 機動戦士ガンダムZZ νガンダム 機動戦士ガンダム 逆襲のシャア ユニコーンガンダム 機動戦士ガンダムUC Ξガンダム 機動戦士ガンダム 閃光のハサウェイ ガンダムF91 機動戦士ガンダムF91 VガンダムV2ガンダム 機動戦士Vガンダム シャイニングガンダムゴッドガンダム 機動武闘伝Gガンダム ウイングガンダムウイングガンダムゼロ 新機動戦記ガンダムW ウイングガンダムゼロ(EW版) 新機動戦記ガンダムW Endless Walts ガンダムエックスガンダムダブルエックス 機動新世紀ガンダムX ∀ガンダム ∀ガンダム ストライクガンダムフリーダムガンダム 機動戦士ガンダムSEED インパルスガンダムデスティニーガンダムストライクフリーダムガンダム 機動戦士ガンダムSEED DESTINY スターゲイザーガンダム 機動戦士ガンダムSEED C.E.73 -STARGAZER- ガンダムエクシアダブルオーガンダムオーガンダム 機動戦士ガンダム00 ダブルオークアンタ 劇場版 機動戦士ガンダム00 -A wakening of the Trailblazer- ガンダムAGE-1ガンダムAGE-2ガンダムAGE-3ガンダムAGE-FX 機動戦士ガンダムAGE G-セルフ Gのレコンギスタ ガンダムバルバトスガンダムバルバトスルプスガンダムバルバトスルプスレクス 機動戦士ガンダム 鉄血のオルフェンズ 作り方 ここからは作り方を紹介していきます。 ①学習データの準備 最初に機械学習に用いる画像を集めていきます。 先ほど紹介したガンダムたちの画像をGoogle画像検索でそれぞれ20枚ほど集めました。 集める際に気をつけたのが、「なるべく全身が写っている」「他に余計なものが写っていない」という点です。 各ガンダムの画像が用意できたら、OpenCVの力でちょっとずつ変化を加えながら画像を増やしていきます。 詳しいやり方は別記事にまとめたのでこちらを参照してください。 これで学習データの準備は完了です。 ②モデルの生成 Teachable Machineで分類モデルを作っていきます。 トップページで「使ってみる」を選択 プロジェクトの作成画面になるので今回は「画像プロジェクト」を選択 学習画面になるので、必要な数(今回だと42個)のクラスを作成して、画像をアップロードし、トレーニングを実行 モデルアップロードを選択後、共有可能なリンクを控えておく これで分類モデルの準備まで完了 Webアプリケーションの作成 作成した機械学習モデルを実行するWebアプリケーションを作成していきます。 コード全文はCodePenで貼っておきます。(モデルのURLだけ変えれば使えます。) ライブラリはml5.jsとBootstrap4.5を使用。 See the Pen 教えて!アストナージさん by shoito66 (@shoito66) on CodePen. 今回はアップロードした画像に対してモデルを適用している。 この辺りの解説は別記事を書いたのでそちらを見てください。 結局アストナージさんはガンダムを分類できるようになったのか ちょっと確認してみました。 2勝1敗でした。 ただ、上げる画像によっては全然違う答えを返したりもします。 人間からすると全く違うガンダムでもAIから見ると意外と似ていたりするのかも知れません。 今後は学習画像のバリエーションなど、工夫することで精度が上がらないか色々考えてみたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマホ&ブラウザでカメラが使えるか デバイス取得お試し

スマホでカメラが使えるか確認 カメラの指定は、コンソールでログ出力して「検証」からデバイス一覧して、配列番号を取得 JS const devices = await navigator.mediaDevices.enumerateDevices(); console.log(devices); // カメラからの映像取得 //私のスマホの背面カメラは1番 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: { deviceId: devices[1].deviceId }, index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Camera Test</title> </head> <body> <h1>Webカメラテスト</h1> <video id="myvideo" width="640" height="480" muted autoplay playsinline></video> <script> // 関数の定義 async function main() { // デバイス(カメラ・マイク)からのデータ取得を試みる // 映像や音声が使えるデバイスが確定するまで時間がかかるためawaitを使う const devices = await navigator.mediaDevices.enumerateDevices(); console.log(devices); // カメラからの映像取得 //私のスマホの背面カメラは1番 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: { deviceId: devices[1].deviceId }, }); // IDが"myvideo"であるDOMを取得 const video = document.getElementById('myvideo'); // myvideoなvideo要素のsrcObject(映像オブジェクトを入れるところ)にデータ(メディアストリーム)をセットする video.srcObject = stream; } // 実行する main(); </script> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WEBブラウザで動画再生お試し

src.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>sample</title> <script> //A再生 function PlayA(){ vp = document.getElementById("videoplay"); vp.src = "./a.mp4"; vp.play(); } //D一時停止 function PlayD(){ vp = document.getElementById("videoplay"); vp.src = "./d.mp4"; vp.play(); } </script> </head> <body> <video id="videoplay" width="500" height="300" > <source src="./d.mp4" type="video/mp4"> </video> <br /> <button onclick="PlayA()">a.mp4を再生</button> <button onclick="PlayD()">d.mp4を再生</button> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像取得WEBAPI PixabayAPIお試し

Pixabay公式 アカウント作ってAPIキーを取得する。特に障壁なし。 per_pageで取得画像枚数を設定できる。3枚が最低限 サンプル Codepen index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Pixabay API サンプルデモ</title> <link rel="stylesheet" href="style.css"> </head> <body> <form name="search"> <input name="key" type="text" placeholder="文字を入力してください"> <input name="btn" type="submit" value="ボタン"> </form> <div id="result"></div> <script src="app.js"></script> </body> </html> Css form { margin: 25px 0; text-align: center; } form input[type="text"] { width: 80%; border: none; outline: none; padding: 5px 0; font-size: 18px; font-weight: bold; border-bottom: 1px solid #aaa; } form input[type="submit"] { padding: 5px 10px; border: 1px solid #aaa; outline: none; color: #000; background-color: #fff; } form input[type="submit"]:hover { color: #fff; background-color: #aaa; cursor: pointer; } img:hover { opacity: 0.5; } #result { width: 90%; margin: auto; padding: 30px; } JS (function() { 'use strict'; //フォームのボタンがクリックされたら、またはエンターキーが押されたら document.search.btn.addEventListener('click', function(e) { e.preventDefault(); //画面更新をキャンセル fetch( createURL(document.search.key.value) ) .then( function( data ) { return data.json(); //JSONデータとして取得する }) .then( function( json ) { createImage( json ); }) }) //リクエスト用のURLを生成する function createURL( value ) { var API_KEY = '*******'; var baseUrl = 'https://pixabay.com/api/?key=' + API_KEY; var keyword = '&q=' + encodeURIComponent( value ); var option = '&orientation=horizontal&per_page=3'; var URL = baseUrl + keyword + option; return URL; } //画像のJSONデータを画面に表示する function createImage( json ) { var result = document.getElementById('result'); result.innerHTML = ''; //検索するごとに画面をリセットする //該当する画像があれば if( json.totalHits > 0 ) { json.hits.forEach( function( value ) { var img = document.createElement('img'); var a = document.createElement('a'); a.href = value.pageURL; //ダウンロード用のサイトURL a.target = '_blank'; img.src = value.previewURL; //プレビュー用の画像URL a.appendChild( img ); result.appendChild( a ); }) } else { alert('該当する写真がありません'); } } })();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのthisについて

はじめに jsの勉強をしているので備忘録として残しておきます。 間違っていたらすみません... jsのthisとは? JavaScriptのthisとはオブジェクトを参照するキーワードのこと const obj = { first_name: '山田', last_name: '太郎', putFullname: function() {         //thisを用いる場合 console.log(this.last_name);         //thisを用いない場合         console.log(obj.last_name); } } //両方とも出力結果は太郎になる obj.putFullname(); オブジェクト内にthisを用いる時、上のコードの場合だとthisはオブジェクトであるobjを指している。 thisをクラス内で用いる場合 クラスを作成しただけではオブジェクトは生成されていないので、下記の場合のthisは何も参照していない。 class Obj { constructor() { this.first_name = '山田'; this.last_name = '太郎'; } putFullname() { console.log('hello'); } } なのでクラス外でオブジェクトを生成することで初めて、thisがオブジェクトを参照することができる。 class Obj { constructor() { this.first_name = '山田'; this.last_name = '太郎'; } putFullname() { console.log(this.last_name); } } //ここでオブジェクトを生成。この場合thisが参照するオブジェクトはobj2となる。 const obj2 = new Obj(); //出力結果は太郎になる obj2.putFullname(); まだまだ勉強中なのでいずれ編集すると思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのthisとbindについて

はじめに jsの勉強をしているので備忘録として残しておきます。 間違っていたらすみません... jsのthisとは? JavaScriptのthisとはオブジェクトを参照するキーワードのこと const obj = { first_name: '山田', last_name: '太郎', putFullname: function() {         //thisを用いる場合 console.log(this.last_name);         //thisを用いない場合         console.log(obj.last_name); } } //両方とも出力結果は太郎になる obj.putFullname(); オブジェクト内にthisを用いる時、上のコードの場合だとthisはオブジェクトであるobjを指している。 thisをクラス内で用いる場合 クラスを作成しただけではオブジェクトは生成されていないので、下記の場合のthisは何も参照していない。 class Obj { constructor() { this.first_name = '山田'; this.last_name = '太郎'; } putFullname() { console.log('hello'); } } なのでクラス外でオブジェクトを生成することで初めて、thisがオブジェクトを参照することができる。 class Obj { constructor() { this.first_name = '山田'; this.last_name = '太郎'; } putFullname() { console.log(this.last_name); } } //ここでオブジェクトを生成。この場合thisが参照するオブジェクトはobj2となる。 const obj2 = new Obj(); //出力結果は太郎になる obj2.putFullname(); オブジェクトが階層になっている場合 オブジェクトが階層になっている場合、thisは階層を上がって行った時に最初に見つかるオブジェクトを参照する。 const obj = { first_name: '山田', last_name: '太郎', putFullname: function() {         //この場合のthisはobjを参照する console.log(this.last_name);         window.setTimeout(function() { //この場合のthisはwindowを参照する console.log(this); }) } } obj.putFullname(); bindについて 下記のwindowオブジェクトの中でwindowではなく、objを参照したい場合はどうすれば良いのか? そんな時に用いるのがbindである。 bindの第一引数に与えられたものをオブジェクトとすることができる。 const obj = { first_name: '山田', last_name: '太郎', putFullname: function() {         //この場合のthisはobjを参照する console.log(this);         window.setTimeout(function() { //この場合のthisはobjを参照する console.log(this); }.bind(this)); //bindの第一引数のthisがobjを参照しているので、上記のwindowオブジェクト内のthisもobjを参照している。 } } obj.putFullname(); //出力結果は下記がコンソールに2つ出力される {first_name: "kkk", last_name: "太郎", setFullname: ƒ} first_name: "kkk" last_name: "太郎" setFullname: ƒ () __proto__: Object まだまだ勉強中なので編集すると思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

propsと$emitによるデータのやりとり

はじめに Vue.jsは1つのコンポーネントで実装するよりも、粒度に応じて複数のコンポーネントに分解して実装する方がよいとされている。そのようが、ソースコードの可読性やメンテナンス性、コンポーネントの再利用性も活かすことができて便利なためだ。だが、複数のコンポーネント間でデータのやり取りも必要不可欠になり、そこがVue.jsの扱いで最も難しいとされる箇所の1つなのではないかと思う。 そこで今回は自分が忘れてしまってからも当記事を確認するだけですぐに思い出せるように、簡単に実装例も交えつつ、「props」「emit」について取り上げます。 emit, propsとは コンポーネント間には親と子という概念がある。片方のコンポーネントがもう片方のコンポーネントを取り込み(import)するとき、importする側を「親」,importされる側を「子」と呼ぶ。そして、親から子へデータを伝達する手段を「props」。子から親へデータを伝達する手段を「emit」としている。 propsの使い方(親から子へデータを送る) V-bind(:)を経由して親から子へデータを送信して、子でpropsを使ってプロパティという形で親からのデータを受け取って利用できる。 親からv-bindでデータを子へ送る Parents.vue <template> //親で扱ってるデータをバインドしてそのプロパティとして子へデータ送る <Child :param0=“item” :param1=“value” /> <template> <script> import Child from './components/Child.vue' components: { Child }, </script> 子でprops内のプロパティをという形で親が飛ばしたデータを受け取ってdataのように利用できる Child.vue //親から送られてきたデータを受け取りdataのように使う props: [“item”, “value”] emitの使い方(子から親へデータ送る) emitを使ったカスタムイベントで子から親へデータが渡される。 カスタムイベントとは、子コンポーネント内「@click」のイベントがクリックされると同時に発火する$emitを使用したイベントのこと。子コンポーネント内で親に送りたいデータを引数にした「$emit」が発火すると、親でそのイベントが違う名前のmethodsに渡されて、そのmethodsが発動する。つまり、子で発火してその引数がデータとして親に渡されて親のmethods内で関数として使われる。 子から親へデータを渡すには、@clickや@changeなどのなにかしらのイベントが必要になってくる。 Child.vue <template> <div> <p>child_num: {{ child_num }}</p>         //@clickイベントでmethodsのsendが発動 <button @click='send'>親に値を渡す</button> </div> </template> <script> export default { data: function() { return { child_num: 0 }; }, methods: {         //関数内のカスタムメソッド$emitが発火し、引数として親に送りたいデータを渡すことができる send() { this.$emit("my-click", this.child_num); } } }; </script> </script> Parents.vue <template> <div> <p>parent_num: {{ parent_num }}</p>         //子のカスタムメソッドを親の関数として使う <Child @my-click='reflectNum'/> </div> </template> <script> import Child from './components/Child.vue' export default { data: function() { return { parent_num: 100 } }, components: { Child }, methods: {         //子のカスタムイベントで引数にしたデータはこちらで受け取る(子から親へデータ渡す) reflectNum(value) { this.parent_num = value } } } </script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

fetchなどでダブルクオーテーション囲みの数値が入ってきた場合の対処方法

問題 fetchで返されるレスポンスに"123"などのダブルクオーテーションが付いた数値が帰ってくる。 APIドキュメント上は、数値と定義されているが、””囲みなので実際の演算時や.toFixedしたい場合などに文字型となりエラーとなる。 数値であることはAPIドキュメント上、保証されているとき、どうすれば簡単に数値型へ変えられるか。 対応 単項プラスを使う。 const numericString = "10"; const numericValue = 10; // 通常は文字列結合 console.log(`numericString + numericValue: ${numericString + numericValue}`); // 単項プラスをあらかじめすることで console.log(`+numericString + numericValue: ${+numericString + numericValue}`); [LOG]: "numericString + numericValue: 1010" [LOG]: "+numericString + numericValue: 20"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

serveしたときの「http://localhost:5000」と「http://192.168.1.37:5000」は同じではない。

パソコンのカメラ映像を表示するJavaScriptをChromeで表示させるとConsoleにこんなエラーが出ました。 で、調べてみると以下とのこと。 Chromeなどのブラウザは、内蔵マイクによる録音や録画などのプログラムをブラウザ上で動作させる際は、SSL化されたサイトでないとパソコンやスマホに内蔵されたカメラやマイク等にアクセスできない仕様となっている ふむ。じゃあSSLしたサイトに乗せて・・・いや、作ってる途中までは問題なくカメラの映像取ってきた動作してましたやん? ここで怪しいなと思ったのがこれ。serveしたときのURL。 特に違いを意識してなかったのですが、「http://localhost:5000 」だとカメラ映像は問題なく映って、「http://192.168.1.37:5000 」だと上記のエラーが出てカメラ映像が出ませんでした。 「Local」と「On YourNetwork」と書かれているので、後者はネットワーク越し扱いで上述のChromeのSSL必須条件でエラーになると推測。 こういうのにハマるのは初心者だけかもしれませんが、お気をつけください・・・。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript 半角数字が入力されたことを正規表現で確認

したいこと 入力された値がすべて半角数字であることを、 正規表現を使って確認したい。 結論 数値である入力値を一度文字列に変換し、match()で比較する。 以下は失敗例と成功例 使用する値 半角数字のみであることを確認する正規表現:/^\d+$/   (行頭から行末まで半角数字のみであるということ) 1. 失敗1 正規表現はイコールでの比較ができない。 // 比較する関数(入力値が半角数字である場合1, それ以外の場合は0を返す) function sample(value){ if(value == /^\d+$/){return 1}else{return 0} } // `1234`を入力してチェック sample(1234) 入力値は半角数字のみだが、返ってくる値は0。 一致しているとみなされていない。 2. 失敗2 数値はmatch()で比較することができない // 比較する関数(入力値が半角数字である場合1, それ以外の場合は0を返す) function sample(value){ if(value.match(/^\d+$/)){return 1}else{return 0} } // `1234`を入力してチェック sample(1234) Uncaught TypeError: value.match is not a functionというエラーが発生。 ※今回の入力値は数値なのでエラーが出たが、入力値が文字列の場合はこの書き方でOK。 3. 成功 数値は文字列に変換してからmatchで比較する // 比較する関数(入力値が半角数字である場合1, それ以外の場合は0を返す) function sample(value){ if(String(value).match(/^\d+$/)){return 1}else{return 0} } // `1234`を入力してチェック sample(1234) 返ってくる値は1。比較ができた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

three.jsで隕石を飛ばしてみる

概要 three.jsで隕石(っぽい球体)が飛んでいくところを表現してみました。 隕石の簡単な飛ばし方 簡単なベクトルの計算で隕石を飛ばせます。ベクトルの計算もthree.jsが用意してくれているので非常に楽です。 手順1 隕石のstart位置とend位置を決めます。(画像はthree.jsの空間を真上から見下ろした図になります。) // 隕石の開始位置 var startMeteorVector = new THREE.Vector3(-100, 500, -9000); // 隕石の終了位置 var endMeteorVector = new THREE.Vector3(250, -400, 10000); 手順2 隕石の開始位置と終了位置から隕石の軌道を求めます。 // 隕石の軌道ベクトル var meteorVector = endMeteorVector.sub(startMeteorVector); 手順3 隕石の移動単位(単位ベクトル)を求めます。 // 隕石移動の基本となる単位ベクトル var unitMeteorVector = meteorVector.normalize(); 手順4 単位ベクトルだと移動が遅すぎるので、隕石が高速で飛んでくるように単位ベクトルを適度にスカラー倍します。 // 隕石の速度 var fastMeteorVector = unitMeteorVector.clone().multiplyScalar(200); var slowMeteorVector = unitMeteorVector.clone().multiplyScalar(1); 手順5 算出した移動速度を毎フレームごとに加算します。 meteor.position.x += fastMeteorVector.x; meteor.position.y += fastMeteorVector.y; meteor.position.z += fastMeteorVector.z; 隕石が飛んでくる動作のサンプル 遠くの方に見えている小さい黒い丸が隕石です。画面のどこかをクリックしたら隕石が高速で飛んできます。小技として、隕石がカメラの周辺に近づいたら一瞬動きがスローモーションになるようにしています。 See the Pen three.js meteor by tosi (@tosizo) on CodePen.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

第 8 章 何はなくともコンポーネント メモ

コンポーネントのメンタルモデル React ではコンポーネントをどう考えればいいか。これはよくいわれることだけど、 JavaScript の関数のようなものと考えるのが一番近い。 props を引数として受け取り、戻り値として React Elementsを返す関数 返されたReact Elementsがそのコンポーネントのレンダリング結果 になる。 ただしコンポーネントが通常の関数とちがうのは、個々に『状態』を持つことができるところ 関数コンポーネントも、 仮想 DOM の差分検出処理エンジンによって React Elements ごとに状態を保持する空間が用意される そのコンポーネントが持つ状態のことを state と呼ぶ props と state が同じである限りはその返す React Elements が変 わることはないんだけど、そのどちらか一方または両方が変わると返す React Elements も変わって くる。つまりコンポーネントのレンダリングに差分が発生する。 React の差 分検出処理エンジンは、仮想 DOM 内の React Elements すべてを監視していて、そのどれかの props またはその保持している state の値に差分を検出すると、そのコンポーネントのレンダリング処理を 再実行するようになってる。 コンポーネントと Props state というのは極力コンポーネントに持たせるべきではないもの? 純粋関数とは、引数が同じなら必ず同じ戻り値を返す関数のこと。 React にとって理想的 なコンポーネントとは、props が同じなら必ずレンダリング結果が同じになるコンポーネント props とはコンポーネントにとっての関数に対する引数のようなもの React に特有の概念で『properties(プロパティ)』を短くして props と呼んでる そして JSX が生成する React Elements を通してコールされたコンポーネント側では、それを { 属性名: 属性値 } の形式の props というオ ブジェクトとして受け取る。 受け取り方には 2 つ コンポーネントの実装が関数だった場合はその関数の第 1 引数として渡される クラスだった場合は初期化時にそのメンバーオブジェクト props として設定される import { VFC } from 'react'; import CharacterList, { Character } from './CharacterList'; import './App.css'; const App: VFC = () => { const characters: Character[] = [ { id: 1, name: '桜木花道', grade: 1, height: 189.2, }, { id: 2, name: '流川 楓', grade: 1, height: 187, }, { id: 3, name: '宮城リョータ', grade: 2, height: 168, }, { id: 4, name: '三井 寿', grade: 3, }, { id: 5, name: '赤木剛憲', grade: 3, height: 197, }, ]; return ( <div className="container"> <header> <h1>『SLAM DUNK』登場人物</h1> </header> <CharacterList school="湘北高校" characters={characters} /> </div> ); }; export default App; VFC これは VoidFunctionComponent インターフェー スのエイリアスで、FunctionComponent の関数の props からそのコンポーネントの子要素が格納される children を除いたものなの 従来の FC で定義された関数コンポーネントだと、子要素の操作が必要ない場合でも暗黙の内に props の中に子要素のオブジェクトが渡されてた。 import { VFC } from 'react'; import { Header, Icon, Item } from 'semantic-ui-react'; export type Character = { id: number; name: string; grade: number; height?: number; }; type Props = { school: string; characters: Character[]; }; const CharacterList: VFC<Props> = (props) => { const { school, characters } = props; return ( <> <Header as="h2">{school}</Header> <Item.Group> {characters.map((character) => ( <Item key={character.id}> <Icon name="user circle" size="huge" /> <Item.Content> <Item.Header>{character.name}</Item.Header> <Item.Meta>{character.grade}年生</Item.Meta> <Item.Meta> {character.height ? character.height : '???'} cm </Item.Meta> </Item.Content> </Item> ))} </Item.Group> </> ); }; export default CharacterList; Props という型エイリアスを定義しているところ。この型はコンポーネントを定義する関数宣言の型適用で使われてる こうやって FC に型引数を渡すことで、そのコンポーネントの props の型を指定できる props の型を設定することで、そのコンポーネントを JSX でマウントするときに必要な属性値と その型に縛りが発生する。だから App.tsx で をマウントするとき、school と chracter の属性値をそれぞれ Props で定義されている適正な型で記述しないと怒られてしまう 関数コンポーネントでは、レンダリングしたい内容を戻り値として return で返す <>...</> これはフラグメントといって React.Fragment のシンタックスシュガー でくくっても表示結果は同じなんだけど、そうすると HTML ソース に意味のない 階層ができてしまう。でもフラグメントにしておくとそれが避けられる た だ、 とちがって必ず中身のノードが必要なので、処理の結果、中身がなくなる可能性のある ときは使っちゃダメ Character 型の定義で height? と要素名にクエスチョンマーク これは『※その要素は省略できます』ってこと だから三井さんの height 値は設定されて ないのに型チェックで怒られてない。設定されてなければ参照値は undefined なので、三項演算子 で '???' という文字列が返ってる JSX で要素 をループ処理によって記述する場合、各要素にユニークな値を key 属性として設定しなければならない 仮想 DOM の差分検出処理で再レンダリングを効率的にするために必要 クラスコンポーネントで学ぶ State コンポーネントをクラスで表現する 今となってはクラスコンポーネントでしかできないことはほんのわずかしか残ってな いし、それも今後すべて関数コンポーネントでサポートされる予定になってる Facebook の React 開発チームが公式に推奨してるのも関数コンポーネント ・this の挙動が不可解で、そのためにコードが冗長になりがち ・minify やホットリローディング、さらに今後導入を検討しているコンポーネントの AOT コンパイルなどにおいて、クラスは最適化が困難で動作も不安定 ・ライフサイクルメソッドを用いると機能的に関連しているはずのコードがバラバラに記述され ることになり、可読性が落ちる ・状態を分離するのが難しく、ロジックを再利用するのが難しい クラスコンポーネントに State を持たせる 関数コンポーネントに state を持たせるためにはちょっとしたマジックが必要になるんだけど、ク ラスコンポーネントは簡単に state が持てる。props と同様、型引数に state の型を渡せば、メンバー 変数state からstateにアクセスできるようになる import { Component, ReactElement } from 'react'; import { Button, Card, Statistic } from 'semantic-ui-react'; import './App.css'; type State = { count: number }; class App extends Component<unknown, State> { constructor(props: unknown) { super(props); this.state = { count: 0 }; } reset(): void { this.setState({ count: 0 }); } increment(): void { this.setState((state) => ({ count: state.count + 1 })); } render(): ReactElement { const { count } = this.state; return ( <div className="container"> <header> <h1>カウンター</h1> </header> <Card> <Statistic className="number-board"> <Statistic.Label>count</Statistic.Label> <Statistic.Value>{count}</Statistic.Value> </Statistic> <Card.Content> <div className="ui two buttons"> <Button color="red" onClick={() => this.reset()}> Reset </Button> <Button color="green" onClick={() => this.increment()}> +1 </Button> </div> </Card.Content> </Card> </div> ); } } export default App; ひとつめは props の型で、このコンポーネントには props が必要ないので unknown を渡して る。デフォルト値は空オブジェクト {} なんだけど、{} の型は TypeScript の解釈では『null 以外の あらゆるオブジェクト』になってしまうため、@typescript-eslint/ban-typesのルールで使用が禁じ られてるの。だからプロパティを持てないオブジェクトの型である unknown がここではよりふさわしい Component に渡してる 2 つめの型引数だけど、これが state の型になる お約束としてスーパークラスに props を渡すのを忘れないように その下で定義しているメンバーメソッド reset と increment の中でその値を操作してる。 気をつけなきゃいけないのは、this.state の値を直接書き換えないこと。直接代入していいのはコ ンストラクタの中だけで、それ以外では値の設定には必ず setState メソッドを使うようにする setState メソッドの使い方について説明しておくと、そ の引数には次の 2 種類が設定できるようになってる i. state 内の変更したい要素名をキーに、値をその値にしたオブジェクト e.g. {count:0} ii. (prevState,props?)=>newState形式の、以前のstate(必要であればpropsも)を引数として 受け取って新しい state を返す関数 e.g. (state,props)=>({foo:state.foo+props.bar}) React ではイベントハンドラには通常、そのイベントが起きたときに実行したい関数を設定する この () => this.increment() というのは、引数を受け取らず increment メソッドを実行する無名関数 reset = (e: SyntheticEvent) => { e.preventDefault(); this.setState({ count: 0 }); }; increment = (e: SyntheticEvent) => { e.preventDefault(); this.setState((state) => ({ count: state.count + 1 })); }; React が提供している SyntheticEvent という型で定義されるイベントオブジェクト イベントハンドラのコールバックに引数として渡されるオブジェクトの型 たとえばこれが a要素だったりするとクリックで ページ移動が起きてしまうので、それをキャンセルするためにこういう記述が必要になる 他にも実用的な使い方だと、 要素で選択した値を受け取りたい場合は、その onChange 値に設定した関数内で同様にイベントハンドラ e を引数に定義しておけば、e.target.value から参照できたりする コンポーネントのライフサイクル コン ポーネントにおけるライフサイクルとは、まずマウントして初期化され、次にレンダリングされた後、 何らかのきっかけで再レンダリングされ、最後にアンマウントされるまでの過程をいう クラスコンポーネントにはライフサイクルの各フェーズに対応したライフサイクルメソッド (lifecycle methods)というものがあり、そこに必要な処理を登録しておける。 1. Mounting フェーズ ...... コンポーネントが初期化され、仮想 DOM にマウントされるまでの フェーズ。このフェーズで初めてコンポーネントがレンダリングされる 2. Updating フェーズ ...... 差分検出処理エンジンが変更を検知してコンポーネントが再レン ダリングされるフェーズ 3. Unmounting フェーズ ...... コンポーネントが仮想 DOM から削除されるフェーズ 4. Error Handling フェーズ ...... 子孫コンポーネントのエラーを検知、捕捉するフェーズ import { Component, ReactElement } from 'react'; import { Button, Card, Icon, Statistic } from 'semantic-ui-react'; import './App.css'; const LIMIT = 60; type State = { timeLeft: number }; class App extends Component<unknown, State> { timerId: NodeJS.Timer | null = null; constructor(props: unknown) { super(props); this.state = { timeLeft: LIMIT }; } componentDidMount = (): void => { this.timerId = setInterval(this.tick, 1000); }; componentDidUpdate = (): void => { const { timeLeft } = this.state; if (timeLeft === 0) this.reset(); }; componentWillUnmount = (): void => { if (this.timerId) clearInterval(this.timerId); }; tick = (): void => this.setState((prevState) => ({ timeLeft: prevState.timeLeft - 1 })); reset = (): void => this.setState({ timeLeft: LIMIT }); render = (): ReactElement => { const { timeLeft } = this.state; return ( <div className="container"> <header> <h1>タイマー</h1> </header> <Card> <Statistic className="number-board"> <Statistic.Label>time</Statistic.Label> <Statistic.Value>{timeLeft}</Statistic.Value> </Statistic> <Card.Content> <Button color="red" fluid onClick={this.reset}> <Icon name="redo" /> Reset </Button> </Card.Content> </Card> </div> ); }; } export default App; setInterval()というのは JavaScript の組み込み関数で、第 1 引数の関数を第 2 引数のミリ秒ご と延々と実行し続けるようにするもの ライフサイクルメソッドはたくさんあるけど、使う機会があるのはほぼ下記 componentDidMount, shouldComponentUpdate, componentDidUpdate, componentWillUnmount の 4 つくらい Presentational Component と Container Component ひとつのコンポーネントを『Presentational Component』お よび『Container Component』というものの 2 種類に分割しようというデザインパターンがある presentational component というのは『presentational(表現に関する)』の名前のとおり、純粋に見た目だけを責務とするコンポーネントのこと。 container component というのはそれをコンテナのごとく抱合してロジックを追加するためのコンポーネントのこと React の公式ドキュメントには『React の流儀(Thinking in React)』という章があって、そこで 公式が推奨するコンポーネントの正しい作り方が説明されてる 1. デザインモックから始め、その UI をコンポーネントの構造に分解して落とし込む 2. ロジックを除外した、静的に動作するバージョンを作成する 3. UI を表現するために最低限必要な「状態」を特定する 4. 3 の「状態」をどこに配置すべきかを決める 5. 階層構造を逆のぼって考え、データが上階層から流れてくるようにする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gatsby v2からv3へアップグレードするためにいろいろしたよ!

はじめに こんにちは。新しいPCほしいのですが、chromebookで仕事できるのかちょっと検討中です。昔はストレージなかったですが、最近はストレージあるんすねー!vscodeとかも使えるしLinux入れられるのであれば、行けるのか... さて、今回はGatsbyのv3が出てたので、アップグレードしました! ただ、いろいろこねくり回してやっとアップグレードできたので、その軌跡を記事にしようと思います。 これからやるぞという方の参考になれば幸いです! 本編 1. gatsbyの最新版をinstallします。 $ npm install gatsby@latest ここで、npmのバージョンが6のときは、以下のようにオプション付けないとダメでした。 $ npm install gatsby@latest --legacy-peer-deps 2. 一旦buildできるか試してみた. 案の定、すごい怒られる...。 $ gatsby develop ERROR Unexpected key "pageData" found in preloadedState argument passed to createStore. Expected to find one of the known reducer keys instead: "nodes", "logs", "pages", "redirects", "schema", "definitions", "staticQueryComponents", "status", "webpack", "webpackCompilationHash", "config", "lastAction", "jobsV2", "pageDataStats", "components", "babelrc", "jobs", "nodesByType", "program", "resolvedNodesCache", "nodesTouched", "flattenedPlugins", "pendingPageDataWrites", "schemaCustomization", "inferenceMetadata", "staticQueriesByTemplate", "queries", "visitedPages", "html", "functions". Unexpected keys will be ignored. ERROR #98123 WEBPACK Generating development JavaScript bundle failed Module build failed (from ./node_modules/postcss-loader/dist/cjs.js): Error: PostCSS plugin postcss-flexbugs-fixes requires PostCSS 8. Migration guide for end-users: https://github.com/postcss/postcss/wiki/PostCSS-8-for-end-users ...(省略) ERROR #98123 WEBPACK Generating development JavaScript bundle failed Module build failed (from ./node_modules/postcss-loader/dist/cjs.js): Error: PostCSS plugin postcss-flexbugs-fixes requires PostCSS 8. Migration guide for end-users: https://github.com/postcss/postcss/wiki/PostCSS-8-for-end-users ...(省略) ERROR #98123 WEBPACK Generating development JavaScript bundle failed ESLint is not a constructor failed Building development bundle - 6.846s ERROR in ./.cache/blank.css Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js): ModuleBuildError: Module build failed (from ./node_modules/postcss-loader/dist/cjs.js): Error: PostCSS plugin postcss-flexbugs-fixes requires PostCSS 8. Migration guide for end-users: https://github.com/postcss/postcss/wiki/PostCSS-8-for-end-users ...(省略) ERROR in ./node_modules/prismjs/themes/prism.css Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js): ModuleBuildError: Module build failed (from ./node_modules/postcss-loader/dist/cjs.js): Error: PostCSS plugin postcss-flexbugs-fixes requires PostCSS 8. Migration guide for end-users: https://github.com/postcss/postcss/wiki/PostCSS-8-for-end-users ...(省略) 3. 古いnode modulesを確認してみた. $ npm outdated Package Current Wanted Latest Location @emotion/core 10.1.1 10.1.1 11.0.0 node_modules/@emotion/core @emotion/styled 10.0.27 10.0.27 11.3.0 node_modules/@emotion/styled chalk 3.0.0 3.0.0 4.1.1 node_modules/chalk contentful-import 7.9.42 7.9.42 8.1.13 node_modules/contentful-import eslint 6.8.0 6.8.0 7.27.0 node_modules/eslint eslint-config-gatsby-standard 2.2.0 2.2.0 3.0.2 node_modules/eslint-config-gatsby-standard gatsby-image 2.11.0 2.11.0 3.6.0 node_modules/gatsby-image gatsby-plugin-catch-links 2.10.0 2.10.0 3.6.0 node_modules/gatsby-plugin-catch-links gatsby-plugin-emotion 4.5.0 4.5.0 6.6.0 node_modules/gatsby-plugin-emotion gatsby-plugin-google-analytics 2.11.0 2.11.0 3.6.0 node_modules/gatsby-plugin-google-analytics gatsby-plugin-instagram-embed 2.1.0 2.1.0 3.0.0 node_modules/gatsby-plugin-instagram-embed gatsby-plugin-manifest 2.12.1 2.12.1 3.6.0 node_modules/gatsby-plugin-manifest gatsby-plugin-netlify 2.11.1 2.11.1 3.6.1 node_modules/gatsby-plugin-netlify gatsby-plugin-offline 3.10.2 3.10.2 4.6.0 node_modules/gatsby-plugin-offline gatsby-plugin-react-helmet 3.10.0 3.10.0 4.6.0 node_modules/gatsby-plugin-react-helmet gatsby-plugin-schema-snapshot 1.7.0 1.7.0 2.6.0 node_modules/gatsby-plugin-schema-snapshot gatsby-plugin-sitemap 2.12.0 2.12.0 4.2.0 node_modules/gatsby-plugin-sitemap gatsby-plugin-theme-ui 0.3.5 0.3.5 0.9.1 node_modules/gatsby-plugin-theme-ui gatsby-remark-autolink-headers 2.11.0 2.11.0 4.3.0 node_modules/gatsby-remark-autolink-headers gatsby-remark-images-contentful 2.10.0 2.10.0 4.3.0 node_modules/gatsby-remark-images-contentful gatsby-remark-prismjs 3.13.0 3.13.0 5.3.0 node_modules/gatsby-remark-prismjs gatsby-source-contentful 2.3.56 2.3.56 5.6.1 node_modules/gatsby-source-contentful gatsby-transformer-remark 2.16.1 2.16.1 4.3.0 node_modules/gatsby-transformer-remark inquirer 7.3.3 7.3.3 8.1.0 node_modules/inquirer prettier 1.19.1 1.19.1 2.3.0 node_modules/prettier react 16.14.0 16.14.0 17.0.2 node_modules/react react-dom 16.14.0 16.14.0 17.0.2 node_modules/react-dom react-helmet 5.2.1 5.2.1 6.1.0 node_modules/react-helmet stylelint-config-standard 19.0.0 19.0.0 22.0.0 node_modules/stylelint-config-standard theme-ui 0.3.5 0.3.5 0.9.1 node_modules/theme-ui おいおい、結構あるな 4. 古いnode modulesを最新にしてみた. 上で確認した古いnode modulesをすべて最新バージョンに上げます. $ npm install @emotion/styled@latest --legacy-peer-deps $ npm install chalk@latest --legacy-peer-deps $ npm install contentful-import@latest --legacy-peer-deps $ npm install eslint@latest --legacy-peer-deps $ npm install eslint-config-gatsby-standard@latest --legacy-peer-deps $ npm install gatsby-image@latest --legacy-peer-deps $ npm install gatsby-plugin-catch-links@latest --legacy-peer-deps $ npm install gatsby-plugin-emotion@latest --legacy-peer-deps $ npm install gatsby-plugin-google-analytics@latest --legacy-peer-deps $ npm install gatsby-plugin-instagram-embed@latest --legacy-peer-deps $ npm install gatsby-plugin-manifest@latest --legacy-peer-deps $ npm install gatsby-plugin-netlify@latest --legacy-peer-deps $ npm install gatsby-plugin-offline@latest --legacy-peer-deps $ npm install gatsby-plugin-react-helmet@latest --legacy-peer-deps $ npm install gatsby-plugin-schema-snapshot@latest --legacy-peer-deps $ npm install gatsby-plugin-sitemap@latest --legacy-peer-deps $ npm install gatsby-plugin-theme-ui@latest --legacy-peer-deps $ npm install gatsby-remark-autolink-headers@latest --legacy-peer-deps $ npm install gatsby-remark-images-contentful@latest --legacy-peer-deps $ npm install gatsby-remark-prismjs@latest --legacy-peer-deps $ npm install gatsby-source-contentful@latest --legacy-peer-deps $ npm install gatsby-transformer-remark@latest --legacy-peer-deps $ npm install inquirer@latest --legacy-peer-deps $ npm install prettier@latest --legacy-peer-deps $ npm install react@latest --legacy-peer-deps $ npm install react-dom@latest --legacy-peer-deps $ npm install react-helmet@latest --legacy-peer-deps $ npm install stylelint-config-standard@latest --legacy-peer-deps $ npm install theme-ui@latest --legacy-peer-deps 5. 一旦buildできるか試してみた. 案の定、怒られる...。 $ gatsby develop success open and validate gatsby-configs - 0.077s warn Plugin gatsby-remark-images-anywhere is not compatible with your gatsby version 3.6.2 - It requires gatsby@^2.12.0 ERROR #11331 PLUGIN Invalid plugin options for "gatsby-plugin-google-analytics": - "trackingId" is required not finished load plugins - 2.132s あれ、gatsby-plugin-google-analyticsのtrackingIdは、本番環境でしか環境変数を流し込んでないから開発で怒られている模様。いままではそれでもビルドできたのに、バージョン上がって必須になったのかな 6. 開発の環境変数にtrackingIdを追加して開発ビルド時に参照できるようにする. プロジェクトルートに、.envファイル作成. env.development GOOGLE_ANALYTICS=UA-000000000-0 gatsby-config.jsで.envを読み込む. gatsby-config.js require("dotenv").config({ path: `.env.${process.env.NODE_ENV}`, }) module.exports = { plugins: [ { resolve: `gatsby-plugin-google-analytics`, options: { trackingId: process.env.GOOGLE_ANALYTICS, }, }, ] } 7. 一旦buildできるか試してみた. 案の定、怒られる...。 $ gatsby develop ERROR #11321 PLUGIN "gatsby-node.js" threw an error while running the createPages lifecycle: Interface field ContentfulReference.contentful_id expects type String! but ContentfulAsset.contentful_id is type String. Interface field ContentfulReference.contentful_id expects type String! but ContentfulTag.contentful_id is type String. Interface field ContentfulEntry.contentful_id expects type String! but ContentfulTag.contentful_id is type String. Interface field ContentfulEntry.node_locale expects type String! but ContentfulTag.node_locale is type String. Interface field ContentfulReference.contentful_id expects type String! but ContentfulPost.contentful_id is type String. Interface field ContentfulEntry.contentful_id expects type String! but ContentfulPost.contentful_id is type String. Interface field ContentfulEntry.node_locale expects type String! but ContentfulPost.node_locale is type String. Interface field ContentfulReference.contentful_id expects type String! but ContentfulPage.contentful_id is type String. Interface field ContentfulEntry.contentful_id expects type String! but ContentfulPage.contentful_id is type String. Interface field ContentfulEntry.node_locale expects type String! but ContentfulPage.node_locale is type String. ん?:thiking: 型のタイプヒンティングが違うのか?null許容してほしいのかな。 8. 上記で言われたタイプヒンティングでnullを許容する. この辺の子たちだね。 contentful_id: String => contentful_id: String! にする。 変更前 root/src/gatsby/schema/schema.gql type ContentfulTag implements Node @derivedTypes @dontInfer { title: String slug: String post: [ContentfulPost] @link(by: "id", from: "post___NODE") spaceId: String contentful_id: String createdAt: Date @dateformat updatedAt: Date @dateformat sys: ContentfulTagSys node_locale: String } 変更後 root/src/gatsby/schema/schema.gql type ContentfulTag implements Node @derivedTypes @dontInfer { title: String slug: String post: [ContentfulPost] @link(by: "id", from: "post___NODE") spaceId: String contentful_id: String! createdAt: Date @dateformat updatedAt: Date @dateformat sys: ContentfulTagSys node_locale: String } 9. 一旦buildできるか試してみた. 案の定、怒られる...。 $ gatsby develop ERROR #11321 PLUGIN "gatsby-node.js" threw an error while running the createPages lifecycle: Interface field ContentfulEntry.node_locale expects type String! but ContentfulTag.node_locale is type String. Interface field ContentfulEntry.node_locale expects type String! but ContentfulPost.node_locale is type String. Interface field ContentfulEntry.node_locale expects type String! but ContentfulPage.node_locale is type String. あ、さっきと同じ感じで型のタイプヒンティングが違いそう。 10. 上記で言われたタイプヒンティングでnullを許容する. この辺の子たちだね。 node_locale: String => node_locale: String! にする。 変更前 root/src/gatsby/schema/schema.gql type ContentfulTag implements Node @derivedTypes @dontInfer { title: String slug: String post: [ContentfulPost] @link(by: "id", from: "post___NODE") spaceId: String contentful_id: String! createdAt: Date @dateformat updatedAt: Date @dateformat sys: ContentfulTagSys node_locale: String } 変更後 root/src/gatsby/schema/schema.gql type ContentfulTag implements Node @derivedTypes @dontInfer { title: String slug: String post: [ContentfulPost] @link(by: "id", from: "post___NODE") spaceId: String contentful_id: String! createdAt: Date @dateformat updatedAt: Date @dateformat sys: ContentfulTagSys node_locale: String } 11. 一旦buildできるか試してみた. 案の定、怒られる...。 $ gatsby develop ERROR #85923 GRAPHQL There was an error in your GraphQL query: Cannot query field "fluid" on type "ContentfulAsset". If you don't expect "fluid" to exist on the type "ContentfulAsset" it is most likely a typo. However, if you expect "fluid" to exist there are a couple of solutions to common problems: - If you added a new data source and/or changed something inside gatsby-node.js/gatsby-config.js, please try a restart of your development server - The field might be accessible in another subfield, please try your query in GraphiQL and use the GraphiQL explorer to see which fields you can query and what shape they have - You want to optionally use your field "fluid" and right now it is not used anywhere. Therefore Gatsby can't infer the type and add it to the GraphQL schema. A quick fix is to add at least one entry with that field ("dummy content") It is recommended to explicitly type your GraphQL schema if you want to use optional fields. This way you don't have to add the mentioned "dummy content". Visit our docs to learn how you can define the schema for "ContentfulAsset": https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization#creating-type-definitions typoしてるだと?そんなはず、だってこれで動いていたんだよ 12. あ、gatsby-imageじゃなくてgatsby-plugin-imageになったんですかい! こいつだ!! そんなわけで、gatsby-plugin-imageをインストールします。 $ npm install gatsby-plugin-image 13. 一旦buildできるか試してみた. 動いたー!!! $ gatsby develop success run page queries - 52.992s - 3/3 0.06/s ⠀ You can now view gatsby-starter-gcn in the browser. ⠀ http://localhost:8000/ ⠀ View GraphiQL, an in-browser IDE, to explore your site's data and schema ⠀ http://localhost:8000/___graphql ⠀ Note that the development build is not optimized. To create a production build, use gatsby build 14. バージョン確認 $ gatsby -v Gatsby CLI version: 3.6.0 Gatsby version: 3.6.2 おお!v3に上がってますー!サイトも動いてますー!素敵 おわりに 1つずつエラーを眺めながらポチポチしてバージョン対応しました。 詳細は時間があるときにはちゃんと調べようと思います。 取り急ぎ、対応しないとで困っている方の参考になれば幸いです。 それでは!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vueのelectronでzoomとかのURLを開けるようにしたい

最近vueいじってねぇ なんかelectronでも使って作ってみるかー vueって最初の初期設定がめんどくさいイメージ 自分の備忘録なので文章とか気にせずいきましょ やっていきましょ vueのcreateから vue create test \\ターミナルで実行 Vue3を選択 なんか、前は色々とmanualyで選んでたんだけど、 めんどくさそうなので、vue3をとりあえず選択 ? Please pick a preset: Default (Vue 3 Preview) ([Vue 3] babel, eslint) electronで実行させたいので、electron-builderを追加 cd test/ vue add electron-builder Choose Electron Version ^12.0.0 さぁ、createが完了したのでとりあえず動かしてみましょう yarn electron:serve よし、とりあえずelectronアプリは立ち上がったね プルダウンリストを作っていくよ App.vueを変えて、そこに書いていこう それ以外は消しましょー App.vue <template> <div class="home"> <select v-model="selectedFruits"> <option disabled value="">果物一覧</option> <option v-for="fruit in optionFruits" v-bind:value="fruit.name" v-bind:key="fruit.id" > {{ fruit.name }} </option> </select> </div> </template> <script> // @ is an alias to /src export default { data() { return { selectedFruits: "", optionFruits: [ { id: 1, name: "バナナ" }, { id: 2, name: "りんご" }, { id: 3, name: "みかん" }, ], }; }, }; </script> とりあえず、プルダウンメニューだけ作れたね URLを入れてzoomを開けるようにしよう じゃあ、ここにURLを入れて、選択された名前のURLをボタンで開けるようにしていきましょう ついでにclassroomも開けるようにしておこうか <template> <div class="app"> <select v-model="subjectedSub"> <option disabled value="">教科一覧</option> <option v-for="subject in optionSubjects" v-bind:value="subject.id" v-bind:key="subject.id" > {{ subject.name }} </option> </select> <div> <button v-on:click="openZoom(subjectedSub)">zoom</button><br /> <button v-on:click="openClassroom(subjectedSub)">classroom</button ><br /><br /><br /> </div> </div> </template> <script> // @ is an alias to /src export default { data() { return { subjectedSub: "", optionSubjects: [ { id: 1, name: "教科名", zoom: "url", \\ここにURLを入れる classroom: "url", }, { id: 2, name: "教科名", zoom: "url", classroom: "url", }, { id: 3, name: "教科名", zoom: "url", classroom: "", }, ], nowSubject: "", }; }, methods: { openZoom(id) { window.location.href = this.optionSubjects[id - 1].zoom; }, openClassroom(id) { window.location.href = this.optionSubjects[id - 1].classroom; }, }, }; </script> とりあえず、こんな感じで3教科から選べるようにできたよ 参考にした記事 Vue.js で プルダウンメニューの作り方 (基礎編) Nuxt.jsで外部サイト(URL)へ遷移する方法 [ElectronでWebviewの簡易ブラウザをつくってみたメモ]https://qiita.com/mamosan/items/084039c3e6d703b7b45f background.jsでelectronアプリのサイズを変更しよう widthとheightで変更できるね const win = new BrowserWindow({ width: 300, height: 150, webPreferences: { // Use pluginOptions.nodeIntegration, leave this alone // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION, contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION, }, show: true, "always-on-top": true, \\常に上に開いてくれるやつらしい }); buildしてみよう ついにbuildするよ アイコンとか、特に設定していないからtestでそのままだけど yarn electron:build そうすると同じフォルダの中にdist_electronができたね この中のtest.dmgを解凍すればアプリが開けるよ 結果 うんとりあえず、できてるねぇ ウィンドウちっさいかな笑 あ、でもclassroomを開くのが、electron上になっちゃうので、そこはまた改良します まぁ、まだ改良の余地はたくさんあるね でわ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IndexedDB

indexedDB データベースを使用しなくてもjavascriptで管理できる __ か ** で囲むとHTMLのstrongタグになります。Qiitaでは太字になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ml5.jsのImage Classifierをブラウザでアップした画像に対して実行する

ブラウザ上で手軽に機械学習もでるを扱えるml5.jsを使って、ローカルからアップした画像を認識させるための手順。 ソース <html> <head> <meta charset="UTF-8"> <title>画像をアップロードすると画像認識してくれるページ</title> <script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script> <style></style> </head> <body> <h1>画像をアップロードすると画像認識してくれるページ</h1> <p id="label">label:</p> <p id="confidence">confidence:</p> <div class="upload"><input type="file" name="file" id="file"></div> <canvas id="canvas"></canvas> <script> let file = document.getElementById('file'); let canvas = document.getElementById('canvas'); let canvasWidth = 400; let canvasHeight = 300; let uploadImgSrc; let label = document.querySelector("#label"); let confidence = document.querySelector("#confidence"); // 作成したモデルのURL const imageModelURL = 'Teachable Machineで作成したモデルのURL'; // Canvasの準備 canvas.width = canvasWidth; canvas.height = canvasHeight; let ctx = canvas.getContext('2d'); let classifier = ml5.imageClassifier(imageModelURL + 'model.json', () => { // ロード完了 console.log('Model Loaded!'); }); //画像を読み込む関数 function loadLocalImage(e) { // ファイル情報を取得 let fileData = e.target.files[0]; // 画像ファイル以外は処理を止める if (!fileData.type.match('image.*')) { alert('画像を選択してください'); return; } // FileReaderオブジェクトを使ってファイル読み込み let reader = new FileReader(); // ファイル読み込みに成功したときの処理 reader.onload = function () { // Canvas上に表示する uploadImgSrc = reader.result; canvasDraw(); } // ファイル読み込みを実行 reader.readAsDataURL(fileData); } // ファイルが指定された時にloadLocalImage()を実行 file.addEventListener('change', loadLocalImage, false); // Canvas上に画像を表示する関数 function canvasDraw() { // canvas内の要素をクリアする ctx.clearRect(0, 0, canvasWidth, canvasHeight); // Canvas上に画像を表示 let img = new Image(); img.src = uploadImgSrc; img.onload = function () { if (this.width / this.height > canvasWidth / canvasHeight) { // 幅に合わせて画像サイズ設定 var imgWidth = canvasWidth; var imgHeight = Math.floor(this.height * (canvasWidth / this.width)); } else { // 高さに合わせて画像サイズ設定 imgHeight = canvasHeight; imgWidth = Math.floor(this.width * (canvasHeight / this.height)); } ctx.drawImage(img, 0, 0, imgWidth, imgHeight); } classifyCanvas(); } function classifyCanvas() { classifier.classify(canvas, gotResult); } // 画像認識結果を表示する関数 function gotResult(error, results) { if (error) { console.error(error); } console.log(results); // 結果のラベルと信頼度を表示 label.textContent = `Label: ${results[0].label}`; confidence.textContent = `Confidence: ${results[0].confidence.toFixed(4)}`; } </script> </body> </html> モデルの準備 モデルのURLを指定してコールバック関数を呼び出すだけです。 // 作成したモデルのURL const imageModelURL = 'Teachable Machineで作成したモデルのURL'; let classifier = ml5.imageClassifier(imageModelURL + 'model.json', () => { // ロード完了 console.log('Model Loaded!'); }); イメージのアップロード HTML要素としてinputをtype="file"で利用 <input type="file" name="file" id="file"> アップロード処理は以下の部分 function loadLocalImage(e) { // ファイル情報を取得 let fileData = e.target.files[0]; // 画像ファイル以外は処理を止める if (!fileData.type.match('image.*')) { alert('画像を選択してください'); return; } // FileReaderオブジェクトを使ってファイル読み込み let reader = new FileReader(); // ファイル読み込みに成功したときの処理 reader.onload = function () { // Canvas上に表示する uploadImgSrc = reader.result; canvasDraw(); } // ファイル読み込みを実行 reader.readAsDataURL(fileData); } // ファイルが指定された時にloadLocalImage()を実行 file.addEventListener('change', loadLocalImage, false); Canvasへの描画 画像ファイルを読み込むと以下の関数が実行される function canvasDraw() { // canvas内の要素をクリアする ctx.clearRect(0, 0, canvasWidth, canvasHeight); // Canvas上に画像を表示 let img = new Image(); img.src = uploadImgSrc; img.onload = function () { if (this.width / this.height > canvasWidth / canvasHeight) { // 幅に合わせて画像サイズ設定 var imgWidth = canvasWidth; var imgHeight = Math.floor(this.height * (canvasWidth / this.width)); } else { // 高さに合わせて画像サイズ設定 imgHeight = canvasHeight; imgWidth = Math.floor(this.width * (canvasHeight / this.height)); } ctx.drawImage(img, 0, 0, imgWidth, imgHeight); } classifyCanvas(); } 最初に宣言したcanvasのサイズ(今回は横400,縦300)に画像側をリサイズして描画している。 画像認識処理の実行 先ほどの描画関数の最後でclassifyCanvas()を呼んでいる。 認識処理完了したらコールバック関数で結果を画面表示。 function classifyCanvas() { classifier.classify(canvas, gotResult); } // 画像認識結果を表示する関数 function gotResult(error, results) { if (error) { console.error(error); } console.log(results); // 結果のラベルと信頼度を表示 label.textContent = `Label: ${results[0].label}`; confidence.textContent = `Confidence: ${results[0].confidence.toFixed(4)}`; }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む