20210418のJavaScriptに関する記事は30件です。

three.jsで物体が衝突したことを検知する

概要 three.jsで物体が衝突したことを検知したくて色々ググって実践してみたんですが、どうもうまくいきませんでした。英語のサイトなども探してまわって最終的に以下で説明するようなコードで実現できたのですが、どうもすっきりしない…謎のあるコードになってしましました。とりあえず現時点でのコードを備忘録として載せます。 衝突検知のサンプル 二つの球体があり、一つの球体がもう一つの球体に衝突したら停止するという非常にシンプルな動作です。intersectsSphereを使って衝突を検知しています。動作自体は想定通りの動作です。 See the Pen three.js intersectsSphere by tosi (@tosizo) on CodePen. 個人的に謎の箇所 私のthree.jsの知識が足りてないからだと思いますが、以下の点が謎でした。 sphere.geometry.computeBoundingSphere(); // 球体(緑)用のBoundingSphere var sphereBBox = new THREE.Sphere(sphere.position, sphere.geometry.boundingSphere.radius); 衝突検知用のBoundingSphereを作成しているんですが、そもそもcomputeBoundingSphere()によってsphere.geometryにboundingSphereオブジェクトが生成されているんですよね。以下のような感じ。 sphere.geometry.computeBoundingSphere(); // sphere.geometry.boundingSphereが生成される // 但し、このオブジェクトで衝突検知しようとしてもうまくいかない? // 球体(緑)用のBoundingSphere ← 結局これが必要になった var sphereBBox = new THREE.Sphere(sphere.position, sphere.geometry.boundingSphere.radius); sphere.geometry.boundingSphereが生成されているのだからこれで衝突判定すればいいように思うんですがどうしてもうまくいかない。(厳密にはboundingSphereの位置をうまくセットできなかった。)結局、判定用にSphereを作成してradiusの値などをコピーすることでうまくいきました。 初歩的な何かを見逃しているような気もしますので、判明したら追記していきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gatsby.jsを一週間勉強して分かったこと

皆さんReactのフレームワークGatsby.jsをご存じでしょうか? 恐らくReactのフレームワークと言ったらNext.jsが代表かと思われます。 そこで今回は、Gatsby.jsを一週間勉強してGatsby.js良いところ悪いところ完全な主観でまとめてみたいと思います。 もう一回言います。 主観でまとめます。 悪魔で僕個人の意見なので、鵜呑みにはしないようお願いします。 こんな感じなんだと思って頂ければ十分です。 それでは早速良いところをまとめていきます。 良いところ ルーティングの設定が不要 まず、1つ目はルーティングについてです。 Reactでルーティングを行う時って結構面倒ですよね。 必要なパッケージをインストールしたり、あとはpushメソッドとかをstoreで管理したり色々と慣れるまでは面倒。 Gatsby.jsではそれが不要です! Gatsby.jsはデフォルトでsrc\pagesというフォルダがあります。 ここがルーティングそのものになります。 どういうことか。 例えば、http://localhost:8000/contact/inputというパスはsrc/pages/contact/input.jsを参照するということです! 要するにルーティングを行う場合は、src/pages配下にあるファイル・フォルダそのものがルーティングになっているわけです! わざわざ設定しなくても勝手にやってくれる! 僕にとってはこれが一番良い点でした。 ページ遷移 ページ遷移を行う場合もGatsbyではデフォルトで用意されています。 わざわざreact-routerやreact-router-domをインストールする必要がありません。 以下に例を挙げておきます。 import React from "react" import { Link } from "gatsby" const Example = (props) => { return ( <> <Link to="/contact/input">お問い合わせフォーム</Link> </> ) } export default Example メソッドとしてページ遷移を行う際もnavigate()というメソッドがもともと用意されています。 これはpushメソッドと同じ動きをします。 import React from "react" import { navigate } from 'gatsby-link' const Example = (props) => { return ( <> <Button variant="outlined" onClick={() => navigate('/contact/input')}> お問い合わせフォーム </Button> </> ) } export default Example こんな感じでルーティングに関しての機能がデフォルトで用意されているのが最高でした。 悪い点 ヘッダーやフッターコンポーネントはページごとにレンダリング ReactやVueでヘッダーやフッターなどのコンポーネントを使用する際、メインとなるファイルに<Header />などを記述すことで各ファイルにこれらを書かなくて済むという仕組みですが、Gatsbyではこれができません。 なぜかは知りません。 もしかしたら出来るかもしれないです。 なので、マイページ使うヘッダーやフッターはlayout.jsにまとめてそれを毎回呼び出すというのが定石らしいです。 Layoutの使い方は公式のドキュメントに詳しく書いてあるのでリンクを貼っておきますね。 https://www.gatsbyjs.com/docs/how-to/routing/layout-components/ SPAならこの問題を解決してほしいと願っております。 まとめ 結論、良い点は ルーティングの設定が不要 ページ遷移の機能がデフォルトで備わっている で、悪い点は 毎回使うコンポーネントは毎回レンダリングを行わなければいけない です! まだ1週間しか勉強していないので、もっと良い点、悪い点があると思います。 学習を進めていくにあたってまた記事として書いていこうかなと思います。 引き続き勉強頑張ります。 Thank you for reading
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptメモその1

文字を出力する(表示) 「console.log("○○");」というコードを書くと、( )の中の○○という文字がコンソールに出力される。 末尾のセミコロン( ; )を忘れないようにする。 console.log("Hello World"); 結果 Hello World コメントアウト 文頭に『//』を書くとその行はコメントとみなされる。 //コメントアウト 結果 ※コメントは出力されない 文字列と数列 『"5 + 2"』のようにクォーテーションをつけると文字列と解釈されそのまま表示される console.log(5 + 2); console.log("5 + 2"); console.log("3" + "5"); 結果 7 5 + 2 35 変数 変数は『let 変数名 = 値』と定義する。 letは変数の宣言、nameは変数名、"jhon"は代入する値となる。 let name = "john"; console.log(name); console.log("name"); console.log("name" + name); 結果 john name namejohn 変数の更新 『変数名 = 新しい値』と書くと値が更新される。 let name = "john"; console.log(name); name = "Kate"; console.log(name); 結果 john Kate 変数自身の更新 例 変数numberに3を足す。 let number = 2; console.log(number); number = number + 3; console.log(number); 結果 2 5 更新省略形 x = x + 10 → x += 10と省略できる。 - * / % も同じように省略できる。 let number = 2; console.log(number); number += 3; console.log(number); 結果 2 5 定数 変数と違い定数は値を更新することはできない。 const name = "john"; console.log(name); 結果 john テンプレートリテラル テンプレートリテラルを用いることで、文字列の中に定数(変数)を埋め込むことができる。 文字列全体をバッククォーテーション( ` )で囲む必要がある。 const name = "たかし"; console.log(`こんにちは、${name}さん`); 結果 こんにちは、たかしさん if文 if文を用いることで『もしも〇〇ならば※※を行う』という条件分岐ができる。 const number = 12; if (number > 10) { console.log("numberは10より大きいです"); } 結果 numberは10より大きいです 比較演算子1 const number = 12; console.log(number < 30); console.log(number <= 12); console.log(number > 12); console.log(number >= 12); 結果 true true false true 比較演算子2 const number = 12; console.log(number === 12); const name = "john"; console.log(name !== "jhon"); 結果 true false else if文が成り立たない時の処理を行うときに用いる。 『もしも〇〇ならば※※を行う、そうでなければ□□を行う』という処理ができる。 const age = 17; if (age >= 20) { console.log("私は20歳以上です"); } else { console.log("私は20歳未満です"); } 結果 私は20歳未満です else if elseよりもさらに条件を増やしたい場合にelse ifを用いる。 const age = 17; if (age >= 20) { console.log("私は20歳以上です"); } else if (age >= 10) { console.log("私は20歳未満ですが、10歳以上です"); } else { console.log("私は10歳未満です"); } 結果  私は20歳未満ですが、10歳以上です 条件式 『かつ』は『&&』で表し、『または』は『||』で表す const age = 24; if (age >= 20 && age < 30) { console.log("私は20代です"); } 結果 私は20代です switch文 switch文はある値によって処理を分岐する場合に用いる。 switch文ではbreakが重要でありbreakがないと合致したcaseの処理を行ったあと、その次のcaseの処理も実行されてしまう。 const color = "赤"; switch (color) { case "赤": console.log("ストップ!"); break; case "黄": console.log("要注意"); break; } 結果 ストップ! switch文 - default switch文の条件の値がcaseのどれにも一致しなかった時、defaultのブロックが実行される。 const color = "黒"; switch (color) { case "赤": console.log("ストップ!"); break; case "黄": console.log("要注意"); break; default: console.log("colorの値が正しくありません"); break; } 結果 colorの値が正しくありません
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript基礎メモその1

文字を出力する(表示) 「console.log("○○");」というコードを書くと、( )の中の○○という文字がコンソールに出力される。 末尾のセミコロン( ; )を忘れないようにする。 console.log("Hello World"); 結果 Hello World コメントアウト 文頭に『//』を書くとその行はコメントとみなされる。 //コメントアウト 結果 ※コメントは出力されない 文字列と数列 『"5 + 2"』のようにクォーテーションをつけると文字列と解釈されそのまま表示される console.log(5 + 2); console.log("5 + 2"); console.log("3" + "5"); 結果 7 5 + 2 35 変数 変数は『let 変数名 = 値』と定義する。 letは変数の宣言、nameは変数名、"jhon"は代入する値となる。 let name = "john"; console.log(name); console.log("name"); console.log("name" + name); 結果 john name namejohn 変数の更新 『変数名 = 新しい値』と書くと値が更新される。 let name = "john"; console.log(name); name = "Kate"; console.log(name); 結果 john Kate 変数自身の更新 例 変数numberに3を足す。 let number = 2; console.log(number); number = number + 3; console.log(number); 結果 2 5 更新省略形 x = x + 10 → x += 10と省略できる。 - * / % も同じように省略できる。 let number = 2; console.log(number); number += 3; console.log(number); 結果 2 5 定数 変数と違い定数は値を更新することはできない。 const name = "john"; console.log(name); 結果 john テンプレートリテラル テンプレートリテラルを用いることで、文字列の中に定数(変数)を埋め込むことができる。 文字列全体をバッククォーテーション( ` )で囲む必要がある。 const name = "たかし"; console.log(`こんにちは、${name}さん`); 結果 こんにちは、たかしさん if文 if文を用いることで『もしも〇〇ならば※※を行う』という条件分岐ができる。 const number = 12; if (number > 10) { console.log("numberは10より大きいです"); } 結果 numberは10より大きいです 比較演算子1 const number = 12; console.log(number < 30); console.log(number <= 12); console.log(number > 12); console.log(number >= 12); 結果 true true false true 比較演算子2 const number = 12; console.log(number === 12); const name = "john"; console.log(name !== "jhon"); 結果 true false else if文が成り立たない時の処理を行うときに用いる。 『もしも〇〇ならば※※を行う、そうでなければ□□を行う』という処理ができる。 const age = 17; if (age >= 20) { console.log("私は20歳以上です"); } else { console.log("私は20歳未満です"); } 結果 私は20歳未満です else if elseよりもさらに条件を増やしたい場合にelse ifを用いる。 const age = 17; if (age >= 20) { console.log("私は20歳以上です"); } else if (age >= 10) { console.log("私は20歳未満ですが、10歳以上です"); } else { console.log("私は10歳未満です"); } 結果  私は20歳未満ですが、10歳以上です 条件式 『かつ』は『&&』で表し、『または』は『||』で表す const age = 24; if (age >= 20 && age < 30) { console.log("私は20代です"); } 結果 私は20代です switch文 switch文はある値によって処理を分岐する場合に用いる。 switch文ではbreakが重要でありbreakがないと合致したcaseの処理を行ったあと、その次のcaseの処理も実行されてしまう。 const color = "赤"; switch (color) { case "赤": console.log("ストップ!"); break; case "黄": console.log("要注意"); break; } 結果 ストップ! switch文 - default switch文の条件の値がcaseのどれにも一致しなかった時、defaultのブロックが実行される。 const color = "黒"; switch (color) { case "赤": console.log("ストップ!"); break; case "黄": console.log("要注意"); break; default: console.log("colorの値が正しくありません"); break; } 結果 colorの値が正しくありません
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TensorFlow.js学習メモ② 線形回帰(Linear Regression)で車の燃費を予測

はじめに k近傍法に引き続き、線形回帰モデルを使って車の燃費を予測してみました。 学習メモなので基本用語の詳しい解説などは書いていません。あしからず。 前の記事は以下 前提知識 実装に際して前提となる超基本知識をまとめました。 線形回帰 (Linear regression) ある変数が与えられたときに、それと相関関係のある値を予測することを回帰分析と呼びます。 多数のデータをプロットし、直線を引いてモデルをつくることで、変数の相関値を予測できるようになります。 つまり、予測の精度をできるだけ高くできるように直線を引くこと(= 適切な傾きと切片を与えること)がとても大事になります。 平均二乗誤差(Mean Square Error) 平均二乗誤差とは、線形回帰モデルの性能を数値化する効果的な手法の一つで、実際の値とモデルによる予測値との誤差の平均値のことをいいます。 勾配降下法 (Gradient Descent) MSEができるだけ小さくなるような直線の傾き(m)と切片(b)を設定するために、勾配降下法を使用します。 MSEをb(もしくはm)で微分したときの値が最小になったときに、MSEを最小にするb(もしくはm)を導出するといった手法になります。 導出手順は以下のようになります。 1. MSEをbで微分 2. 微分値が最小であるか判定 -> 最小であればbを決定(ここで終了) 3. 最小でなければ学習率(Learning Rate)をかける 4. b = b - (3で導出した値)とする 5. 2に戻る 実装 horsepower(馬力), weight(重量), displacement(エンジンの大きさ)を入力値として、mpg(燃費)を予測します。 メソッドの構成 線形回帰を実装するにあたり、LinearRegressionクラスを作成し、各メソッドに機能をわけました。 gradientDescent(): 勾配降下処理の実行とm, bの更新 train(): 最適なm, bがでるまでgradientDescent()を実行 test(): Test dataから導出したm, bの精度を評価 predict(): 導出したm, bで値を予測 processFeatures(): 計算に使えるようにfeaturesを加工 standardize(): データの標準化 recordMSE(): 学習率の調整に使えるようにMSEを記録 updateLearningRate(): 学習率の更新 コード① linear-regression.js コードにするとこのような感じになりました。 linear-regression.js //ライブラリの読込 const tf = require('@tensorflow/tfjs'); const _ = require('lodash'); //クラス class LinearRegression { constructor(features, labels, options) { this.features = this.processFeatures(features); // this.labels = tf.tensor(labels); // this.mseHistory = []; this.options = Object.assign( { learningRate: 0.1, iterations: 100 }, options ); this.weights = tf.zeros([this.features.shape[1], 1]); //m, bの初期値 } gradientDescent(features, labels) { const currentGuesses = features.matMul(this.weights); //features * weights const differences = currentGuesses.sub(labels); // features * weights - labels // dMSE/dmとdMSE/dbのテンソル // (features * (features * weights - labels))/n const slopes = features .transpose() .matMul(differences) .div(features.shape[0]); //列の個数で割る this.weights = this.weights.sub(slopes.mul(this.options.learningRate)); //m, bのテンソル } train() { //バッチの個数(データ個数/1回のバッチサイズ) const batchQuantity = Math.floor( this.features.shape[0] / this.options.batchSize ); // 学習率を最適化しながらMSEを繰り返し実行 for (let i = 0; i < this.options.iterations; i++) { for (let j = 0; j < batchQuantity; j++) { const startIndex = j * this.options.batchSize; //バッチ開始位置 const { batchSize } = this.options; //1回のバッチサイズ //各バッチのfeatures const featureSlice = this.features.slice( [startIndex, 0], [batchSize, -1] ); //各バッチのlabels const labelSlice = this.labels.slice([startIndex, 0], [batchSize, -1]); //バッチごとにgradientDescentを実行 this.gradientDescent(featureSlice, labelSlice); } this.recordMSE(); this.updateLearningRate(); } } predict(observations) { return this.processFeatures(observations).matMul(this.weights); } //決定係数の導出 test(testFeatures, testLabels) { testFeatures = this.processFeatures(testFeatures); testLabels = tf.tensor(testLabels); const predictions = testFeatures.matMul(this.weights); const res = testLabels.sub(predictions).pow(2).sum().get(); //Sres const tot = testLabels.sub(testLabels.mean()).pow(2).sum().get(); //Stot return 1 - res / tot; //coefficient of determination(決定係数) } //計算に使えるようにfeaturesを加工 processFeatures(features) { features = tf.tensor(features); features = tf.ones([features.shape[0], 1]).concat(features, 1); if (this.mean && this.variance) { features = features.sub(this.mean).div(this.variance.pow(0.5)); //正規化 } else { features = this.standardize(features); //標準化 } return features; } //標準化 standardize(features) { const { mean, variance } = tf.moments(features, 0); this.mean = mean; this.variance = variance; return features.sub(mean).div(variance.pow(0.5)); } //学習率(Learning Rate)調整のためにMSEを記録 recordMSE() { const mse = this.features .matMul(this.weights) .sub(this.labels) .pow(2) .sum() .div(this.features.shape[0]) .get(); this.mseHistory.unshift(mse); } //学習率(Learning Rate)の更新 updateLearningRate() { if (this.mseHistory.length < 2) { return; } if (this.mseHistory[0] > this.mseHistory[1]) { this.options.learningRate /= 2; } else { this.options.learningRate *= 1.05; } } } module.exports = LinearRegression; 決定係数(Coefficient of Determination) test()メソッドで出力する予測精度として、決定係数というものを考えました。 決定係数は、回帰によって導いたモデルで予測した値が実際の値とどの程度一致しているかを表現する評価指標です。 決定係数はR2として表現され、値が大きいほどモデルが適切にデータを表現できていること(予測値の精度が高いこと)を意味します。 バッチ勾配降下法 勾配降下法はパラメータの更新のたびにすべてのTraining dataで勾配を計算します。 Training dataの量が増加するにつれて計算を行うのが難しくなるため、その場合はバッチ勾配降下法や確率的勾配降下法を利用します。 バッチ勾配降下法では、一度に利用するTraining dataの量が勾配降下法と比較して少なくなります。 Training dataの中からいくつかのデータを取り出し、そのデータで計算した勾配にもとづいてパラメータを更新します。 勾配降下法と比較すると計算に必要なメモリ量が少なく、確率的勾配降下法と比較すると外れ値の影響を受けにくく学習が比較的安定して進むという利点があります。 train()メソッドではbatchQuantityでバッチの個数(回数)を定義し、1回のiterationごとにその回数gradientDescent()を実行しています。 学習率(Learning Rate)の最適化 勾配降下法(gradientDescent)で使用する学習率(Learning Rate)は、MSEの導出ごと(1回のiterationごと)に最適化を行います。 MSEの導出と記録 1回前のMSEと新しいMSEの値の比較 MSEが大きくなっていたら学習率を1/2にする MSEが小さくなっていたら学習率を5%増やす コード② index.js index.jsでlinear-regression.jsを読み込んで、決定係数(R2)とm, bを出力してみました。 index.js require('@tensorflow/tfjs-node'); //ルートフォルダで1回だけ呼ぶ const tf = require('@tensorflow/tfjs'); const loadCSV = require('../load-csv'); const LinearRegression = require('./linear-regression'); const plot = require('node-remote-plot'); let { features, labels, testFeatures, testLabels } = loadCSV( '../data/cars.csv', { shuffle: true, splitTest: 50, dataColumns: ['horsepower', 'weight', 'displacement'], labelColumns: ['mpg'], //Miles Per Gallon } ); const regression = new LinearRegression(features, labels, { learningRate: 0.1, iterations: 100, batchSize: 10, }); regression.train(); const r2 = regression.test(testFeatures, testLabels); plot({ x: regression.mseHistory.reverse(), xLabel: 'Iteration #', yLabel: 'Mean Squared Error', }); console.log('R2 is', r2); console.log( 'Updated M is:', regression.weights.get(1, 0), 'Updated B is:', regression.weights.get(0, 0) ); regression.predict([[120, 2, 380]]).print(); 結果 R2 is 0.6592028145756478 Updated M is: -1.645716905593872 Updated B is: -23.4530029296875 また、iterationごとのMSEの変遷をplotでグラフにすると以下のようになりました。 おわりに 確率的勾配降下法を使うとどんな結果になるのかも試してみたいです。 参考資料
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】半角全角を考慮してテキストを改行させる方法

Canvasで意外と手こずった部分なので復習も兼ねて、似たようなものを作る人に少しでも参考になればと。Canvasに限らず使えると思う。 //改行して分割された文字列を入れるための配列 var textArray = []; var text = ''; //改行させたい半角での長さ const kaigyou = 8; //文字列のバイト数を取得 const ByteCount = (string) =>{ var byte = 0; for (i = 0; i < string.length; i++){ if(string[i].match(/[ -~]/)){ byte += 1; //半角の場合 }else{ byte += 2; //全角の場合 } } return byte ; } //何文字目で改行するかを取得 const SliceCount = (string) =>{ var count = 0; var byte = kaigyou;  for (i = 0; byte > 0; i++){ if(string[i].match(/[ -~]/)){ byte -= 1; //半角の場合 }else{ byte -= 2; //全角の場合 } count++; } //byteが0になった時点で何文字カウントされたかを戻り値として返す return count; } const InputTextA = () =>{ var byteRest = byte //残りのバイト数という変数 var row = 0; //HTMLでidをTextに設定してある項目の文字列を代入 text = document.getElementById("Text").value; byte = ByteCount(text); textArray[0] = text; //残りのバイト数が一行分より長い間はぐるぐる回す while(byteRest > kaigyou){ var sliceNum = SliceCount(text); textArray[row] = text.slice(0,sliceNum); textArray[row + 1] = text.slice(sliceNum); text = textArray[row + 1]; byteRest -= kaigyou; //1行分のバイト数を引く row ++; } DisplayText(); } //テキストを描画 const DisplayText = () =>{ if(text != ""){ ctx.font = "23px 'MS ゴシック'"; var row = 0; var byteRest = byte while(byteRest > 0){ ctx.fillStyle = 'black'; ctx.textBaseline = 'top'; //30pxずつずらすことで改行している ctx.fillText(textArray[row],0, row * 30); row++; byteRest -= kaigyou; } } } 下の画像で8バイトで改行できることが確認できます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ハンズオンJavascript Chrome DevToolsの使い方

はじめに プログラミング学習進めてきて、いくらか自分の中で「この技術を極める」というのが決まってきたので、ネットだけでなく本格的に参考書も使用して勉強していこうかなと思います。(とはいえ、まだまだ未熟です) これから極めていく技術としては、以下の通り。 【メイン】 JavaScript(React含む) Node PHP(Laravel含む) 【次点】 CSS 今回はJavascriptの学習用に購入したO'REILLYのハンズオンJavaScriptの第一章の内容を簡単にまとめたいと思います。 ハンズオンJavaScript Chrome DevToolsの使い方 まずこの書籍では、JavaScriptがどんな特徴をもった言語か〜っていうのを触れています(ここは省略)。 その後に、実際にJavaScriptを使用するに当たって、ブラウザ(chrome devtools)の使い方を学んでいこうよっていう流れになっています。 ハンズオンを前提としているため、この時点で読むだけで学ぼうとしている人にはきついと思います。この本を読むのであればPCを開きましょう。 と、少し話がずれましたが、まずはDevToolsの開き方から始まります(Chromeが使える前提)。 Windows: Ctrl-Shift-I Mac: Command-Option-I またはChrome右上端の点三つを押下>その他のツール>デベロッパーツール そうすると、以下のようにデベロッパーツール(赤枠)が見えてきます。 (ページ自体は書籍内で用意されているものです。) お馴染みだとは思いますが、ここではこのページの裏側の情報が見えるわけですね。 画像内ではElementsからHTMLを参照しております。cellがえげつないですねぇ。 HTMLだけでなく、実際のソースコードも見れるので、見てみましょう。(一応jsのお勉強なので、jsのコードを見てみましょう) クラス定義されていて、何やら関数もたくさんありそうですね(具体的なコードの内容はここでは触れません) とりあえず、HTMLのあのcell操作をここでしてるそうなので、ブレイクポイントでこのゼルダの記号みたいなのを改造しちゃいましょう。 一旦ブレイクポイントをindexの77行目に置いてみて Webサイトをリロード! 止まりましたね。赤枠ないの右向いてる青い三角形を一回押してみましょう。 セルの一行目が出てきました笑 consoleでiを打ってみたら、1が出てきたので、裏で繰り返し構文が回っているのがわかりますね笑 せっかくなので、consoleでわざと値を書き換えてみましょうか笑 ca1.statesと打ったところ、 "00000000000000000001010000000000000000000" という情報を返してきたので、ca1.statesを全部1にしてみます。 さぁどうなるでしょうか笑 見事にゼルダが崩れました笑 こんな感じでChrome DevToolsで値の書き換えもできるんですね(実際にコードが変わるわけではないのでご安心を) 今回は書籍を通じた簡単な例でしたが、ブラウザ上のツールを使用することで開発の理解などが進むのではないかなぁと思いますので、積極的にDevTools使用していきましょうか!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptを学ぶ―関数オブジェクトとは―

JavaScriptでは「関数はオブジェクト」であるようです。 Javaを学習していたので、意味が分かりません… 違和感があるので、まとめてみたいと思います。 関数はオブジェクト 関数⇒は何かしらの処理 オブジェクト⇒モノ であると認識していますが、この2つが何故同じオブジェクトであるのでしょうか? 何となく分かりやすい料理のレシピで例えると 関数⇒やること:何かを作るために、何が必要で、どんな手順で処理するか。 オブジェクト⇒レシピ:どんな材料で、どんな手順で何ができるか書いたもの。 こう考えるとほぼ一緒ですね。 関数をオブジェクト扱いするとどうなるか JavaScriptではこのように「関数は処理手順を定義したオブジェクト」として扱われます。 オブジェクトなので変数に代入したり、引数として別の関数に渡したりすることが可能です。 ソースコードを見てみる <script> function add(a, b) { return a + b;//※ a + bというメソッドを返す } //a+bというメソッドをcalcLatorに代入する const calcLator = add; //xに2+3=5を代入する const x = calcLator(2, 3); console.log(x);//5 </script> 上記のコードだと、※の位置でa+bというメソッドをオブジェクトとして扱っているので、calcLatorという変数に代入が可能という訳です。 calcLatorは計算式がはいっているので、calcLator(2, 3);とすると2+3をしてくれます。 因みに無名関数(関数名を指定しない)書き方はこんな感じです。 <script> //無名関数 //a/bをそのままcalcLatorに入れたvar const calcLator = function (a, b) { return a / b; } //xに3/2=5を代入する const z = calcLator(3, 2); console.log(z);//1.5 </script> まとめ 関数オブジェクトのポイントは***「関数オブジェクト単体で考えるのではなく、呼び出し側とペアで考えること」 関数オブジェクトは単体では意味がなく、利用する呼び出し側があって初めて機能します。 「関数もオブジェクト」が分かってきました。 参考:https://book.impress.co.jp/books/1115101084
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

要素の取得+CSS書き換え1

DOM (Document Object Model) htmlで表現されている個々のオブジェクトを取得/操作したりできる 要素の取得 getElementById(id); 要素の作成 createElement(tag_type); createTextNode(text); 要素の追加 appendChild(element); jQueryの基本文法 $(document).ready(function(){ //ここにjQueryの処理を記述 }); $('要素').css('プロパティ','値'); jQueryでテキストの色を変更する方法 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Practice</title> <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script> </head> <body> <h1 id="title">タイトル1</h1> <p id="element1">要素1</p> <p id="element2">要素2</p> <script> $(document).ready(function(){ $('h1').css('color', 'red'); }); </script> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

要素の取得+CSS書き換え

DOM (Document Object Model) htmlで表現されている個々のオブジェクトを取得/操作したりできる 要素の取得 getElementById(id); 要素の作成 createElement(tag_type); createTextNode(text); 要素の追加 appendChild(element); jQueryの基本文法 $(document).ready(function(){ //ここにjQueryの処理を記述 }); $('要素').css('プロパティ','値'); テキストの色を変更する方法 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Practice</title> <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script> </head> <body> <h1 id="title">タイトル1</h1> <p id="element1">要素1</p> <p id="element2">要素2</p> <script> $(document).ready(function(){ $('h1').css('color', 'red'); }); </script> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

5歳娘「パパのReact、めっちゃ遅いね!」

37歳無職ワイ ワイ「(カタカタカタカタ・・・ッターン!)」 娘(5歳)「パパ、今日は何してるの?」 ワイ「今日はな、むかしWordPressで作った自分用TODOリストの」 ワイ「デザインをリニューアルしてんねん」 よめ太郎「(そんなことより職を探せや)」 娘「へぇ〜」 娘「WordPressってことは、PHPを書いてるの?」 ワイ「いや、ちゃうで」 ワイ「リニューアル後は、フロント部分をReactで実装しようと思ってな」 ワイ「そこで、WordPressをREST APIモードで使うことにしたんや」 ワイ「つまり、WordPressを管理画面つきAPIみたいに使うってことや」 娘「要は、WordPressをヘッドレスCMSとして使うんだね」 ワイ「ヘッドレス・・・?」 ワイ「ちゃうちゃう、管理画面つきAPIや」 娘「・・・まぁいいや」 娘「とにかく、APIからTODOリストのデータを取得して」 娘「それをReactで表示したいわけだね」 ワイ「そういうことや」 現在の進捗状況 娘「それで、フロントの実装はどこまでできたの?」 ワイ「ほぼ完成してるで」 ワイ「↓こんな感じや」 よめ太郎「(酒ばっかりやないかい・・・)」 ワイ「だいぶ下のほうに『職を探す』っていうタスクも」 ワイ「ちゃんと入ってるで!」 よめ太郎「(ちゃんととは・・・)」 娘「へぇ〜」 娘「ちょっと触ってみてもいい?」 ワイ「もちろんええで」 娘「(ポチポチポチ・・・)」 娘「う〜ん・・・」 ワイ「え、どしたん」 娘「パパのReact、めっちゃ遅いね!」 ワイ「え、マジ・・・?」 娘「なんか動きが重いよ?」 ワイ「え、そんなことないやろ・・・」 ワイ「(ポチポチポチ・・・)」 ワイ「あれ、ほんまや」 ワイ「検索フォームに文字を入力するときに、なんかモタついてんな」 ワイ「キーを叩いてから文字が表示されるまでの間に、タイムラグが1秒近くあるで」 ワイ「何やこれ・・・」 娘「パパ、もしかしたらコンポーネントたちが無駄に再レンダーされてるのかもよ」 娘「そんな時は、React Developer Toolsを使って再レンダー状況を可視化してみようよ」 ワイ「そ、そんな機能あんの?」 娘「あるよ」 ChromeにReact Developer Toolsという拡張機能を追加する Chrome Devtoolsを開く Componentsタブを開く 歯車アイコンをクリック Highlight updates when components render.にチェックを入れる 娘「手順としては↑こんな感じだね」 ワイ「なるほど・・・」 ワイ「↑ここにチェックを入れるんやな!」 娘「そうだね!」 さっそくレンダー状況を見てみる ワイ「何やこれ」 ワイ「検索フォームに文字を1つ入力したり消したりするたびに」 ワイ「100件くらいあるTODOリスト全体が再レンダーされてるやん」 ワイ「まだ検索ボタンを押してもないのに・・・!」 娘「そうだね・・・」 ワイ「検索ボタンを押して、そんで検索結果が変わったことでリストが再レンダーされるならまだしも」 ワイ「検索フォームに文字を1つ入力するたびに、リスト全体が再レンダーされるんかい」 ワイ「何やそれ・・・」 React.memoで、コンポーネントをメモ化しよう 娘「パパ、そんな時はReact.memoを使って」 娘「TODOリストのコンポーネントをメモ化してみようよ」 ワイ「ど、どうやんの・・・?」 娘「ええと・・・」 src/components/TodoList.tsx - const TodoList: React.FC<Props> = ({ list }) => { + const TodoList: React.FC<Props> = React.memo(({ list }) => { return ( <ul> {list.map((task) => ( <TodoItem task={task} key={task.id} /> ))} </ul> ) - } + }) + TodoList.displayName = 'TodoList' 娘「↑こうだね」 ワイ「おお、React.memoっていう関数に」 ワイ「TodoListコンポーネントの中身をぶち込んであげるだけなんやな」 娘「うん!」 よめ太郎「(前々から思ってたけど)」 よめ太郎「(引数をぶち込むって何やねん・・・)」 モタつきは解消されたのか ワイ「おお、TODOリストがピカピカせんようになっとるわ」 ワイ「文字の入力が重たい感じもなくなっとる」 娘「よかったね!」 ワイ「・・・これはどういうことなん?」 娘ちゃんによる解説 娘「えっとね、今までは」 React「お、検索フォームに文字が入力されたで!」 React「ほな状態が変わったことやし、再レンダーや!再レンダーや!」 娘「↑こんな感じだったんだけど」 娘「コンポーネントをメモ化すると・・・」 React「お、検索フォームに文字が入力されたで!」 React「でも、TODOリストの中身が変わったわけやないから」 React「TodoListコンポーネントはレンダーしなおす必要ないか・・・」 React「さっきメモしといた結果を再利用しとこか」 娘「↑こんな感じだね」 よめ太郎「(なんでReactも関西弁やねん)」 ワイ「なるほどなぁ」 ワイ「useStateとかで管理している状態が、文字入力によって変わったとしても」 ワイ「TODOリストに関係のない変更だけが起こっていた場合には」 ワイ「再レンダーをスキップしてくれるんやね」 娘「そういうことだね!」 ワイ「ありがとうやで、娘ちゃん!」 しかし30分後・・・ ワイ「こ、今度はタスクの削除機能を実装したら」 ワイ「また画面の動きが遅くなってもうた・・・」 娘「パパ、今度はどうしたの?」 ワイ「おお、娘ちゃん・・・」 ワイ「あのな・・・」 src/pages/TodoListPage.tsx + const onDelete = (taskId: number): void => { + /* 省略 */ + } return ( <> <h1>TODOリスト</h1> <form> {/* 省略 */} </form> - <TodoList list={todoList} /> + <TodoList list={todoList} onDelete={onDelete} /> </> ) ワイ「↑こんな感じで、タスク削除用の関数を作って」 ワイ「それをTodoListコンポーネントに渡すようにしただけなんや・・・」 ワイ「それなのに・・・」 ワイ「また文字入力のたびにTODOリスト全体がピカピカしてんねん・・・」 娘「そんな時は、またメモ化だね!」 useCallbackで、関数もメモ化しよう src/pages/TodoListPage.tsx - const onDelete = (taskId: number): void => { + const onDelete = useCallback((taskId: number): void => { setTodoList((prevList) => { return prevList.filter((todoItem) => todoItem.id !== taskId) }) - } + }, []) ワイ「おお、今度はonDelete関数の中身を」 ワイ「useCallbackというやつにぶち込んだるわけか」 娘「そうそう」 娘「これでもう一度、画面を触ってみて?」 ワイ「おお、チカチカしないし、サクサク入力できとるわ」 ワイ「・・・でも、これ何でなん?」 ワイ「TodoListコンポーネントに渡すonDelete関数は、毎回おんなじ関数なんやから」 ワイ「TodoListは再レンダーされないはずちゃうの・・・?」 娘「それはね・・・」 src/pages/TodoListPage.tsx const onDelete = (taskId: number): void => { /* 省略 */ } return ( <> <h1>TODOリスト</h1> <form> {/* 省略 */} </form> <TodoList list={todoList} onDelete={onDelete} /> </> ) 娘「↑ここで、onDelete関数を作って」 娘「TodoListコンポーネントに渡してるでしょ?」 娘「そこが問題・・・というかポイントなの」 娘「つまり・・・」 React「よっしゃ、onDelete関数を作って」 React「TodoListコンポーネントにぶち込むでぇ!」 娘「↑こんな感じで、作りたての関数をTodoリストコンポーネントに渡す」 娘「そういうことになっちゃうの」 ワイ「ああ、そうか」 ワイ「アロー関数式で関数を作って、onDeleteっていう変数に代入してるわけやもんな」 娘「そう」 娘「だから・・・」 React「さっきまでとは違う、今作ったばかりのonDelete関数を渡すから」 React「TodoListコンポーネントも再レンダーや!再レンダーや!」 娘「↑こんな感じになっちゃうの」 ワイ「つまり、ReactはObject.isによる比較アルゴリズムを使用する、ってことか・・・」 よめ太郎「(いや、そこは急に理解早いんかい)」 娘「そういうことだね」 娘「そして、useCallbackを使うと」 ワイ「この関数は毎回作り直さないで、前回と同じのを使ってな!」 娘「ってことをReactに伝えられるわけだね」 ワイ「ふーん、そうするとTodoListコンポーネントが再レンダーされなくなるん?」 娘「そうだよ」 React「TODOリストの内容もonDelete関数も前回と変わってへんな」 React「ほな今回はTodoListコンポーネントの再レンダーはスキップや!」 娘「↑って感じになるの」 ワイ「なるほどなぁ」 ワイ「ちなみにuseCallbackの第二引数の配列、これは何なん?」 娘「これは・・・」 ワイ「hogeっていう変数の中身が変わった場合には」 ワイ「前回と同じ関数を使いまわさずに、新しく作ってな!」 娘「↑的なことをReactに伝えるときのための配列だね」 ワイ「ほぇ〜・・・」 ちなみに 娘「ちなみに、useCallbackを使わないでも」 娘「コンポーネントの外で関数を定義するだけで」 娘「再レンダーを抑止できる場合もあるよ!」 ワイ「なんや、よう分からんけど」 ワイ「とにかく激重サイトにならんくてよかったわ!」 まとめ ワイ「いやー、娘ちゃんのおかげで良いTODOリストができそうやわ・・・!」 娘「えっと・・・パパ・・・」 ワイ「なんや?」 娘「大変なことに気づいたんだけど・・・」 娘「このTODOリスト、どこからタスクを追加するの?」 ワイ「あ・・・」 ワイ「タスクの作成機能を作り忘れてたわ・・・」 娘「・・・」 ワイ「で、でも大丈夫や!」 タスク追加機能を実装する ワイ「↑っていうタスクを、このTODOリストに追加しておけばええねん!」 娘「で、そのタスクはどこから追加するの?」 ワイ「それな」 〜おしまい〜 参考文献 React の最上位 API『React.memo』 差分検出処理 – React Object.is() - JavaScript | MDN - Mozilla 補足
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptメソッドで出来ること

はじめに 初学者の私が、アクティブラーニング兼備忘録の為にJavaScriptのメソッドをまとめる記事です。 少しずつ追記していきます。 目次 getElementById 基本構文 innerHTMLで活用 onClickで活用 getElementById getElementByIdメソッド HTMLタグで指定した任意のIDにマッチするドキュメント要素を取得する。 これが一番よく使う気がします。 基本構文 <html> <body> <p id='hey'>Hello world</p> <script> console.log(document.getElementById('hey')); </script> </body> </html> <p id='hey'>Hello world</p> innerHTMLで活用 <html> <body> <p id='hey'>こんにちは</p> <input type='button' value='Click' onclick='myfunc()'> <script> let myfunc = () => { const myp = document.getElementById('hey'); myp.innerHTML = 'こんばんは'; } </script> </body> </html> ボタンを押すと「こんにちは」から「こんばんは」になる onClickで活用 <html> <body> <p>push button</p> <input type='button' id='myid' value='Click'> <script> let mybutton = document.getElementById('myid'); let myfunc = () => { mybutton.value = 'Hello world'; } mybutton.onclick = myfunc; </script> </body> </html> ボタンを押すと、ボタンに書いてある「Click」が「Hello world」になる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】methodsでバインドした値をsubmitする際に上手くpostされない時にnextTickを使った話

Qiitaの質問機能で回答頂いた内容がとても勉強になったので、 復習を兼ねて記事としても残しておく。 発生した問題 vueテンプレート内で、form要素のaction属性、input要素のvalue属性を動的に処理したいが、 methods内でdataに対して、属性値を更新しても上手く代入した値が反映されず、空でpostされてしまう。 事例 vue.js 略 <form :action="action" method="post" ref="form"> <input :value="id" type="hidden" name="id"> </form> <span @click.prevent="formSubmit(item.id, '/edit')">編集する</span> 略 export default { data(){ return { action: '', id: '' } }, methods:{ formSubmit(id, action){ this.id = id; this.action = action; this.$refs.form.submit(); return false; } 略 ちなみに↓だと上手くいく vue.js 略 <form action="" method="post" ref="form"> <input value="" type="hidden" name="id" ref="input"> </form> <span @click.prevent="formSubmit(item.id, '/edit')">編集する</span> 略 export default { methods:{ formSubmit(id, action){ this.$refs.form.value = id; this.$refs.form.action = action; this.$refs.form.submit(); return false; } 略 解決方法 そもそもの問題はbindした結果がDOMに反映されるのが、 メソッドがreturnした後になる為、仮想DOMだけ書き換えて、 DOMそのものが書き換わる前に submitを実行する形となり、値がPOST出来なかった。 そこでnextTickを使ってsubmitをDOMの更新サイクル後に実行する。 vue.js 略 <form :action="action" method="post" ref="form"> <input :value="id" type="hidden" name="id"> </form> <span @click.prevent="formSubmit(item.id, '/edit')">編集する</span> 略 export default { data(){ return { action: '', id: '' } }, methods:{ formSubmit(id, action){ this.id = id; this.action = action; this.$nextTick(() => this.$refs.form.submit()); return false; } 略 nextTickはDOM更新後に第1引数で渡した関数を実行してくれる。 これでmethodsで更新した値でPOSTする事が出来た。めでたしめでたし。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript  九九 表なし シンプル 1*1=1 →目指す形

九九を作ってみよう for(let i =1; i <10; i++){ for(let j =1; j<10; j++){ document.write(i + '*' + j + '=' + i*j); } } letってなによ ・変数名を宣言する言葉 var let const ってのがある letやconstを使うと{}→ブロックの外側から呼び出せない。 多分この後もqiitaに投稿していきながら、付き合いの長い言葉になりそうです。その時にこの辺りがもっと深掘りできると思います ブラウザ表示↓ 1*1=11*2=21*3=31*4=41*5=51*6=61*7=71*8=81*9=92*1=22*2=42*3=62*4=82*5=102*6=122*7=142*8=162*9=183*1=33*2=63*3=93*4=123*5=153*6=183*7=213*8=243*9=274*1=44*2=84*3=124*4=164*5=204*6=244*7=284*8=324*9=365*1=55*2=105*3=155*4=205*5=255*6=305*7=355*8=405*9=456*1=66*2=126*3=186*4=246*5=306*6=366*7=426*8=486*9=547*1=77*2=147*3=217*4=287*5=357*6=427*7=497*8=567*9=638*1=88*2=168*3=248*4=328*5=408*6=488*7=568*8=648*9=729*1=99*2=189*3=279*4=369*5=459*6=549*7=639*8=729*9=81
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript 繰り返し 2000年 本日まで

<script> var date = new Date(); var year = date.getFullYear(); for(var i = 2000; i <= year; i++){ document.write('<p>' + i + '</p>'); } </script> ブラウザ表示 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuetify で特定の id リンクにジャンプする(Stringの場合、v-btnの場合)

経緯 Vuetifyをつかって、Rails で作成した id リンクに直接飛びたい。 しかし、一筋縄にはいきませんでした。 ボタンと文字、それぞれのリンクの付け方が異なっていたからです。 備忘録として残します。 String に idリンク をつけたい場合 <nuxt-link :to="{ path: `/users/${item.user_id}` }"> <span> Something </span> </nuxt-link> v-btn に idリンク をつけたい場合 <v-btn nuxt :to="`/users/${item.user_id}`" > Something </v-btn> 参考記事 Nuxt × Vuetifyの状況下でv-btnをnuxt-linkにして使いたい モーダルへのNuxtオープンリンク https://knews.vip/q/so73690053/mo-daru-e-no-nuxt-o-pun-rinku 以上です。{} や / や " など、どの記号が必要なのか総当たりで試しました。 この記事で、どなたかの時間が節約されるのであれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fizz Buzz なんでこうなるんだろう どなたか教えて欲しいです。

なんで書き方変えたら理想としている答えになったのか 優先順位があるのでしょうか?????????? ・3で割り切れる Fizz ・5で割り切れる Buzz ・3と5で割り切れる FizzBuzz ・上に書いたの意外そのまま表示 for (var i = 1; i < 18 ; i++){ if(i%3 === 0){ document.write('<p>Fizz</p>'); } else if (i%5 === 0){ document.write('<p>Buzz</p>'); } else if ((i%3 === 0) && (i%5 === 0)){ document.write('<p>FizzBuzz</p>'); } else { document.write('<p>' + i + '</p>'); } } こう作ったのだけど失敗 ブラウザ表示↓ 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 Fizz  なんでー!???? 15のところFizzBuzzになんない 16 17 今度はこうしてみた for (var i = 1; i < 18 ; i++){ if((i%3 === 0) && (i%5 === 0)){ document.write('<p>FizzBuzz</p>'); } else if (i%3 === 0){ document.write('<p>Fizz</p>'); } else if (i%5 === 0){ document.write('<p>Buzz</p>'); } else { document.write('<p>' + i + '</p>'); } } ブラウザ表示↓ 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz あ!15のところFizzBuzzになった! 16 17
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fizz Buzz

なんで書き方変えたら理想としている答えになったのか 優先順位があるのでしょうか?????????? ↓ 教えてくださった先輩方・みなさま 本当にありがとうございます 答えは 15の時は最初のもし3で割って余りがゼロならに引っかかるので、そこで15の処理は終わりになる。なるほどーだから最初に書くべきだったのか こんなふうに考えてしまいました ・少ない数字から順番に書いていこう ・ちゃんと理解していれば i%3 === 0){document.write・・・の部分で 終了じゃん!て気づく ・先に大きな条件書いて、あとは細かいやつ書くイメージかなと今回反省いたしました ・3で割り切れる Fizz ・5で割り切れる Buzz ・3と5で割り切れる FizzBuzz ・上に書いたの意外そのまま表示 for (var i = 1; i < 18 ; i++){ if(i%3 === 0){ document.write('<p>Fizz</p>'); } else if (i%5 === 0){ document.write('<p>Buzz</p>'); } else if ((i%3 === 0) && (i%5 === 0)){ document.write('<p>FizzBuzz</p>'); } else { document.write('<p>' + i + '</p>'); } } こう作ったのだけど失敗 ブラウザ表示↓ 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 Fizz  なんでー!???? 15のところFizzBuzzになんない 16 17 今度はこうしてみた for (var i = 1; i < 18 ; i++){ if((i%3 === 0) && (i%5 === 0)){ document.write('<p>FizzBuzz</p>'); } else if (i%3 === 0){ document.write('<p>Fizz</p>'); } else if (i%5 === 0){ document.write('<p>Buzz</p>'); } else { document.write('<p>' + i + '</p>'); } } ブラウザ表示↓ 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz あ!15のところFizzBuzzになった! 16 17
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WEBサイトを作成するまでの道のり04

管理しているサイトがwordpressだったので自分で作り変えよ~~と思ったのでそれまでの道のりを記録したものです。 そろそろそれっぽい見た目を作っていきたい ということで、ぽいやつを作っていきます。 intersectionObserver 意味 = 指定したものを監視してもらうツールだと。。 実際に使ってみます。 const child = document.querySelector('.child'); const cb = function() { alert('non'); } const io = new IntersectionObserver(cb); io.observe(child) //対象のメソッドを監視 監視にしていしたclass childが枠外から出るとアラートが飛んできました。 何となくintersectionObserverというものの物体がわかってきた気がします。 完成 画像では止まっている状態ですがここに書かれている文字が流れるように表示されます。 //main.js document.addEventListener('DOMContentLoaded', function () { const els = document.querySelectorAll('.animate-title'); const cb = function (entries, observer) { entries.forEach(entry => { if (entry.isIntersecting) { const ta = new TextAnimation(entry.target); ta.animate(); observer.unobserve(entry.target); } else { } }); }; const options = { root: null, rootMargin: "0px", threshold: 0 }; const io = new IntersectionObserver(cb, options); els.forEach(el => io.observe(el)); }); //text-animation.js class TextAnimation { constructor(el) { this.DOM = {}; this.DOM.el = el instanceof HTMLElement ? el : document.querySelector(el); this.chars = this.DOM.el.innerHTML.trim().split(""); this.DOM.el.innerHTML = this._splitText(); } _splitText() { return this.chars.reduce((acc, curr) => { curr = curr.replace(/\s+/, '&nbsp;'); return `${acc}<span class="char">${curr}</span>`; }, ""); } animate() { this.DOM.el.classList.toggle('inview'); } } class TweenTextAnimation extends TextAnimation { constructor(el) { super(el); this.DOM.chars = this.DOM.el.querySelectorAll('.char'); } animate() { this.DOM.el.classList.add('inview'); this.DOM.chars.forEach((c, i) => { TweenMax.to(c, .6, { ease: Back.easeOut, delay: i * .05, startAt: { y: '-50%', opacity: 0}, y: '0%', opacity: 1 }); }); } } では実装していきます! 3つの画像が画面上でスクロールされる物を作っていきます。 完成形 使用するライブラリー Swiperを使いスライダーエフェクトを記載していきます。 このページに記載してある設定手順に従って追記していきます。 //index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.min.css" /> <link rel="stylesheet" href="style.css"> </head> <body> <!-- Slider main container --> <div class="swiper-container"> <!-- Additional required wrapper --> <div class="swiper-wrapper"> <!-- Slides --> <div class="swiper-slide"><img src="images/1567413421.jpg" alt="" srcset=""></div> <div class="swiper-slide"><img src="images/1567413502-1.png" alt="" srcset=""></div> <div class="swiper-slide"><img src="images/1567413577-1.png" alt="" srcset=""></div> ... </div> <!-- If we need pagination --> <div class="swiper-pagination"></div> <!-- If we need navigation buttons --> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> <!-- If we need scrollbar --> <div class="swiper-scrollbar"></div> </div> <script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script> <script src="main.js"></script> </body> </html> //mian.js const swiper = new Swiper('.swiper-container', { // Optional parameters direction: 'vertical', loop: true, // If we need pagination pagination: { el: '.swiper-pagination', }, // Navigation arrows navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, // And if we need scrollbar scrollbar: { el: '.swiper-scrollbar', }, }); const swiper = new Swiper('.swiper-container', { // Optional parameters direction: 'vertical', ←の部分をコメントアウトすると横のスクロールになります。様々なエフェクトがあります loop: true, ///style.scss .swiper-container { width: 100%; height: 500px; } スライドの追加ができました。 次に、上記のように文字を中央に追加し、左右画面に展開されている<>を消すなどしていきます。 //index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.min.css" /> <link rel="stylesheet" href="style.css"> </head> <body> <div id="global-container"> <div id="container"> <div id="content"> <div class="hero"> <div class="swiper-container"> <div class="swiper-wrapper"> <div class="swiper-slide"> <div class="hero__title">Enjoy Rich</div> <img src="images/1567413421.png" alt="" /> </div> <div class="swiper-slide"> <div class="hero__title">Fantastic</div> <img src="images/1567413502-1.png" alt="" /> </div> <div class="swiper-slide"> <div class="hero__title">Experience</div> <img src="images/1567413577-1.png" alt="" /> </div> </div> <div class="hero__footer"> <img class="hero__downarrow" src="images/arrow.svg"> <span class="hero__scrolltext">scroll</span> </div> </div> </div> </div> </div> </div> <script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script> <script src="main.js"></script> </body> </html> //style.scss $rosybrown: rosybrown; .swiper-container { width: 100%; height: 300px; } .hero { &__title { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: $rosybrown; font-size: 25px; } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【jQuery】追加した要素にイベント処理を設定することに失敗した

jQueryを使ってページに要素を追加し、その要素にイベント処理を設定しようとしたところ、失敗しました。 かなり初歩的なミスでした。 まず、こんな感じでボタンを用意します。 <body> <button id="first">ボタンを追加する</button> </body> ボタンを押すとボタンを追加するようにします。 追加したボタンを押すと、メッセージが表示されるようにします。 $(function(){ $('#first').click(function(){ $('body').append('<button id="second">メッセージを表示する</button>'); }); $('#second').click(function(){ alert('このメッセージは表示されません!'); }); }); しかし、このような書き方だと、メッセージは表示されません。 追加したボタン(id="second")は、ページ読み込み時に存在していなかったため、イベント処理を設定できなかったようです。 $(function(){ $('#first').click(function(){ $('body').append('<button id="second">メッセージを表示する</button>'); }) $('body').on('click', '#second', function(){ alert('このメッセージは表示されます!'); }); }); このようにonメソッドを使うと、ページ読み込み時に存在しなかった要素にもイベント処理を設定できます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Slickの基礎知識

はじめに 業務の中でreact-slickを使用する機会があったので 使用方法やオプションなどメモ代わりに書いていきたいと思います。 ユースケースは別で書こうと思います。 アジェンダ React Slickとは 導入 使用方法 個人的よく使うオプション React Slickとは react-slick/git-hub React Slick Documentation スライダーやカルーセルを簡単に実装でき、 レスポンシブ対応や豊富なオプションを使用できるのが特徴です 元々jQeryのプラグインであるSlickを Reactでも使用できるようモジュール化したものになります。 導入 npmを使用する場合の導入までを公式から拝借。 パッケージのインストール npm install react-slick --save CSSをインストール npm install slick-carousel // Import css files import "slick-carousel/slick/slick.css"; import "slick-carousel/slick/slick-theme.css"; これで準備は整いました。 使用方法 ここでは画像3枚を配列に入れて使用しています。 settingsにオプションを指定し、Sliderに渡すだけで 簡単にカスタムすることができます。 import React from "react"; import Slider from "react-slick"; export default function SimpleSlider() { const settings = { dots: true, infinite: true, speed: 500, }; const images = [image, image2, image3] return ( <Slider {...settings}> {images.map(img)=> <div> <img src={img} alt="pictuer" /> </div } </Slider> ); } 個人的よく使うオプション 個人的によく使うオプションを中心に紹介します。 jQueryのSlickとは異なる部分もあるので注意が必要です。 なお主観で機能と見た目でオプションを分類しています。 機能に関するオプション autoplay スライドを自動再生します。 Type: bool Default: false infinite コンテンツをループさせます(① -> ② -> ③ -> ① ...) Type: bool Default: true initialSlide 最初に表示するスライドを指定します。 Type: int Default: 0 lazyLoad 画像の読み込むタイミングを指定します。 progressiveは最初にまとめて、 ondemandは遅延読み込みをします。 Type: ondemand / progressive Default: null slidesToScroll 一度にスライドを何枚スクロールするか指定します。 Type: int Default: 1 slidesToShow フレーム内にスライドを何枚表示するかを指定します。 Type: int Default: 1 speed スクロール、フェードアニメーションの速度msで指定します。 Type: int Default: 500 pauseOnHover autoplayがtureの場合に 自動再生をマウスホバーで一時停止します。 Type: bool Default: false 見た目に関するオプション arrows 「前」「次」のスライドを操作する矢印を表示します。 Type: bool Default: true centerMode 現在表示しているスライドを中央に配置し、 次のスライドを少し見切れて表示させます。 Type: bool Default: false centerPadding centerModeを指定した場合に 見切れて表示をさせる割合を指定します。 Type: string Default: '50px' customPaging dotsがtrueの場合ドットナビをカスタムできます。 Type: func Default: i => {i + 1} dots ドットナビを表示します。 Type: bool Default: false fade スライドの切り替え方をフェードにします。 Type: bool Default: false nextArrow arrowsがtureの場合に 「次」の矢印の見た目をHTMLでカスタムできます。 Type: html Default: NEXT prevArrow arrowsがtureの場合に 「前」の矢印の見た目をHTMLでカスタムできます。 Type: html Default: Previous おわりに カルーセルも一から実装しようと思うと手間ですが Slickだと、とても簡単に実装できます。 最後まで、見ていただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TensorFlow.js学習メモ① k近傍法(k-nearlest neighbor)で座標から家の価格を予測

はじめに 仕事の関係で機械学習について学習する必要性がでてきたので、アルゴリズムを何個か軽く勉強することにしました。 Pythonが絡むと学習負荷が一気に高くなってしまう気がしたので、学習はJavaScript(TensorFlow.js)で行いました。 まずは、座標や床面積を入力してk近傍法で家の価格を予測するようなアルゴリズムを実装しました。 ほぼ学習メモみたいな感じなので、深い内容をお求めの方はその点ご了承ください。 TensorFlow.js TensorFlow.jsはPythonのnumpyみたいな操作ができるライブラリです。 JSで簡単に行列計算をすることができます。 k近傍法(k-nearllest neighbor) 入力値に対する予測値を出すアルゴリズムの一つです。 指定した座標(lat, long)から家の価格(price)を予測する場合を考えると、予測まで以下のような流れになります。 座標と家の価格に関する実際のデータをたくさん集める(Training data) 価格を知りたい座標を指定して前ステップで集めた座標データとの差分をそれぞれとっていく 差分が小さい順にデータをソートする 差分が小さい順に指定した個数(k)分のデータを取得する 取得したデータの価格の平均値をとる(予測値) 理想的なkの値を探すために、Training dataとTest dataをそれぞれ用意します。 knnでTest dataの座標から価格を出し、Test dataの価格との差が小さくなるようにkを調整していきます。 また、座標だけではなく床面積(sqft_lot)やリビング面積(sqft_living)などの複数の要素を考慮すると、より精度の高い価格予測ができそうです。 前提知識 実装に際して前提となる超基本知識をまとめました。 分類と回帰 値予測のために分類(Classification)と回帰(Regression)について理解しておく必要があります。 入力値に対してその値が合格か不合格かを予測したい場合には分類、どのような値になるかを予測したい場合には回帰を使います。 正規化(Normalization)と標準化(Standardization) データを最大値が1、最小値が0のデータとなるように変換することを正規化、元のデータの平均を0、標準偏差が1のものへと変換することを標準化と呼びます。 最大値及び最小値が決まっている場合(画像処理とか)などには正規化を利用します。 一方、最大値及び最小値が決まっていない場合や外れ値が存在する場合には、重みを学習しやすくするために標準化を利用します。 正規化の式 標準化の式 (μ: 平均, σ: 標準偏差) 実装 実装の流れをまとめました。 データの準備 予測に使うデータ(kc_house_data.csv)を用意します。 Training dataとTest dataをこの中から抽出します。 index.jsの作成 この中にデータの予測処理を作成します。 ライブラリの読み込み index.js require('@tensorflow/tfjs-node'); const tf = require('@tensorflow/tfjs'); const loadCSV = require('./load-csv'); knn処理 index.js //一番近いpriceを探索するknn function knn(features, labels, predictionPoint, k) { const { mean, variance } = tf.moments(features, 0); //平均値と分散の取得、第2引数でrowかcolumnを指定 const scaledPrediction = predictionPoint.sub(mean).div(variance.pow(0.5)); //入力値の標準化 (入力値-平均値)/標準偏差 return ( features .sub(mean) .div(variance.pow(0.5)) //Training dataの標準化 .sub(scaledPrediction) //入力値(標準化済)との差 .pow(2) //各要素を2乗 .sum(1) //各要素の和 .pow(0.5) //ルートをとる(distance算出) .expandDims(1) //連結するためにdimを操作 .concat(labels, 1) //labels(price)と連結 .unstack() //sort, sliceの操作を行うためにobj化 .sort((a, b) => (a.get(0) > b.get(0) ? 1 : -1)) //distance小さい順にソート .slice(0, k) //k個データを取得 .reduce((acc, pair) => acc + pair.get(1), 0) / k //priceの平均値(knnの結果) ); } データの取得 splitTest:10でTest data(testFeatures, testLabels)をランダムに取得し、それ以外をTraining data(features, labels)として取得します。 index.js let { features, labels, testFeatures, testLabels } = loadCSV( 'kc_house_data.csv', { shuffle: true, splitTest: 10, //testFeatures, testLabelsの数を指定 dataColumns: ['lat', 'long', 'sqft_lot', 'sqft_living'], //featureカラムを指定 指定数増やすと精度上がる labelColumns: ['price'], //labelカラムを指定 } ); features = tf.tensor(features); labels = tf.tensor(labels); 予測値の出力 10個のTest data(testFeatures)をknnで処理した結果(Guess)とtestLabelsとの乖離度合(Error)を出力しました。 index.js testFeatures.forEach((testPoint, i) => { //testFeature10個それぞれのerrをみる const result = knn(features, labels, tf.tensor(testPoint), 10); //knnの結果(予測値) const err = (testLabels[i][0] - result) / testLabels[i][0]; //Test dataのpriceと予測値の乖離度合 console.log('Error', err * 100); console.log('Guess', result, testLabels[i][0]); }); 出力結果 Error -15.323502304147466 Guess 1251260 1085000 Error -11.344580119965723 Guess 519756.5 466800 Error -2.047058823529412 Guess 433700 425000 Error 19.327433628318584 Guess 455800 565000 Error 7.806324110671936 Guess 699750 759000 Error -14.106372465729613 Guess 584260 512031 Error -8.782552083333334 Guess 835450 768000 Error 13.227406199021207 Guess 1329790 1532500 Error -36.336911441815076 Guess 279422.5 204950 Error 7.381578947368421 Guess 228767.5 247000 おわりに ほんとに触りだけなので、数式がわかればそこまで実装は難しくないという印象。 参考資料
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Teachable Machine で出力した機械学習モデル(画像分類、TensorFlow.js用)を Node.js で動かすために公式情報をたどる&実際に試す

タイトルの通りの内容で、以下の記事を書いた際にやった「ブラウザ上での実行」の部分を、「Node.js での実行」にしたものです。 Teachable Machine について等の話はこの記事では省略しますので、それらに関する情報をご覧になりたい方は以下の記事内のものをご覧ください。 ●Teachable Machine で出力した機械学習モデル(画像分類、TensorFlow.js用)をダウンロードしてブラウザで動かす【2021年4月版】 - Qiita  https://qiita.com/youtoy/items/3be502c6266cfb61e49a まずは公式情報をたどる 冒頭に書いた記事では、Teachable Machine で出力した画像分類用の機械学習モデル(TensorFlow.js用)をローカルにダウンロードし、それを用いた推論をブラウザ上で行いました。JavaScript の処理を含む HTMLファイルを使った形です。 今回は、推論を行うプログラムを Node.js で実行します。そのやり方については、公式で掲載された FAQ の情報からたどって探していきます。 公式のよくある質問から情報をたどる Teachable Machine のページの上部メニューを見ると、右のほうに「よくある質問」と書かれた部分があります。そしてその中を見ていくと「保存とエクスポート」と書かれた部分があり、さらにその中で「他のライブラリやプラットフォームでも Teachable Machine のモデルを使用できますか?」という質問がありました(※以下の画像の部分)。 上記の画像中の文章で、「Teachable Machine コミュニティの Github リポジトリをご覧ください。」とあるので、その Github リポジトリのページへと進んでいきます。 Teachable Machine コミュニティの Github リポジトリ 上記の Github リポジトリを見ると、「Community Contributions and Projects」という項目の中で、以下の画像の記載を見つけました。 この「Teachable Machine Node Library for image models」という部分をたどっていきます。 プロジェクトのページとその先 上記の続きで「tr7zw/teachablemachine-node-example」というページにたどり着きました。その説明を見ていこうとすると、以下の画像にあるように「最新版はまた別のページにある」という記載と誘導用のリンクが・・・。 上記のリンク先は「drinkspiller/teachablemachine-node-example」というページでした。さらにこの先へ誘導される、ということはなさそうです(笑) このページで「How It Works」と書かれた部分を見てみます。 リクエストに対して、JSON でレスポンスを返すような動きをしてくれそうな記載がありました。 サンプルを動かしてみる 上記の記載があった箇所の少し下を見ると、使い方などが書かれた部分がありました。 その手順通りに進めてみます。 インストール リポジトリの内容をクローンして、パッケージのインストールを行います。どこか適当なフォルダで、以下のコマンドを実行していきましょう。 # クローン git clone https://github.com/drinkspiller/teachablemachine-node-example.git # パッケージのインストール cd teachablemachine-node-example/ npm install 自分の環境で実行したところ、以下のエラーが・・・。 少し調べてみると、自分が Node.js の v15 を使っているのが関係していそうで、Node.js を v14 にすると良さそうでした。Node.js のバージョン管理ツールを使っていたので、サクッと v14 に変更してから再度 npm install を実行して、その後は無事にインストールがエラーもなく完了となりました。 なお、今回の内容の package.json の中身を見てみると、dependencies は以下となっているようです。 app.js の書きかえ 次の手順は、app.js の書きかえです。 以下の部分を見ると、モデルの URL の指定方法について書かれており、ファイルシステムのパスは利用不可と書いてあります。 そして、デフォルトの設定として、事前に用意されたモデルの URL が別の行に書いてありました。具体的には以下の部分です。 この部分を独自の機械学習モデル用の設定に置きかえます。 Teachable Machine の機械学習モデルをクラウドにアップロードするやり方をとっていた場合は、そのアップロード先の URL を上記の部分に書くだけで大丈夫です。 そのやり方がシンプルな方法ではありましたが、今回は、冒頭に書いた前の記事と同様に、ローカルに機械学習モデルの関連ファイル一式をダウンロードするやり方ができないかを試していきました。 手順が少なくてすみそうなやり方で進めてみます。適当にフォルダを作成し、その中にダウンロードした機械学習モデル関連のファイル一式を置きましょう(ここで「関連ファイル」と書いた部分の詳細は、冒頭に掲載した記事の「モデルのダウンロード」の部分をご参照ください)。 そして、そのファイル一式を置いたフォルダで、サーバーを立ち上げます。冒頭に掲載した記事の「機械学習モデルのファイルに関する準備をする(+ローカルでサーバーを動かす)」の部分にも書いている、ワンライナーでサーバーを立ち上げるコマンドを使うのが簡単かと思います。 以下にコマンドの部分のみ記載しますが、どれを使っても http://localhost:8080/ でローカルにサーバーが立ち上がります。 # 2.x系 $ python -m SimpleHTTPServer 8080 # 3.x系 $ python -m http.server 8080 # npmのバージョン5.2.0で導入された「npx」を利用 $ npx install http-server このコマンドを機械学習モデル関連のファイルが置かれた場所で実行していれば、 http://localhost:8080/ にアクセスすることで、機械学習モデル関連のファイルを参照可能になります。app.js の中の URL を指定する部分は、以下のように書きかえれば OK です。 function configureEndPoints() { addEndpoint('test', 'http://localhost:8080/'); } addEndpoint('test', ... の部分は、この後の手順に関わってくる設定ですが、デフォルトのままで使っていきます。 以下に、ここで用いたソースコード全体を掲載します(サンプルをほぼそのまま使ったような感じですが)。 const canvas = require("canvas"); const express = require("express"); const JSDOM = require("jsdom").JSDOM; require("@tensorflow/tfjs-node"); const tmImage = require("@teachablemachine/image"); const app = express(); function init() { configureBodyParser(); configureEndPoints(); configureBrowserPolyFills(); app.listen(3000, () => { console.log("Server running on port 3000"); }); } async function addEndpoint(name, baseUrl) { const modelURL = baseUrl + "model.json"; const metadataURL = baseUrl + "metadata.json"; const model = await tmImage.load(modelURL, metadataURL); app.post("/" + name, (request, response) => { const base64Image = Buffer.from(request.body).toString("base64"); const contentType = request.get("Content-Type"); getPrediction(model, base64Image, contentType, (output) => { response.send(output); }); }); } function configureBodyParser() { app.use(require("body-parser").raw({ type: "image/jpeg", limit: "3MB" })); } function configureBrowserPolyFills() { global.window = new JSDOM(` <body> <script> document.body.appendChild(document.createElement("hr")); </script> </body>`).window; global.document = window.document; global.fetch = require("node-fetch"); global.HTMLVideoElement = class HTMLVideoElement {}; } function configureEndPoints() { addEndpoint("test", "http://localhost:8080/"); } async function getPrediction(model, imageData, contentType, responseFunction) { const imageCanvas = canvas.createCanvas(64, 64); const canvasContext = imageCanvas.getContext("2d"); const canvasImage = new canvas.Image(); canvasImage.onload = async () => { canvasContext.drawImage(canvasImage, 0, 0, 64, 64); const prediction = await model.predict(imageCanvas); console.log(prediction); responseFunction(prediction); }; canvasImage.onerror = (error) => { throw error; }; canvasImage.src = `data:${contentType};base64,` + imageData; } init(); Node.js でプログラムを実行 ここまで準備ができたら、app.js を実行します。 node app.js を実行すると以下のような出力がされて、先ほどローカルで実行したサーバーとは別のサーバーが待ち受け状態になります(こちらのサーバーのポート番号は、app.js のデフォルト設定を変更せずにいた場合 3000番になります)。 画像をポストして結果を得る あとは、先ほどの app.js を実行して立ち上がったサーバーのほうに、画像を POST で送信すれば OK です。今回参照している GitHub のリポジトリのページでは、curl を利用した方法のコマンドの例が掲載されています。 自分は Mac で試しているので、上記の上側のコマンド例を利用しました。とりあえず動作確認ができれば良かったので、そのコマンド(※以下)をそのまま流用します。 以下のコマンドで、POST する画像のパスが '@./sample_images/person-using-iphone-1194760.jpg' となっていますが、これは今回クローンしたリポジトリのファイル・フォルダ一式の中で、app.js と同じ階層にあるフォルダの中に置かれた画像の 1つを指定しているようです。 $ curl -X POST -H "Content-Type: image/jpeg" --data-binary '@./sample_images/person-using-iphone-1194760.jpg' http://localhost:3000/test リポジトリのほうで具体的に見ていくと、 teachablemachine-node-example/sample_images/ の中にある画像のうちの 1つです。 これをそのまま流用するために、上記の curlコマンドを実行する場所は app.js と同じ階層にします。これで既に用意されている画像を流用して POST を実行できそうです。試してみた際の出力は、以下となりました。 上記のとおり、レスポンスとして推論の結果が JSON形式で得られました。 今回使ったモデルは「Class 1、Class 2」という、Teachable Machine のデフォルト設定のクラス名を使った 2つのクラスを用意していたのですが、出力ではそれらのラベル対する confidence の値が出力されたのが確認できたと思います。 終わりに 今回、Teachable Machine で出力した画像分類用の機械学習モデル(TensorFlow.js用)を Node.js で動かすことができました。 個人的には「もう少しだけ手順をシンプルにできないかな」と思う部分があるので、改善の余地がないかをもう少し見ていこうと思っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

30分で習うより慣れる! React + Redux Toolkit + TypeScript の Todoアプリチュートリアル その1

はじめに 最近 React を勉強し始めた新参者です。 「とりあえず TypeScript で始めよう → Redux って便利そう → Redux Toolkit ってのがあるのか~ ・・・・・・・・・ 」 という具合にインターネットを潜ってチュートリアル始めたら、情報が多かったり古かったりEnglishだったりでカオスったので、習うより慣れるチュートリアルをここに残します。 習うより慣れたい皆様の参考になれば幸いです。 環境 % sw_vers ProductName: Mac OS X ProductVersion: 10.15.7 //Catalinaさん、そろそろ上げないと、、、 BuildVersion: 19H2 % brew -v Homebrew 3.0.11 % nodebrew -v nodebrew 1.0.1 % node -v v14.16.1 % yarn -v 1.22.10 Start!! プロジェクト作成 Redux+TypeScript テンプレートを使用 こちら yarn create react-app redux-sample --template redux-typescript 作成したプロジェクトへ移動 cd redux-sample テンプレートに含まれてるRedux関連のモジュール達です % ls -d -e node_modules/*redux* node_modules/@reduxjs node_modules/react-redux node_modules/redux node_modules/redux-thunk Redux を TypeScript に対応させるやつをインストールします (雑 yarn add typescript-fsa typescript-fsa-reducers 以下の構成が出来上がります ~/redux-sample % tree -I ".DS_Store|node_modules|.git" -La 4 --dirsfirst . ├── public │   ├── favicon.ico │   ├── index.html │   ├── logo192.png │   ├── logo512.png │   ├── manifest.json │   └── robots.txt ├── src │   ├── app │   │   ├── hooks.ts │   │   └── store.ts │   ├── features │   │   └── counter │   │   ├── Counter.module.css │   │   ├── Counter.tsx │   │   ├── counterAPI.ts │   │   ├── counterSlice.spec.ts │   │   └── counterSlice.ts │   ├── App.css │   ├── App.test.tsx │   ├── App.tsx │   ├── index.css │   ├── index.tsx │   ├── logo.svg │   ├── react-app-env.d.ts │   ├── serviceWorker.ts │   └── setupTests.ts ├── .gitignore ├── README.md ├── package.json ├── tsconfig.json └── yarn.lock これを以下に変更します (こちらはお好みでどうぞ) . ├── public │   └── # 省略 ├── src │   ├── app │   │   ├── hooks.ts │   │   └── store.ts │   ├── features │   │   └── counter │   │      ├── counterAPI.ts │   │      ├── counterSlice.spec.ts │   │      └── counterSlice.ts │   ├── pages # <-- 作成 │   │   ├── assets # <-- 作成 │   │   │   └── logo.svg # <-- 移動 │   │   ├── counter # <-- 作成 │   │   │   ├── Counter.module.css # <-- 移動 │   │   │   └── Counter.tsx # <-- 移動 │   │   ├── App.css # <-- 移動 │   │   ├── App.test.tsx # <-- 移動 │   │   └── App.tsx # <-- 移動 │   ├── index.css │   ├── index.tsx │   ├── react-app-env.d.ts │   ├── serviceWorker.ts │   └── setupTests.ts └── # 省略 既存のソースをちょっと修正する src/index.tsx import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './pages/App'; // <-- 修正 // 以下省略 src/pages/App.tsx import React from 'react'; import logo from './assets/logo.svg'; // <-- 修正 import { Counter } from './counter/Counter'; // <-- 修正 import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> {/* --- ここから削除 --- <Counter /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> --- ここまで --- */} <span> // 以下省略 src/pages/counter/Counter.tsx import React, { useState } from 'react'; // 省略 } from '../../features/counter/counterSlice'; // <-- 修正 import styles from './Counter.module.css'; export function Counter() { //省略 const incrementValue = Number(incrementAmount) || 0; return ( <div> <div className={styles.row}> // 省略 </div> {/* --- ここから追記 --- */} <p> Edit <code>src/pages/counter/Counter.tsx</code> and save to reload. </p> {/* --- ここまで --- */} </div> ); } TodoアプリのUI作成 Todoアプリ用のページディレクトリを作成 mkdir src/pages/todo Todoアプリのベースを作成 src/pages/todo/TodoApp.tsx import React from 'react'; import AddTodo from './components/AddTodo' function TodoApp() { return ( <div> <AddTodo /> <p> Edit <code>src/pages/todo/TodoApp.tsx</code> and save to reload. </p> </div> ); }; export default TodoApp; Todo 入力用の Input と Button を作成 src/pages/todo/components/AddTodo.tsx import React from "react"; export default function AddTodo(): JSX.Element { const [text, setText] = React.useState(""); function handleChange(e: { target: HTMLInputElement }) { setText(e.target.value); } function handleSubmit(e: any) { e.preventDefault(); if (!text.trim()) { return; } setText(""); } return ( <form onSubmit={handleSubmit}> <input value={text} onChange={handleChange} /> <button type="submit">Add Todo</button> </form> ); } ルーティングの実装 ルーティングモジュールをインストール yarn add react-router-dom @types/react-router-dom App.tsx から各ページを URL 毎に読み込むように修正 src/pages/App.tsx import React from 'react'; import logo from './assets/logo.svg'; import { BrowserRouter as Router, Route } from 'react-router-dom'; // <-- 追記 import { Counter } from './counter/Counter'; import Todo from './todo/TodoApp'; // <-- 追記 import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> // --- ここから追記 --- <Router> <Route exact path="/" component={Counter} /> <Route exact path="/todo" component={Todo} /> </Router> // --- ここまで --- // 省略 </header> </div> ); } export default App; ロゴの主張が強いので以下の css を修正 src/App.css .App-logo { height: 20vmin; /* 40 --> 20 */ /* 省略 */ } .App-header { min-height: 10vh; /* 100 --> 10 */ /* 省略 */ } 起動して見た目を確認 yarn run start localhost:3000/ にアクセスしたときの見た目 (サンプルソース) localhost:3000/todo にアクセスしたときの見た目 Todoを追加できるようにする Todoリストの componet を作成 Todoリスト src/pages/todo/components/TodoList.tsx import * as React from "react"; import TodoItem from './TodoItem' export default function TodoList() { const text: any = "" return ( <ul> <TodoItem {...text} /> </ul> ); } Todo単体 src/pages/todo/components/TodoItem.tsx import * as React from "react"; interface TodoProps { text: string; } export default function TodoItem( { text }: TodoProps ) { return ( <li> {text} </li> ); } 作成した Todo Components を読み込む src/pages/todo/TodoApp.tsx import React from 'react'; import AddTodo from './components/AddTodo' import TodoList from './components/TodoList' // <-- 追記 export default function TodoApp() { return ( <div> <AddTodo /> <TodoList /> {/* <-- 追記 */} <p> Edit <code>src/pages/todo/TodoApp.tsx</code> and save to reload. </p> </div> ); }; 起動して見た目を確認 Todoリストの表示部分ができました 入力した値をTodoリストに出力する Slice を作成 src/features/todo/todoSlice.ts import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { AppThunk, AppDispatch, RootState } from "../../app/store"; import { Todo } from './types' const initialState: Todo[] = []; const todoSlice = createSlice({ name: 'todos', initialState, reducers: { addTodo(state, action: PayloadAction<Todo>) { state.push(action.payload); }, } }); export const addTodo = (text: string): AppThunk => async ( dispatch: AppDispatch ) => { const newTodo: Todo = { id: Math.random().toString(36).substr(2, 9), text: text }; dispatch(todoSlice.actions.addTodo(newTodo)); }; export default todoSlice.reducer; Todo Interface type を作成 src/features/todo/types.ts export interface Todo { id: string; text: string; } Reducer を作成 src/app/rootReducer.ts import { combineReducers } from "@reduxjs/toolkit"; import counter from "../features/counter/counterSlice"; import todos from "../features/todo/todoSlice"; const rootReducer = combineReducers({ counter, todos }); export type RootState = ReturnType<typeof rootReducer>; export default rootReducer; Store を修正 src/app/store.ts import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit' import rootReducer from "./rootReducer"; export const store = configureStore({ reducer: rootReducer }); export type AppDispatch = typeof store.dispatch; export type RootState = ReturnType<typeof store.getState>; export type AppThunk<ReturnType = void> = ThunkAction< ReturnType, RootState, unknown, Action<string> >; 各 Todo Components を修正して結合する src/pages/todo/components/AddTodo.tsx import React from "react"; import { useDispatch } from "react-redux"; // <-- 追記 import { addTodo } from "../../../features/todo/todoSlice"; // <-- 追記 export default function AddTodo(): JSX.Element { const dispatch = useDispatch(); // <-- 追記 const [text, setText] = React.useState(""); function handleChange(e: { target: HTMLInputElement }) { setText(e.target.value); } function handleSubmit(e: any) { e.preventDefault(); if (!text.trim()) { return; } dispatch(addTodo(text)); // <-- 追記 setText(""); } return ( <form onSubmit={handleSubmit}> <input value={text} onChange={handleChange} /> <button type="submit">Add Todo</button> </form> ); } src/pages/todo/components/TodoList.tsx import * as React from "react"; import { useSelector } from "react-redux"; // <-- 追記 import { RootState } from "../../../app/rootReducer" // <-- 追記 import { Todo } from "../../../features/todo/types"; // <-- 追記 import TodoItem from './TodoItem' //export default function TodoList() { <-- 削除 // const text: any = ""; <-- 削除 export default function TodoList() { // <-- 追記 const todos: Todo[] = useSelector((state: RootState) => // <-- 追記 state.todos // <-- 追記 ); // <-- 追記 return ( <ul> {/* --- 追記 --- */} {(todos || []).map(todo => ( <TodoItem {...todo} /> ))} {/* --- ここまで --- */} {/* <TodoItem {...text} /> <-- 削除 */} </ul> ); } 起動して動作確認 最後に いかがでしたでしょうか、慣れましたか? こちらのサイトがとても分かりやすく解説してくれています。感謝(-人-) おそらく、この後にこちらをみると理解が深まると思います。 皆様の助けになると幸いです。 チュートリアル通りだと、その2以降は、タスクを完了させたり、タスクの完了・未完了のフィルタリングですが、そのうち投稿したいとおもうのですが、もう皆さんなら出来ると思うのです。 断じて力尽きたわけではありません。私もこれから勉強していきます(><) 今回のソースはこちらに置いておきます でわ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MoneyForwardのruleの保存

MoneyForward の 分類ルール をcsvで保存 (Chromeのconsole使用) ちょっと整理するためにCSVに保存。その時のメモ。 var downloadAsTextFile = function(fileName, content) { var a = document.createElement('a'); a.download = fileName; a.href = (window.URL || window.webkitURL).createObjectURL(new Blob([new Uint8Array([0xEF, 0xBB, 0xBF]), content])); document.body.appendChild(a); a.click(); document.body.removeChild(a); }; var downloadAsCSVFile = function(fileName, rows) { var content = ""; for(var i in rows) { for (var j = 0, m = rows[i].length; j < m; ++j) content += '"' + ("" + rows[i][j]).replace(/"/g, '""') + '"' + (j !== m ? ',' : ''); content += '\n'; } downloadAsTextFile(fileName, content); }; rows = []; $("section table:has(th:contains('金融機関') ~ th:contains('大項目')) tr").each(function() { row = []; $(this).find("th, td").each(function() { row.push($(this).text()); }); rows.push(row); }); downloadAsCSVFile("rule.csv", rows);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数計測対応ストップウォッチ

複数の計測ユニットを表示してそれぞれ別々にタイム計測できます。 LAP機能は親ユニットのタイム/スプリットタイムを計測停止状態でコピーしたユニットを追加表示することでLAPとしています。 動作デモ HTML index.html <!DOCTYPE html> <html lang='ja'> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width,initial-scale=1'> <title>stopwatch multiple</title> <link rel='stylesheet' href='style.css'> <script src='script.js'></script> </head> <body> <div class='allControl'> <button id='allReset'>ALL RESET</button> <button id='allStop'>ALL STOP</button> <button id='allStart'>ALL START</button> </div> <div id='container'> </div> </body> </html> CSS style.css .unit { position: relative; width: fit-content; border-radius: 3px; margin: 12px; padding: 8px; display: inline-block; background: #ddd; } .view { font-size: 30px; text-align: right; padding-right: 4px; line-height: 100%; padding-top: 8px; } .lap { font-size: 16px; text-align: right; padding-right: 4px; } .reset, .start { height: 30px; font-size: 16px; cursor: pointer; padding: 0; } .reset { width: 75px; } .start { width: 125px; } .unit button, #allReset, #allStop, #allStart { border-radius: 3px; background-color: #fff; -webkit-appearance: none; -webkit-user-select: none; border: 0; } .close, .add { width: 25px; height: 25px; padding: 0; } .unit .close { background-color: #faa; color: #fff; } .allControl { position: fixed; left: 0; bottom: 0; width: 100%; background-color: #ddd8; padding: 8px; z-index: 1; } #container { padding-bottom: 40px; } #allReset { border: 1px #ccc solid; background-color: #fdd; } #allStop, #allStart { position: relative; left: 20px; } #allStop { border: 1px #c88 solid; } #allStart { border: 1px #8c8 solid; } .caption { width: 138px; border: 0; } JavaScript script.js 'use strict'; const storageKey = 'stopWatch_Multiple', s = localStorage.getItem(storageKey), storage = s !== null ? JSON.parse(s) : [], obj = {}, cEvent = window.ontouchstart !== undefined ? 'touchstart' : 'mousedown'; let unitId = 0; window.addEventListener('DOMContentLoaded', function() { // localStorageに状態保持データがあれば復元 if(storage.length) { let tmp = 0; for(let i in storage) { addUnit(storage[i]); if(storage[i].id > tmp) tmp = storage[i].id; } unitId = tmp + 1; } // 無ければ新規ユニット1個追加 else { addUnit(); } count(); // 停止中の全ユニットスタート document.getElementById('allStart').addEventListener('click', function() { const now = Date.now(); for(let i in obj) { if(obj[i].sTime <= 0) { obj[i].sTime += now; obj[i].lTime += now; obj[i].start.textContent = 'STOP'; obj[i].reset.textContent = 'LAP'; } } setStorage(); }); // 動作中の全ユニットストップ document.getElementById('allStop').addEventListener('click', function() { const now = Date.now(); for(let i in obj) { if(obj[i].sTime > 0) { obj[i].sTime -= now; obj[i].lTime -= now; obj[i].start.textContent = 'START'; obj[i].reset.textContent = 'RESET'; } } setStorage(); }); // リセット document.getElementById('allReset').addEventListener('click', function() { if(confirm('状態保持データを削除して計測ユニットをリセットします')) { allReset(); } }); }); function allReset() { localStorage.removeItem(storageKey); for(let i in obj) delete obj[i]; unitId = 0; document.getElementById('container').innerHTML = ''; addUnit(); } function count() { const now = Date.now(); for(let i in obj) { const o = obj[i]; if(o.sTime > 0) { o.view.textContent = timeString(now - o.sTime); o.lap.textContent = timeString(now - o.lTime); } } requestAnimationFrame(count); } function timeString(sTime) { const str = timeFormat(sTime); return str; } function timeFormat(time) { return Math.floor(time / 36e5) + ':' + new Date(time).toISOString().slice(14, 22); } // ユニット追加 function addUnit(storageObj) { unitId++; const id = storageObj !== undefined ? storageObj.id : unitId; document.getElementById('container').appendChild(document.createElement('div')); document.getElementById('container').lastChild.outerHTML = "<div class='unit' id='u" + id + "'>" + " <input type='text' class='caption' placeholder='-- No Caption --'>" + " <button class='add'>+</button>" + " <button class='close'>x</button>" + " <div class='view'>0:00:00.00</div>" + " <div class='lap'>0:00:00.00</div>" + " <button class='reset'>RESET</button>" + " <button class='start'>START</button>" + "</div>"; const unit = document.getElementById('u' + id); obj[id] = { id : id, close : unit.querySelector('.close'), add : unit.querySelector('.add'), reset : unit.querySelector('.reset'), start : unit.querySelector('.start'), view : unit.querySelector('.view'), lap : unit.querySelector('.lap'), caption: unit.querySelector('.caption'), sTime : storageObj !== undefined ? storageObj.sTime : 0, lTime : storageObj !== undefined ? storageObj.lTime : 0, }; if(Object.keys(obj).length === 1 && !(storageObj !== undefined && storage.length > 1)) { closeButtonDisplay(false); } const o = obj[id]; if(o.sTime > 0) { o.start.textContent = 'STOP'; o.reset.textContent = 'LAP'; } if(storageObj !== undefined) { o.caption.value = storageObj.caption; if(o.sTime < 0) { o.view.textContent = timeString(-o.sTime); o.lap.textContent = timeString(-o.lTime); } } // クローズボタン o.close.addEventListener('click', function() { document.getElementById('container').removeChild( document.getElementById('u' + o.id) ); delete obj[o.id]; if(Object.keys(obj).length === 1) { closeButtonDisplay(false); } setStorage(); }); // 追加ボタン o.add.addEventListener('click', function() { addUnit(); if(Object.keys(obj).length === 2) { closeButtonDisplay(true); } setStorage(); }); // リセットボタン o.reset.addEventListener(cEvent, function() { const now = Date.now(); // 停止中 if(o.sTime <= 0) { // リセット o.sTime = o.lTime = 0; o.view.textContent = timeString(o.sTime); o.lap.textContent = timeString(o.lTime); } // 計測中 else { // LAP(メイン及びスプリットタイムを新規ユニットに計測停止状態で複製) const newCaption = o.caption.value.replace(/(:Lap)?$/, ':Lap'); addUnit({ id : unitId + 1, caption: newCaption, sTime : o.sTime - now, lTime : o.lTime - now, }); // 親ユニットのスプリットタイムをリセット o.lTime = now; if(Object.keys(obj).length === 2) { closeButtonDisplay(true); } } setStorage(); }); // スタートボタン o.start.addEventListener(cEvent, function() { const now = Date.now(); // 計測中 if(o.sTime > 0) { // 計測停止 o.sTime -= now; o.lTime -= now; o.view.textContent = timeString(-o.sTime); o.lap.textContent = timeString(-o.lTime); o.start.textContent = 'START'; o.reset.textContent = 'RESET'; } // 停止中 else { // 計測開始・再開 o.sTime += now; o.lTime += now; o.start.textContent = 'STOP'; o.reset.textContent = 'LAP'; } setStorage(); }); // キャプション o.caption.addEventListener('input', function() { setStorage(); }); } function closeButtonDisplay(f) { document.querySelector('#container .unit .close').style.display = f ? '' : 'none'; } function setStorage() { const tmp = []; for(let i in obj) { tmp.push({ id : obj[i].id, sTime : obj[i].sTime, lTime : obj[i].lTime, caption: obj[i].caption.value }); } // ユニット1個、時間0で停止、キャプションなしの場合は削除 if(tmp.length === 1 && tmp[0].sTime === 0 && tmp[0].caption === '') { localStorage.removeItem(storageKey); } else { localStorage.setItem(storageKey, JSON.stringify(tmp)); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptを学ぶ―DOM操作とは―

DOMとは Document Object Model のこと DOMはHTMLをツリー構造で表現をします。 下記のコードをツリー工場にして「ねこ」までを辿ると html ➡ body ➡ article ➡ ul ➡ li ➡ ねこ となります。 <html> <head> <title>ぼくのサイト</title> </head> <body> <article> <h1>すきなどうぶつ</h1> <ul> <li>ねこ</li> <li>いぬ</li> <li>とり</li> </ul> </article> </body> </html> ブラウザ内にDOMデータが構築されます。 このDOMを取得し、内容を書き換える操作のことをいいます。 DOMが書き換えられると、自動的に新たなDOMに合わせてブラウザの画面も変わります。 Document オブジェクトとは JavaScriptを学習し始めてからしょっちゅう登場するdocument.getElementById('the-button');ですが、documentとは何でしょうか? documentオブジェクトは、画面に表示されているWEBページを表すオブジェクトでDOM操作をするためのメソッドが含まれています。 document.getElementById('the-button'); この例だと、DOM構造に分解し、DOM構造内を検索し、id名「the-button」のノードを取得していをます。 メソッドには getElementById('the-button') id名で検索 querySelector('#the-button') cssセレクタで最初に一致したものを検索 querySelectorAll('.button') cssセレクタで一致したすべてを検索 等がある。 最後に Javaをこれまで学習してからJavaScriptに手を出しましたが、考え方が違いすぎて理解がなかなかできません。 そのために基礎的なことを簡潔にまとめてみました。 https://www.hypertextcandy.com/vanilla-javascript-dom こちらの記事を参考にさせて頂きました。 ありがとうございます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

余計なフレームワーク、ライブラリ抜きの Vanilla Web Project 20本でHTML5、CSS、JSを楽しくお得に学ぶ

Vanilla Web Project。 HTML5、CSS、JavaScriptのみで構築された20 (+増量予定と書いてある) のミニプロジェクトらしい。GitHubから引用。 # Project Live Demo 01 Form Validator Live Demo 02 Movie Seat Booking Live Demo 03 Custom Video Player Live Demo 04 Exchange Rate Calculator Live Demo 05 DOM Array Methods Project Live Demo 06 Menu Slider & Modal Live Demo 07 Hangman Game Live Demo 08 Mealfinder App Live Demo 09 Expense Tracker Live Demo 10 Music Player Live Demo 11 Infinite Scrolling Live Demo 12 Typing Game Live Demo 13 Speech Text Reader Live Demo 14 Memory Cards Live Demo 15 LyricsSearch App Live Demo 16 Relaxer App Live Demo 17 Breakout Game Live Demo 18 New Year Countdown Live Demo 19 Sortable List Live Demo 20 Speak Number Guessing Game Live Demo Vanilla Web Project とは このコースは、純粋で、実用的で、楽しいプロジェクトベースのコースです。すべてのプロジェクトは異なり、繰り返し行われるプラクティスを使用しますが、それらはそれぞれ新しいことを学ぶためのものです。フレームワークやライブラリは使用しません。HTML、CSS、JavaScriptを含むすべてのプロジェクトをゼロから構築します。 プロジェクトは将来的に更新・追加される予定です。最終的には、「50のWebプロジェクト」というタイトルで、より高度なプロジェクトも含めたコースにしたいと考えています。 とのこと。初心者から中級者までを対象に作られており、HTML、CSSとJavaScriptの基本的な知識があれば楽しめる。 HTML/CSSによるレイアウトやUIの作成 基本的なJSを実際のプロジェクトで使う CSS アニメーションと JS トリガー DOMの選択と操作 JavaScriptイベント 配列メソッド - map()、filter()、reduce()など データの取得とサードパーティのAPIとの連携 JSON プロミスと非同期/待ち時間 ローカルストレージ ドラッグ&ドロップ 音声認識 音声合成 キャンバスAPIとアニメーション というのが以上、(DeepL翻訳での翻訳と) サマリ。 具体的にどう楽しむか ソースを丁寧に読むとよさそうだったので読んだ。以下つまみ食いメモ。 Dom Array Methods 抜粋.js // Fetch random user and add money async function getRandomUser() { const res = await fetch('https://randomuser.me/api'); const data = await res.json(); const user = data.results[0]; const newUser = { name: `${user.name.first} ${user.name.last}`, money: Math.floor(Math.random() * 1000000) }; addData(newUser); } なんて部分がありますが、以下を発見した。Random User Generator などというAPIがあるのですね。 例.js var res; var data; var user; res = await fetch('https://randomuser.me/api'); data = await res.json(); user = data.results[0]; console.log(user); でお試し可能。 Exchange Rate 抜粋.js function calculate() { const currency_one = currencyEl_one.value; const currency_two = currencyEl_two.value; fetch("https://open.exchangerate-api.com/v6/latest") .then(res => res.json()) .then(data => { // console.log(data); const rate = data.rates[currency_two] / data.rates[currency_one]; rateEl.innerText = `1 ${currency_one} = ${rate} ${currency_two}`; amountEl_two.value = (amountEl_one.value * (rate)).toFixed(2); }); } こちらも などというAPIを使っている。 Memory Card こちらはlocalStorageが何なのかを知るにはよさそう。 // Get cards from local storage function getCardsData() { const cards = JSON.parse(localStorage.getItem('cards')); return cards === null ? [] : cards; } // Add card to local storage function setCardsData(cards) { localStorage.setItem('cards', JSON.stringify(cards)); window.location.reload(); } // Clear cards button clearBtn.addEventListener('click', () => { localStorage.clear(); cardsContainer.innerHTML = ''; window.location.reload(); }); LocalStorageは色々注意。 Infinite Scroll こちらもどうやらこんなものを使っている。 抜粋.js // Fetch posts from API async function getPosts() { const res = await fetch( `https://jsonplaceholder.typicode.com/posts?_limit=${limit}&_page=${page}` ); const data = await res.json(); return data; } window.addEventListener('scroll', () => { const { scrollTop, scrollHeight, clientHeight } = document.documentElement; if (scrollTop + clientHeight >= scrollHeight - 5) { showLoading(); } }); // Show posts in DOM async function showPosts() { const posts = await getPosts(); posts.forEach(post => { const postEl = document.createElement('div'); postEl.classList.add('post'); postEl.innerHTML = ` <div class="number">${post.id}</div> <div class="post-info"> <h2 class="post-title">${post.title}</h2> <p class="post-body">${post.body}</p> </div> `; postsContainer.appendChild(postEl); }); } // Show loader & fetch more posts function showLoading() { loading.classList.add('show'); setTimeout(() => { loading.classList.remove('show'); setTimeout(() => { page++; showPosts(); }, 300); }, 1000); } 等など。動かしてみると少し分かる。 そのほか副産物 色々なPlaceholder発見。 Udemyの教材らしい 内容が充実しているのはそのはず、Udemyのコースの題材だからのようですが、GitHubを覗くだけなら上述のとおり。お得。 以上 GitHubのTrending に居たのを見つけたのでメモでした。お役に立てばさいわいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript + Node.js(Express)でポートフォリオを作ってみた

はじめに フロントエンドはJavaScript、Bootstrap、バックエンドはNode.js(FW:Express)といった構成です。 この記事では、アプリ開発にあたって苦労した点や、 各機能実装の際に参考にした記事や教材についてもご紹介していければと思います。 アプリの概要 LINEをプラットフォームに「自動予約ツール」をテーマにしたアプリです。 日本国民の8000万人が使っているLINEで店舗の予約を自動受付 LINE上で予約の取得、次回予約の確認、予約のキャンセルが可能であり、ユーザーが簡単に操作できる 制作背景 私の現職(医療、サービス)での課題が、プログラミングを学習スタートさせたきっかけです。 Web制作会社と連携して、新規顧客の獲得のため、SEO、MEO対策に取り組み、 その効果は大きく、アクセス数は増加し、新規顧客の多くはHPから獲得することができ、 日々、公式LINE、メールにてたくさんの患者さんからのご予約、問い合わせが寄せられます。 しかし、ここで問題が発生・・・ メールや公式LINEでの問合せが増加した事で、スタッフの対応が遅延しクレームに繋がる事もあったのです。 自動予約を受付する機能を作って!!と会社に提案しましたが、見事通らず。 エンジニアの友人に話を持ちかけた時に、自分で作ればいいんじゃない?と言われ、 たしかに(笑)となったことが背景となります。 使用画面のイメージ 予約の取得/次回予約の確認/予約のキャンセル 管理画面でのCRUD このアプリの特徴 リッチメニューを選択することで FlexMessageにより自動対話で選択されるボタンを配置して、 どんな操作が可能なのか直感的に認識してもらえるようにしました。 予約の取得、次回予約の確認、予約のキャンセルがLINE上で操作 管理画面でユーザーネーム、施術時間の可変を行える 使用技術 フロントエンド HTML / EJS / Bootstrap / CSS JavaScript バックエンド Node.js FW:Express LINEMessagingAPI インフラ Heroku PostgresSQL その他 Fontawesome Postman ngrok FlexMessage Simulator バックエンドのロジックはExpressでプログラミングし、 フロントの大枠はBootstrapとCSSで整え、JavaScriptでのコーディングもしています。 LINEBOTは適宜、動作確認が必要となりますのでngrokを使いました。 ローカルPC上で稼働しているネットワーク(TCP)サービスを外部公開できるサービスです。この存在を知り、開発がスムーズになりました。 選定理由 実装したい機能として3点ありました。 管理画面にて施術時間を可変、ユーザーの削除ができる 予約を自動対話で行えるようにする  ⑴来店希望日を表示  ⑵希望時間帯の表示 ユーザーが次回予約の確認、キャンセルをLINE上で行える このような機能があれば、LINEBOTでの自動予約受付として成立すると考えました。そして、そのために必要な技術を自身なりに調べてみたところ、フロントとバックを両方で活用できるのがJavaScriptである事がわかり、現役のエンジニアの方にもDMで実現可能か確認して、JavaScriptで実装することを決めました。 システムの全体像 今回の趣旨としてLINEBOTを制作することなのでインフラは公開が初学者でも扱いやすそうなHeroku、データベースはHerokuのアドオンであるPostgresSQLを選択。 機能一覧 CRUD処理 患者さんによって治療内容や時間が異なるケースがあるため、管理者が施術時間を変更できる LINEに登録している、ユーザーネームが管理画面に表示され、登録されたLINE登録している名前で、誰が予約をしたのか判断できない場合もあるため、管理者が変更できるようにした リッチメニューから予約の取得 リッチメニューから次回予約の確認 患者さんから次回の予約はいつでした?と問い合わせがあったことがきっかけで、LINE上で確認がとれたら楽になると思い実装し、従業員の負担も軽減できる事も考慮しました。 リッチメニューから予約のキャンセル 就業時間外でのキャンセルもあるため、自由にキャンセルできるように機能をつけました。 ドラック&ドロップ 管理画面にて、施術時間を表示したカードをドラック&ドロップできる 苦労したこと 選んだ日付、選んだ時間帯の内容をpostback.dataに記録していかなければならなかった点です。ここをアンパサンド(&)で数珠繋ぎのように過去の選択を記録していく方法をとりました。 また、次回予約を確認する時に、postback.dataで返ってきたデータが全て文字列型なので時間の表示を全て数字型に変換する点に苦労しました。 データベースを今まで学習してこなかったため、設計がさっぱりわからずでした(笑) MVCモデルの理解 知識定着のために、Qiitaに投稿しました。 参考にした学習教材 / 参考サイト 基本的にドットインストールとUdemyの動画教材でおおまかな流れをインプットし、公式ドキュメントで必要なメソッドを検索しながら行いました。またQiitaの記事で環境構築などとても参考になりました。 またMENTAのサービスで実装したいアプリに特化したメンターを契約し適宜、分からない点など相談にのっていただきました。本当にお世話になり、返信もはぐれメタル級に早く、感謝の気持ちでいっぱいです。 Node.js / Express 【Udemy】 Node.js速習講座 Part1 導入&基礎編 【Udemy】 Node.js速習講座 Part2 Express編 JavaScript 【参考サイト】 MDN 【Udemy】 【JS】初級者から中級者になるためのJavaScriptメカニズム LINEBOT環境構築 【LINE Developers】 LINEDevelopers公式 【Qiita】 1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest 今後の課題 FlexMessageを1時間単位で指定しているため、予約枠である20分間隔でのメッセージの処理 管理画面へのログイン機能 予約を取得した時の管理画面でのソート機能 Dialogflowというサービスを使い、メッセージに対し学習していくAIの導入を検討しております。患者さんから予約の関係以外で質問やメッセージを受けられるように、自動で応答するAIを育てたいと思っています。 LINEでのFlexMessageをもっと見やすくする方法はないか。 セキュリティ まだまだ課題が多く、完成系には程遠いですがひとつずつ改善していきたいく思います。 少し長くなってしまいましたが、ここまで読んでくださりありがとうございました!!٩( ᐛ )و
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Teachable Machine で出力した機械学習モデル(画像分類、TensorFlow.js用)をダウンロードしてブラウザで動かす【2021年4月版】

内容は表題の通りです。 【追記】 その後、Node.js で実行する話も試して記事にしました。  ●Teachable Machine で出力した機械学習モデル(画像分類、TensorFlow.js用)を Node.js で動かすために公式情報をたどる&実際に試す - Qiita   https://qiita.com/youtoy/items/e47fe723771b308c486e Teachable Machine について 「Teachable Machine」 はブラウザ上で画像・音・姿勢推定の機械学習が行える仕組みで、ブラウザ上で学習を行えるだけでなく、そのままブラウザ上で推論を実行してみることができたり、機械学習モデルをファイルとして出力できたり(また、機械学習モデルを別途利用できるようクラウドにアップロードできたり)もします。 ちなみに、Googleさんが提供しているものです。 扱える種類は、2021年4月17日時点では「画像・音の分類、姿勢推定」の 3つです。 過去に Teachable Machine 関連でやったこと 自分は以前も Teachable Machine を使ったことがあり、その際に例えば以下の記事などを書いたりしました。 toio を音で制御してみた(Audio用の Teachable Machine でベルやタンバリンの音を機械学習) - Qiita 独自拡張版 Scratch で Teachable Machine拡張を使った音(異なる音階)でのロボットカー操作 - Qiita Node-RED で Teachable Machine を試す(Node-RED のセットアップ後にノードを追加してサンプルを動かす) - Qiita 今回扱うのは、3種類の中の画像分類で、実行環境は Webブラウザになります。 今回の記事のメインの内容 ブラウザ上での学習 この記事では、画像分類の学習の過程は省略します。 公式のチュートリアルや、既にいろいろ公開されている Web上の記事でやり方が解説されているので、そちらをご参照ください(例えば以下など)。 ●Google Teachable MachineでAIを体験してみよう | 子ども向けプログラミング学習サイトならメクルン[Mekurun]  https://mekurun.com/tips/teachablemachine/ ●Google Teachable Machine で画像認識|miine|note  https://note.com/y_miine/n/n617a3a793c78 モデルのダウンロード ここでは、画像の学習が終わった後である、という前提で話を進めます。 機械学習モデルの利用方法について、「クラウドにアップロードしてアップロード先の URL を参照して使う方法」と「ファイルとしてダウンロードして自分がホスティングする方法」の 2つがありますが、今回は後者のほうのファイルをダウンロードするやり方で進めていきます。 Teachable Machine の学習等を行うページで、そのページの画面右上に以下の画像で示した部分があるので、赤枠で示した「モデルをエクスポートする」というボタンを押します。 その後の画面では、機械学習モデルの出力形式などが複数選べますが、今回は以下の赤枠で示した「TensorFlow.js」を選び、赤い下線で示した「ダウンロード」を選択して「モデルをダウンロード」と書かれたボタンを押してください。 そうすると ZIPファイルがダウンロードでき、その ZIPファイルを解凍すると以下のような複数のファイルが得られると思います。 これらのファイルを、この後で利用していきます。 HTMLファイル(+JavaScript)と機械学習モデルに関する準備する +α HTMLファイル(+JavaScript) ブラウザで動かすために、HTMLファイル(※JavaScriptの処理が書かれたもの)を用意します。名前は「index.html」にしておくと後が少しだけ楽です。 この HTMLファイル を実行する際には、上の手順でダウンロードした機械学習モデルのファイルに関する準備も必要ですが、それは後で書きます。 以下は、Teachable Machine のモデルダウンロード時に表示されるサンプルを、そのまま使った形です(JavaScript のサンプルとして表示されていたほうの、タイトルを変えたくらいという内容)。この中の const URL = "./my_model/"; という部分は、これ以降の手順に少し関わってきます。 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>画像分類</title> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.3.1/dist/tf.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@teachablemachine/image@0.8/dist/teachablemachine-image.min.js"></script> </head> <body> <h1>Teachable Machine Image Model</h1> <button type="button" onclick="init()">Start</button> <div id="webcam-container"></div> <div id="label-container"></div> <script type="text/javascript"> const URL = "./my_model/"; let model, webcam, labelContainer, maxPredictions; async function init() { const modelURL = URL + "model.json"; const metadataURL = URL + "metadata.json"; model = await tmImage.load(modelURL, metadataURL); maxPredictions = model.getTotalClasses(); const flip = true; webcam = new tmImage.Webcam(200, 200, flip); await webcam.setup(); await webcam.play(); window.requestAnimationFrame(loop); document.getElementById("webcam-container").appendChild(webcam.canvas); labelContainer = document.getElementById("label-container"); for (let i = 0; i < maxPredictions; i++) { labelContainer.appendChild(document.createElement("div")); } } async function loop() { webcam.update(); await predict(); window.requestAnimationFrame(loop); } async function predict() { const prediction = await model.predict(webcam.canvas); for (let i = 0; i < maxPredictions; i++) { const classPrediction = prediction[i].className + ": " + prediction[i].probability.toFixed(2); labelContainer.childNodes[i].innerHTML = classPrediction; } } </script> </body> </html> 以下は、Teachable Machine のモデルダウンロード時に表示されるサンプルで、p5.js という記載で表示されていたサンプルです。 こちらはタイトル以外も少し変更をしていて、p5.min.js と ml5.min.js を記事執筆時点の CDN で指定できるものの最新版にしています。それに伴い、Teachable Machine のサンプルでは読み込むように指定されていた p5.dom.min.js は不要になりました(特定のバージョン以降から p5.min.js の中に取り込まれているようです)。 先ほどと同様、この中の const URL = "./my_model/"; という部分は、これ以降の手順に少し関わってきます。 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>画像分類</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script> <script src="https://unpkg.com/ml5@0.6.1/dist/ml5.min.js"></script> </head> <body> <h1>画像分類</h1> <script> let classifier; let imageModelURL = "./my_model/"; let video; let flippedVideo; let label = ""; function preload() { classifier = ml5.imageClassifier(imageModelURL + "model.json"); } function setup() { createCanvas(320, 260); video = createCapture(VIDEO); video.size(320, 240); video.hide(); flippedVideo = ml5.flipImage(video); classifyVideo(); } function draw() { background(0); image(flippedVideo, 0, 0); fill(255); textSize(16); textAlign(CENTER); text(label, width / 2, height - 4); } function classifyVideo() { flippedVideo = ml5.flipImage(video); classifier.classify(flippedVideo, gotResult); flippedVideo.remove(); } function gotResult(error, results) { if (error) { console.error(error); return; } console.log(results[0]); label = results[0].label; classifyVideo(); } </script> </body> </html> 機械学習モデルのファイルに関する準備をする(+ローカルでサーバーを動かす) ここで、機械学習モデルのファイルに関する準備します。利用するのは、前の手順で ZIPファイルでダウンロードし、その ZIPファイルを解凍して得られた複数のファイルです。 それらのファイルを「my_model」という名前のフォルダに入れて、上記の HTML ファイルと同じ階層に置いてください。 あとは「上の手順で準備した HTMLファイルをブラウザで開くだけ」と言いたいところですが、ファイルをそのままブラウザで開いてしまうと、2種類あったどちらの HTMLファイルでも CORS 関連のエラーが出てしまいます(ブラウザの開発者ツールのコンソールにエラーが表示されます)。 それに対する対応として、ローカルでサーバーを動かしましょう。 やり方は複数ありますが、もし Python が入っているならワンライナーでサーバーを実行できるコマンド(以下のもの)を利用するのが早いと思います。 # 2.x系 $ python -m SimpleHTTPServer 8080 # 3.x系 $ python -m http.server 8080 もし、Python以外を使う方法で Node.js を使うなら、例えば npx install http-server を実行するのが簡単かもしれません(もちろん、それ以外の方法を用いても問題はないです)。 HTMLファイル・「my_model」という名前のフォルダがある階層で上記を実行してから、ブラウザの URL に http://localhost:8080/ を入力して HTMLファイルにアクセスしましょう。これは、HTMLファイルの名前を「index.html」にしている前提で書いていますが、違う名前にしている場合は、先ほどの URL の後にファイル名も指定してアクセスしてください。 2種類ある HTMLファイルのどちらも、ローカルにある機械学習モデルを読み込んで、推論の処理が行われるのを確認できると思います(※画面上での推論結果の表示方法は、それぞれ少し違いがあります)。 終わりに 今回、Teachable Machine で出力した画像分類用の機械学習モデルを、ブラウザ上で簡易に実行できる手順を試しました。 今後は、今回の内容に手を加えたり、その他には Node.js で動かす方法を試せれば、と思っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む