- 投稿日:2020-07-29T22:58:23+09:00
Railsにおける非同期通信
非同期通信(Ajax)とは
ブラウザが再読み込みされること無く通信が行われる通信方法です。
ex.メッセージを投稿→画面をリロードしなくても反映されるJSONとは
データ交換を行うためのデータ記述形式の一種で、キーとバリューの組み合わせでデータを表現します。
AjaxでJSONという型でレスポンスが行われ、ブラウザではJavaScriptが動作しサーバからJSON形式で返されたデータをHTMLにつくりブラウザが書き換えられます。
これらにより、ブラウザの一部だけを更新することができるようになります◎ポイント
① JavaScriptのメソッドを利用してリクエストを送る。
② コントローラでJSON形式のデータを用意するよう準備(コントローラーにアクション記述)
③ レスポンスするためのJSON形式のデータを準備(json.jbuilder)
④ JavaScriptでレスポンスを受け取り、HTMLを操作して追加
非同期通信の実装
respond_toメソッドを使用します!
リクエストがHTMLか、JSONかを条件分岐してくれますrespond_to do |format| format.html { render ... } # HTMLリクエストのときにに呼ばれる format.json { render ... } # JSONリクエストのときに呼ばれる end
- 以下のようにレスポンスできるようにhogesコントローラーに記述していきます リクエストされたformatによって処理を分けます
def create @hoge = モデル名.create(hoge_params) respond_to do |format| format.html { redirect_to :root } format.json { render json: @hoge} #jsファイル側で作成した@hogeを使用するためにrenderメソッドを使用し、作成したhogeをjson形式で返すようにする end end
assets/javascripts配下にhoge.jsファイルを作成する
非同期通信でリクエストする
$(function() { $('.js-form').on('submit', function(e) { e.preventDefault(); var hoge = $('.js-form__text-field').val(); $.ajax({ type: 'POST', url: '/hoges', data: { hoge: { content: hoge } }, dataType: 'json' }) .done(function(data) { var html = $('<li class="hoge">').append(data.content); $('.hoges').append(html); $('.js-form__text-field').val(''); }) .fail(function() { alert('error'); }); }); });
オプション 詳細 Type HTTP通信の種類を記述する。(GET、POST) url リクエストを送信する先のURLを記述 data サーバに送信する値を記述する datatype サーバから返されるデータの型を指定 挙動確認
htmlを生成するvar html = $('
').append(***.content);という処理をメソッドにして切り出します。$(function() { function buildHTML(hoge) { var html = $('<li class="hoge">').append(hoge.content); return html; } $('.js-form').on('submit', function(e) { e.preventDefault(); var hoge = $('.js-form__text-field').val(); $.ajax({ type: 'POST', url: '/hoges', data: { hoge: { content: hoge } }, dataType: 'json' }) .done(function(data) { var html = buildHTML(data); $('.hoges').append(html); $('.js-form__text-field').val(''); }) .fail(function() { alert('error'); }); }); });再度挙動確認して完了です!
- 投稿日:2020-07-29T22:33:05+09:00
obniz×LINE Messaging API】ラジコンをリッチメニューから遠隔操作する。自動ブレーキ付き
やったこと
前回作成した【obniz×LINE Messaging API】音と光を使い侵入防止システムを製作するの時に使用したタッパを乗せて走らせてみました。
できたもの
LINEのリッチメニューから操作でき、10㎝以内に障害物があると自動で停止します。
obniz×LINEBOTを使用して操作するラジコンを製作しました。自動ブレーキを搭載しています。https://t.co/E6R6lLIpXR#obniz pic.twitter.com/Vbud1nzxXd
— Toshiki (@Hirasawa1987) July 29, 2020構成
全体像
配線図
使用部品
obniz Board 1Y
- https://obniz.io/ja/products/obnizboard1y/
- 6,300円(税抜)
シャープ測距モジュール GP2Y0A21YK
2輪ロボットスマートカー
100均のタッパー
USB タイプCのUSB2.0ケーブル(家にあったものを使用)
モバイルバッテリー(家にあったものを使用)
環境
- node.js v14.5.0
- @line/bot-sdk 7.0.0
- express 4.17.1
- obniz 3.7.0
コード
index.js'use strict'; // obniz呼び出し const Obniz = require('obniz'); const obniz = new Obniz('××××-××××'); // Obniz_IDに自分のIDを入れます const line = require("@line/bot-sdk"); const express = require("express"); const PORT = process.env.PORT || 3030; const config = { channelSecret: '{チャネルシークレット}', channelAccessToken: '{アクセストークン}' }; const userId = '{ユーザーid}'; const app = express(); obniz.onconnect = async function () { obniz.display.clear(); obniz.display.print('obniz meets LINE Bot!'); // モーターを呼び出す const motorRight = obniz.wired("DCMotor", { forward: 1, back: 0 }); const motorLeft = obniz.wired("DCMotor", { forward: 8, back: 11 }); // 距離センサーを呼び出す const GP2Y0 = obniz.wired("GP2Y0A21YK0F", {vcc:4, gnd:5, signal:6 }); setInterval(async function () { app.post('/webhook', line.middleware(config), (req, res) => { console.log(req.body.events); Promise .all(req.body.events.map(handleEvent)) .then((result) => res.json(result)); }); const client = new line.Client(config); function forward_command() { motorLeft.forward(); motorRight.forward(); } function stop_command() { motorLeft.stop(); motorRight.stop(); } function reverse_command() { motorLeft.reverse(); motorRight.reverse(); } function turning() { motorLeft.forward(); motorRight.reverse(); } const distance = await GP2Y0.getWait(); console.log(distance + ' mm'); function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } switch(event.message.text) { case '前進': client.replyMessage(event.replyToken, { type: 'text', text: '前進します' }); forward_command(); break; case '停止': client.replyMessage(event.replyToken, { type: 'text', text: '停止しました' }); stop_command(); break; case '後退': client.replyMessage(event.replyToken, { type: 'text', text: '後退します' }); reverse_command(); break; case 'ダンス': client.replyMessage(event.replyToken, { type: 'text', text: '踊ります' }); turning(); break; } } if(distance < 100){ // setTimeout(function () { // client.pushMessage(userId, { // type: 'text', // text: '障害物を検知しました', // }); // },1000); stop_command(); } }, 1000); } app.listen(PORT); console.log(`Server running at ${PORT}`);参考にした記事
LINEmessagingAPIの基礎部分で参考にしました。
1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest終わりに
公式サイトのラジコン類はスマートに動いていて次回の目標となります、できるかどうかはわかりませんが、次に作るときはカメラで画像認識を使用して走行させてみたいと思います。今回購入したキットは、格安品でまっすぐ進むよう調整するのが大変でした。
- 投稿日:2020-07-29T22:33:05+09:00
【obniz×LINE Messaging API】ラジコンをリッチメニューから遠隔操作する。自動ブレーキ付き
やったこと
前回作成した【obniz×LINE Messaging API】音と光を使い侵入防止システムを製作するの時に使用したタッパを乗せて走らせてみました。
できたもの
LINEのリッチメニューから操作でき、10㎝以内に障害物があると自動で停止します。
obniz×LINEBOTを使用して操作するラジコンを製作しました。自動ブレーキを搭載しています。https://t.co/E6R6lLIpXR#obniz pic.twitter.com/Vbud1nzxXd
— Toshiki (@Hirasawa1987) July 29, 2020構成
全体像
配線図
使用部品
obniz Board 1Y
- https://obniz.io/ja/products/obnizboard1y/
- 6,300円(税抜)
シャープ測距モジュール GP2Y0A21YK
2輪ロボットスマートカー
100均のタッパー
USB タイプCのUSB2.0ケーブル(家にあったものを使用)
モバイルバッテリー(家にあったものを使用)
環境
- node.js v14.5.0
- @line/bot-sdk 7.0.0
- express 4.17.1
- obniz 3.7.0
コード
index.js'use strict'; // obniz呼び出し const Obniz = require('obniz'); const obniz = new Obniz('××××-××××'); // Obniz_IDに自分のIDを入れます const line = require("@line/bot-sdk"); const express = require("express"); const PORT = process.env.PORT || 3030; const config = { channelSecret: '{チャネルシークレット}', channelAccessToken: '{アクセストークン}' }; const userId = '{ユーザーid}'; const app = express(); obniz.onconnect = async function () { obniz.display.clear(); obniz.display.print('obniz meets LINE Bot!'); // モーターを呼び出す const motorRight = obniz.wired("DCMotor", { forward: 1, back: 0 }); const motorLeft = obniz.wired("DCMotor", { forward: 8, back: 11 }); // 距離センサーを呼び出す const GP2Y0 = obniz.wired("GP2Y0A21YK0F", {vcc:4, gnd:5, signal:6 }); setInterval(async function () { app.post('/webhook', line.middleware(config), (req, res) => { console.log(req.body.events); Promise .all(req.body.events.map(handleEvent)) .then((result) => res.json(result)); }); const client = new line.Client(config); function forward_command() { motorLeft.forward(); motorRight.forward(); } function stop_command() { motorLeft.stop(); motorRight.stop(); } function reverse_command() { motorLeft.reverse(); motorRight.reverse(); } function turning() { motorLeft.forward(); motorRight.reverse(); } const distance = await GP2Y0.getWait(); console.log(distance + ' mm'); function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } switch(event.message.text) { case '前進': client.replyMessage(event.replyToken, { type: 'text', text: '前進します' }); forward_command(); break; case '停止': client.replyMessage(event.replyToken, { type: 'text', text: '停止しました' }); stop_command(); break; case '後退': client.replyMessage(event.replyToken, { type: 'text', text: '後退します' }); reverse_command(); break; case 'ダンス': client.replyMessage(event.replyToken, { type: 'text', text: '踊ります' }); turning(); break; } } if(distance < 100){ // setTimeout(function () { // client.pushMessage(userId, { // type: 'text', // text: '障害物を検知しました', // }); // },1000); stop_command(); } }, 1000); } app.listen(PORT); console.log(`Server running at ${PORT}`);参考にした記事
LINEmessagingAPIの基礎部分で参考にしました。
1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest終わりに
公式サイトのラジコン類はスマートに動いていて次回の目標となります、できるかどうかはわかりませんが、次に作るときはカメラで画像認識を使用して走行させてみたいと思います。今回購入したキットは、格安品でまっすぐ進むよう調整するのが大変でした。
- 投稿日:2020-07-29T21:25:14+09:00
React Hooksのメモ
・hooksは関数コンポーネント内でstateに値の追加や更新が行えるようにするもの?
・this.setState()のような処理を行う関数を自動で作成してくれる?index.jsimport React, {useState} from "react"; import ReactDOM from "react-dom"; //function App()でも可 var App = function() { //現在の値と値を更新する関数を取得 //useState(n)のnがcntの初期値になる var [cnt, setCount] = useState(0); var addCount = function() { setCount(cnt + 1); } var minusCount = function() { setCount(cnt - 1); } return ( <> <div> <p>Hello React!!</p> <input type="button" value="+1" onClick={() => addCount()} /> <input type="button" value="-1" onClick={() => minusCount()} /> <p>{cnt}</p> </div> </> ) } /* 同じものをクラスコンポーネントで書く */ /* class App extends React.Component { constructor(props) { super(props); this.state = {"cnt": 0}; this.addCount = this.addCount.bind(this); this.minusCount = this.minusCount.bind(this); } addCount() { var cnt = this.state.cnt + 1; this.setState({"cnt": cnt}); } minusCount() { var cnt = this.state.cnt - 1; this.setState({"cnt": cnt}); } render() { return( <div> <p>Hello React!!</p> <input type="button" value="+1" onClick={this.addCount} /> <input type="button" value="-1" onClick={this.minusCount} /> <p>{this.state.cnt}</p> </div> ); } } */ /* クラスコンポーネントと関数コンポーネントで共通 */ ReactDOM.render(<App />, document.getElementById("app"));hooksは複数の状態(this.state)を管理できる。
ここでは{gender: "", place: ""}の状態を管理している。index.jsimport React, {useState} from "react"; import ReactDOM from "react-dom"; var App = function() { //genderの値を書き換えるsetGender()関数が自動で作成される? var [gender, setGender] = useState("男性"); var [place, setPlace] = useState("東京都"); //genderの値を更新する var onClick = function() { if (gender === "男性") { setGender("女性"); } else { setGender("男性"); } } //処理が1行だけなのでonChange={() => setPlace(document.getElementById("place").value()}でも良い var onChange = function() { setPlace(document.getElementById("place").value); } return ( <> <p>性別 : {gender}</p> <p><input type="button" value="性別変更" onClick={() => onClick()} /></p> <p>居住地 : {place}</p> <select id="place" onChange={() => onChange()}> <option value="東京都">東京都</option> <option value="神奈川県">神奈川県</option> <option value="埼玉県">埼玉県</option> </select> </> ) } /* クラスコンポーネントと関数コンポーネントで共通 */ ReactDOM.render(<App />, document.getElementById("app"));
- 投稿日:2020-07-29T21:11:35+09:00
axiosライブラリを用いてクエリパラメータを指定する方法(GET,POST)
はじめに
時々忘れることがあるので、今回も自分用メモとして残します。
今回は、簡潔に非同期処理を記述する為にasyncとawaitを利用していますが説明等はしておりません。ご了承くださいませ?♂️GETリクエスト
主にAPIを叩いて、データを取得する時に利用します。
const response = await axios.get(url, { params: { id: 1, name: 'hoge' } })上記のように記述すると、id=1とname='hoge'をクエリパラメータとして渡すことができます。
urlの箇所は、APIにアクセスしたいエンドポイントを記述してくださいまた、直接URLに記述する方法もあります。
const response = await axios.get('url?id=1&name="hoge"')複数のパラメータを指定する場合は、&記号(アンパサンド)でつないで記述します。
POSTリクエスト
主にAPIを叩いて、データを保存する時に利用します。
const response = await axios.post(url, { id: 1, name: 'hoge' })第二引数に保存したいデータをオブジェクトで指定します。
はい、以上になります。
おわりに
毎回、自分用のメモとして記述してしまってすみません...もし今回の記事で誤字や脱字等ありましたらご指摘頂けると助かります。閲覧して頂き、ありがとうございました。
- 投稿日:2020-07-29T20:45:17+09:00
JavaScript 未分類の覚書き
未分類の覚書きです。
Map
や配列関係のものは別に投稿しています。オブジェクトリテラルで配列の長さを指定して作成する
Array.from
は1番目の引数にオブジェクトリテラル{length: 要素数}
を指定できます。2番目の引数(mapFn
)と組み合わせることで一時的な配列を作成せずに指定長の配列を作成できます。Array.from({length: 100}) // (100) [undefined, undefined, ..., undefined] Array.from({length: 100}, (_, i) => i) // (100) [0, 1, ..., 98, 99] Array.from({length: 100}, (_, i) => 2 + i * 2) // (100) [2, 4, ..., 198, 200]動作確認環境:Google Chrome 84.0.4147.105(Windows、64 ビット)
関数が
new
演算子で呼び出されたか判断する
function
内部でnew.target
を呼び出すとnew演算子で呼び出されたか判断できます。function f1() { console.log(`this: ${this}, new.target: ${new.target}`); } // new演算子なしで呼び出すとthis == グローバルオブジェクト、new.target == undefined f1(); // this: [object Window], new.target: undefined // new演算子ありで呼び出すとthis == 新しいインスタンス、new.target == コンストラクタや関数への参照 new f1(); // this: [object Object], new.target: function f1() // { // console.log(`this: ${this}, new.target: ${new.target}`); // }動作確認環境:Google Chrome 84.0.4147.105(Windows、64 ビット)
関数は
new
演算子の有無でthis
の値と省略時の戻り値が異なるfunction f1() { this.a = 1; this.b = 2; } // 新しいインスタンスを作成してそのフィールドa、bに値を代入する。 t = new f1(); // thisに代入したのでグローバル変数a、bは存在しません。 console.log(`global: a=${a}, b=${b}`); // Uncaught ReferenceError: a is not defined // newで呼び出した関数はreturnの省略時にthisを返します。 console.log(`f1: a=${t.a}, b=${t.b}`); // f1: a=1, b=2 delete a; delete b; // 関数f1を呼び出してグローバル変数a、bに値を代入する。 t = f1(); // newが指定されていないのでthisはグローバル変数を指します。 // したがってグローバル変数a、bが作成されます。 console.log(`global: a=${a}, b=${b}`); // global: a=1, b=2 // newなしで呼び出した関数はreturnの省略時にundefinedを返します。 console.log(`f1: a=${t.a}, b=${t.b}`); // Uncaught TypeError: t is undefined
Array
関数はArray
コンストラクタ(new Array
)として振る舞う
Array
関数はnew Array
と同様の機能を提供します。これはArray
関数の特別な振る舞いであり、通常はnew
演算子の有無で動作が変わることに注意してください。
- 投稿日:2020-07-29T20:11:13+09:00
Javascriptのreplaceで、置換後の文字列を動的に変えたい
(ぜんぜんわからないので、有識者に、教えてもらおう、という気持ちで記事書いてます)
Javascriptのreplaceで、置換後の文字列を動的に変えたい
https://github.com/santarou6/tikan/blob/master/tikan_v1.html
やりたいことは、html+javascriptで、正規表現を使った、「置換」ができるような、一種のテキストエディタを作りたいということです。
この画面でいうと、「改行¥n」を、「タブ¥t」に変換する、というだけのことをやってます。
1つ目のテキストボックスに、置換「対象」の文字列として、正規表現を許容する文字列を入力し、
2つ目のテキストボックスに、置換「後」の文字列として、正規表現を許容する文字列を入力し、「置換」ボタンを押すと、置換されると。
r_f = document.getElementById('rp_frm').value; r_t = document.getElementById('rp_to').value; tx = tx.replace(new RegExp(r_f,"g"), r_t );で、置換対象の文字列を動的に変えるのは、
replace(A,B)で、AにRegExp(hogehoge,"g")と入れればよいのはわかりました。が、Bの方に、正規表現を含む、文字列を動的に入れる方法が、わかりません。。
「あいう」を「あ
う」や、
「あいう」を「あ→う」にしようとして、
.変換前「い」 → 変換後「\n」
.変換前「い」 → 変換後「\t」
とすると、そのまま、「あ¥nう」「あ¥tう」という文字に置き換わってしまいます当然ソースに
.replace(“い”,”¥n”)
とすれば改行されます。変数2=‘¥n‘
.replace(変数1,変数2)
も改行されますが、、
変数2=画面上で入力した変換後文字列(例:¥n)
.replace(変数1,変数2)としても、改行されず、「¥n」になってしまうと、、
↑ここが、わからないところです。。
ちなみに、
r_t = r_t.replace("\\" + "n","\u000d\u000a"); r_t = r_t.replace("\\" + "t","\u0009");とあるのは、かなり無理矢理(苦肉の策)で、
改行文字「¥n」と、タブ文字「¥t」だけ、それと認識できるように、変換してます。この「replace(A,B)」のBの部分、どうすればよいか
もしおわかりの方、いらっしゃったら、ご教示いただければありがたいです。※このソースはこのソースで、タブと改行に限って言えば、使えるものにはなっています。
tikan.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <title>置換</title> <style type="text/css"> body { background-color: #eeeeee; color: #000000; margin: 2; padding: 2; font-family: "MS ゴシック",sans-serif; } textarea { width: 800px; height: 150px; border: 2px solid #ffcc77; font-family: "MS ゴシック",sans-serif; } </style> <script type="text/javascript"> function tikan(){ tx = document.getElementById('ss1').value; var r_f; var r_t; r_f = document.getElementById('rp_frm').value; r_t = document.getElementById('rp_to').value; r_t = r_t.replace("\\" + "n","\u000d\u000a"); r_t = r_t.replace("\\" + "t","\u0009"); tx = tx.replace(new RegExp(r_f,"g"), r_t ); document.getElementById('ss2').value = tx; } function tereko(){ var p1; var p2; p1 = document.getElementById('rp_frm').value; p2 = document.getElementById('rp_to').value; //alert(p1 +" " + p2); document.getElementById('rp_frm').value = p2; document.getElementById('rp_to').value = p1; } </script> </head> <body > <form name="test"> <input type="text" id="rp_frm" value="\n"> <input type="button" value="⇔" onClick="tereko()"> <br/> <input type="text" id="rp_to" value="\t"><br/> <br/><br/><input type="button" value="置換" onClick="tikan()"> </form> <br/> <textarea id="ss1" > あいうえお かきくけこ さしすせそ </textarea><br/> <textarea id="ss2"> </textarea><br/> </body> </html>
- 投稿日:2020-07-29T19:17:05+09:00
Vue.js+Flaskで画像のアップロード機能
概要
今回はフロントエンドにVue.js、バックエンドにFlaskを用いた画像認識アプリを作ります。
ひとまず今回は画像アップロード機能までの実装です。環境
- Docker
- Vue-cli
- flask (pipenv)
上記の環境で環境構築しました。
手順や詳細は以下のリンクを参照してください。要所の説明
Vue
詳細を説明するのは、以下のコード。
Home.vue// 画像をサーバーへアップロード onUploadImage () { var params = new FormData() params.append('image', this.uploadedImage) // Axiosを用いてFormData化したデータをFlaskへPostしています。 axios.post(`${API_URL}/classification`, params).then(function (response) { console.log(response) })
- 取得した画像はBase64化がなされている。「data:image/jpeg:base64,〜」
- FormDataにより、データをHTTPリクエストで「キー:値」の形式へ。
- Axiosを適用し、'127.0.0.1:5000/classification'+ POSTメソッドでデータを送信。
Flask
詳細を説明するのは、以下のコード。
app.py@app.route('/classification', methods=['POST']) def uploadImage(): if request.method == 'POST': base64_png = request.form['image'] code = base64.b64decode(base64_png.split(',')[1]) image_decoded = Image.open(BytesIO(code)) image_decoded.save(Path(app.config['UPLOAD_FOLDER']) / 'image.png') return make_response(jsonify({'result': 'success'})) else: return make_response(jsonify({'result': 'invalid method'}), 400)
- FormDataの内部に「data:image/jpeg:base64,〜」が存在。ファイル名を取得。
- Pillow(PIL)で画像を取得。
- 画像の保存。
全体像
Vue
Home.vue<template> <div> <div class="imgContent"> <div class="imagePreview"> <img :src="uploadedImage" style="width:100%;" /> </div> <input type="file" class="file_input" name="photo" @change="onFileChange" accept="image/*" /> <button @click='onUploadImage'>画像判定してくだちい・・・</button> </div> </div> </template> <script> import axios from 'axios' const API_URL = 'http://127.0.0.1:5000' export default { data () { return { uploadedImage: '' } }, methods: { // 選択した画像を反映 onFileChange (e) { let files = e.target.files || e.dataTransfer.files this.createImage(files[0]) }, // アップロードした画像を表示 createImage (file) { let reader = new FileReader() reader.onload = (e) => { this.uploadedImage = e.target.result } reader.readAsDataURL(file) }, // 画像をサーバーへアップロード onUploadImage () { var params = new FormData() params.append('image', this.uploadedImage) // Axiosを用いてFormData化したデータをFlaskへPostしています。 axios.post(`${API_URL}/classification`, params).then(function (response) { console.log(response) }) } } } </script>Flask
留意点
- CORSにより、異なるオリジン(プロトコルやドメイン、ポート)でもリソースを共有できる。
- CORSは異なるWebアプリケーションを持つ場合、必須。
- app.config['JSON_AS_ASCII'] = False により日本語対応可能。
app.py# render_template:参照するテンプレートを指定 # jsonify:json出力 from flask import Flask, render_template, jsonify, request, make_response # CORS:Ajax通信するためのライブラリ from flask_restful import Api, Resource from flask_cors import CORS from random import * from PIL import Image from pathlib import Path from io import BytesIO import base64 # static_folder:vueでビルドした静的ファイルのパスを指定 # template_folder:vueでビルドしたindex.htmlのパスを指定 app = Flask(__name__, static_folder = "./../frontend/dist/static", template_folder="./../frontend/dist") #日本語 app.config['JSON_AS_ASCII'] = False #CORS=Ajaxで安全に通信するための規約 api = Api(app) CORS(app) UPLOAD_FOLDER = './uploads' app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # 任意のリクエストを受け取った時、index.htmlを参照 @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def index(path): return render_template("index.html") @app.route('/classification', methods=['POST']) def uploadImage(): if request.method == 'POST': base64_png = request.form['image'] code = base64.b64decode(base64_png.split(',')[1]) image_decoded = Image.open(BytesIO(code)) image_decoded.save(Path(app.config['UPLOAD_FOLDER']) / 'image.png') return make_response(jsonify({'result': 'success'})) else: return make_response(jsonify({'result': 'invalid method'}), 400) # app.run(host, port):hostとportを指定してflaskサーバを起動 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)様子
こんな感じ
非常に参考になりました
https://developer.mozilla.org/ja/docs/Web/HTTP/CORS
画像をPOST、顔検出、canvasで顔にお絵かき
- 投稿日:2020-07-29T17:38:24+09:00
【初心者でもできる】仕事が忙しくても、赤ちゃんの熱中症の危機にすぐに気づけるIoTをつくった
はじめに
少し前に、2才のこどもを保育園に送り忘れ、
かつ、車の中に放置してしまい、亡くなってしまう
痛ましい事件が起きました。本当に悲しい出来事であり、あってはならないことですが、
人ごとではないような気がしました。何かに集中してしまって、
つい何かやるべきことが抜け落ちてしまうことはある人も多いのではないでしょうか。今回は、それと類似のシーンとして、
「仕事が忙しく、赤ちゃんの部屋が高温になっていることに気づけなかった」ということがないように、高温になればアラートが出て、気づける仕組みをIoTを使って、できないかとチャレンジしました。作りたいイメージ
温度センサーによって、常に部屋の温度を監視し、
赤ちゃんにとって快適な温度(26-28度らしい)から外れた時に、
自動でLINEに通知がくるもの。さらに、LINEの通知に気づかない可能性があるので、
音やLEDの光でアラートを出す。その2つの仕組みがあれば、仕事に集中していても気づくことができ、
すぐにエアコンをつけにいくことができるはずです。実際につくった仕組み
自動でLINEに通知する仕組みは作れなかったので、
外出中の夫or妻がLINE botで温度を確認し、
快適温度から外れた時に、
自宅にいる夫or妻にLEDで通知をする仕組みを作りました。<STEP1>外出先からLINE botで赤ちゃん部屋の温度を確認する
まずは、LINE botに「どう?」と問いかけます。
<STEP2>部屋の温度に応じて、LEDの反応を変える
①もし28度を超えていた場合は?
■LINEの通知
■LEDの反応
別室で仕事に集中している方でも、赤ちゃん部屋の温度管理がうまくいくIoTをつくってみました。
— 柳瀬浩之@人材開発×UX専門家 (@btap_hiro) July 29, 2020
室内温度が28度以上になると、アンパンマンが光って、教えてくれます。#IoT #ProtoOut #obniz pic.twitter.com/X5ew1BtEOa②もし26度より下回っていた場合は?
■LINEの通知
※検証した日はなかなか26度を下回らなかったため、
コードの方の数字をいじって、強引に表示させてしまいました。■LEDの反応
今度は、室内温度が26度以下になると、アンパンマンがゆっくり光って、教えてくれます。#IoT #ProtoOut #obniz pic.twitter.com/98FrHSWzx5
— 柳瀬浩之@人材開発×UX専門家 (@btap_hiro) July 29, 2020③もし26度〜28度の間(快適温度)だった場合は?
■LINEの通知
■LEDの反応
LEDが消えます。
<STEP3>エアコンをつけるor消す。
LEDで通知を受けたら、家にいる方が
エアコンのスイッチを押せば、
快適な温度が保たれます。これで一安心です。
・・・・と思ったのですが、
ここまで考えてみて、1つのobnizを使うと、
温度センサーとLEDの位置は、同じ場所でしかできないので、
赤ちゃん部屋の温度を、別部屋で仕事をする人に
LEDで通知はできなかったです。。なので、これでできることは、
・外出先からでも赤ちゃん部屋の温度がわかる。
・外出先から家にいる夫or妻にLINEで普通に通知する。
ですね。また発展版を考えてみようと思います。
コード
'use strict'; // obniz呼び出し const Obniz = require('obniz'); const obniz = new Obniz('Obniz_ID'); // Obniz_ID を入力 // LINE botを使えるようにする const express = require('express'); const line = require('@line/bot-sdk'); const axios = require('axios'); const PORT = process.env.PORT || 3000; const config = { channelAccessToken: 'channelAccessToken',// LINEの方から入力 channelSecret: 'channelSecret' }; const app = express(); app.post('/webhook', line.middleware(config), (req, res) => { console.log(req.body.events); Promise .all(req.body.events.map(handleEvent)) .then((result) => res.json(result)); }); const client = new line.Client(config); // obniz接続 obniz.onconnect = async function () { obniz.display.clear(); obniz.display.print('obniz meets LINE Bot!'); } function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } let mes = '' if(event.message.text === 'どう?'){ mes = 'ちょっと調べてきますねー'; //少し時間がかかるのでメッセージを入れる getAskObnizTemp(event.source.userId); //プッシュメッセージの準備 }else{ mes = event.message.text; } return client.replyMessage(event.replyToken, { type: 'text', text: mes }); } const getAskObnizTemp = async (userId) => { // obniz温度センサー実行準備、差し込んだ番号を入力 const tempsens = obniz.wired('LM60', { gnd:0 , output:1, vcc:2}); const led = obniz.wired('LED', {anode:4, cathode:5}); // 部屋の温度を取得 const temp = await tempsens.getWait(); // 温度をコンソールに表示 if (temp > 28) { console.log(temp); // obnizのdisplayに表示 obniz.display.clear(); obniz.display.print(temp + 'C'); // obnizにpushメッセージを送る await client.pushMessage(userId, { type: 'text', text: temp + '度。エアコンつけよう!', }); // LEDの点滅スピードを決める(ちょい早め) setInterval(async function(){ led.on(); },2000); // 1000ミリ秒 = 1秒 setInterval(async function(){ // 同期で取得 led.off(); },1000); // 1000ミリ秒 = 1秒 }else if(temp < 26){ console.log(temp); obniz.display.clear(); obniz.display.print(temp + 'C'); await client.pushMessage(userId, { type: 'text', text: temp + '度。エアコン消そう!', }); // LEDの点滅スピードを決める(ちょい遅め) setInterval(async function(){ // 同期で取得 led.on(); },3000); // 1000ミリ秒 = 1秒 setInterval(async function(){ // 同期で取得 led.off(); },2000); // 1000ミリ秒 = 1秒 }else{ console.log(temp); // displayに反映 obniz.display.clear(); obniz.display.print(temp + 'C'); // 英語が出力できる await client.pushMessage(userId, { type: 'text', text: temp + '度。快適です!', }); // LEDのオフにする led.off(); } } app.listen(PORT); console.log(`Server running at ${PORT}`);まとめ
はじめてIoTつくってみました。
まだまだ狙ったものをつくるスキルはないですが、
少しずつ改良して、より良いものにしていきたい。今後も自分にとって、誰かにとって必要なものを
色々作ってみたいと思います。
- 投稿日:2020-07-29T17:37:45+09:00
The complete Web Developer in 2020(英語メモ)
The Complete Web Developer in 2020
Career Of a Web Developer
The Takeaways:
- React is the most popular framework you need to learn.
- jQuery is not a popular framework anymore but it is still used here or there.
- React can also build Android app and iOS app.
- You can be a freelancer to use Upwork or some other websites to get hired to be a developer.Developer Fundamentals
the Takeaways:
- use the rubber duck method to code
- by this way you can figure out the problem. Talking to yourself is good.
- stackoverflow is a website which can be very good to you when you are on you way to code more.
- googling is the best way to solve the problems. this is quite important.DOM selector
- getElementsByClassName
- getElementById
- getElementsByTagName
- the 3 selector above is some old way
querySlector("")
you can put in anything you want like you selected in the CSS file.
- It select the first item it finds.
querySelectorAll("")
this one can select all of the items about it.- these later ones (querySelector, querySelectorAll) are recommended to be used. cuz they are like CSS selectors.
document.querySelector("li").getAttribute("random");
: get the "random" attribute of the li element in the document.document.querySelector("li").setAttribute("random", "10000");
by this way, you can set the attribute of the "random" to "10000".Changing Style
using the style
javascript
document.querySelector("h1").style.background = "yellow";
// by this you change the CSS. But it is not a good way to change the style. cuz usually we use the css file and html file to control the style and structure respectively. we don't want to mix them together. so that's why we use it the way below.
var h1 = document.querySelector("h1");
h1.classname = "coolTitle;"
// this will change the class of the h1 to "coolTitle"
document.querySelector("li").classList.toggle("done");
// if you toggle once it will be changed once. if you want to change something on/off it is quite useful.
use the methods below to change the style of webpage.
className
-> best
classList
-> best
classList.add("cooltitle")
classList.remove("cooltitle")
classList.toggle('done')
/* line-through can 横線で削除された感じ*/ h1 { text-decoration: line-through; }change the text
innerHTML
document.querySelector("h1").innerHTML = "<strong>!!!!</strong>"; // this can change the text content of the h1 tagparent element
var baba = document.querySelectorAll("li")[1].parentElement; // ul var yeye = document.baba.parentElement; // body // this "parentElement" can get the parent element of the element. var childrenA = yeye.children; // get all the element below the element -> childrenDOM Events
var button = document.getElementsByTagName("button"); button[0].addEventListener("click", function(){ console.log("CLICKED!"); })<input id="userinput" type="text" placeholder="enter items"> <button id="enter">enter</button>var button = document.getElementById("enter"); var input = document.getElementById("userinput"); var ul = document.querySelector("ul"); button.addEventListener("click", function(){ if (input.value.length > 0){ var li = document.createElement("li"); li.appendChild(document.createTextNode(input.value)); ul.appendChild(li); // this one reset the input value input.value = ""; } })Callback function
In javascript function is also an object. Function can also become parameter of other functions, or become the return value of other functions.
A callback function is to
put a function into a funciont's parameter position and don't let it run until the main event is called.
e.g.button.addEventListener("click", addListAfterClick); input.addEventListener("keypress", addListAfterClick);We did not put a function which runs immediately. We want to let it know though that we want this action to happen wehn a click happens. So we are passing a reference to the function without running it.
- 投稿日:2020-07-29T16:59:33+09:00
JSを使ったiframe内のページ内リンク
結論から言うと、JavaScriptの
window.location.href
でアンカーリンクが簡単に実装できるよという話です。はじめに
iframe外からiframe内へのアンカーリンクで困る、というのはよく目にします。
今回はiframe内で完結しているコンテンツにおいて、アンカーリンクが正常に作動しなかったときのメモです。
滅多にないと思いますが、ちょっと特殊なケース(今回ならGAS)で同じような問題に遭遇した際の代替案として今後役立てばと思います。
また、JSで手軽にページ内リンクが設定できる個人的な発見でもあったので記録します。Google Siteで困りました
始まりはGoogle Siteでページを作成する中で、GASのApps Scriptガジェットを埋め込んだときのことです。
埋め込んだガジェット部分はiframeとして処理され、作成したコードは全てiframe内に組み込まれるわけですが
例えば、ガジェット内で下記のようにページ内リンクをさせたかったとします。<a href="#hoge">リンク</a> <div id="hoge"> 「リンク」クリックでここにジャンプさせたい </div>私の想定ではいつも通り
id="hoge"
までピュンと飛んでくれるはずだったのですが
なぜだか別ページに移動してしまいます。
たぶんなのですが、GASでリンクを貼る場合は絶対パスでないと自動でURLを補完するようで、全く別ページのURLの末尾に#hoge
が追記されたものに置き換わってしまうのです。
これを機能させるにはiframe内のURLを知る必要があるということ…?
iframe内のURLって…?試したこと
わざわざ段落を設けるまでのことはしていないのですが、調べると
id="hoge"
をname="hoge"
に変えたらいいという解決策が複数出てきたので試しました。だめでした。対処法 onclick="window.location.href='ID'"
主にHTMLとCSSばかり触ってきた人間なので、
<a href="#ID"></a>
のみを使ってきましたがJSでも簡単に実装できるということを今回学びました。方法は簡単で、クリックさせたいタグに
onclick="window.location.href='飛ばしたい先のID'"
を足すだけです。
先ほどの例になぞればこんな感じ。<a onclick="window.location.href='#hoge'">リンク</a> <div id="hoge"> 「リンク」クリックでここにジャンプさせたい </div>aタグ以外にも使える上、手軽で、さらにIDを持つ要素の位置情報を確実に取得し移動してくれるので万能そうです。
- 投稿日:2020-07-29T16:57:17+09:00
ReactでsetStateをしても画面表示が切り替わらない問題の解決方法メモ
はじめに
概要
Reactでフロントエンドのツールを作っている時に、
state
の中に設定されているjson形式のデータを、HTMLのテーブルとして表示する機能を作りました。その際に、表示の基となるデータを変更しても、テーブル内に表示される値は切り替わらないというエラーに出会ったため、簡単な解決方法をここにメモしておきます。
TL;DR
Reactを使うときに、イテラブルなオブジェクトを基に画面表示をする場合は、各要素ごとにidが振られていないと、同オブジェクトの値が変わっても画面表示が切り変わらないことがあるようです。
やりたかったこと
state
に登録されているjsonの配列形式のデータを、HTMLの<table>
要素の中に上手く表示させることがしたかったです。
具体的には、state
には以下のようなデータが登録をされていました。this.state.dummyData = [ { "key":"aa", "value":"bb" },{ "key":"aa", "value":"bb" },{ "key":"aa", "value":"bb" } ]こんな感じで、単純なオブジェクトの配列になっていました。
これを基に画面表示をする、概略化したコードは、以下のような感じです。render(){ // 省略 <table> <tbody> {this.state.dummyData.map((d,i) => <tr> <td>{d.key}</td> <td>{d.value}</td> </tr> )} </tbody> </table> // 省略 }配列の中身の数だけ
<tr>
を生成して、その中の<td>
に値を表示しているだけです。
しかしこれだと、this.state.dummyData
の値が変わっても、テーブルの中身は変わらない場合がありました。解決方法
解決方法のヒントは、Reactの開発モードの時に出るコンソール内のwarningにちゃんと出ていました。
Warning: Each child in a list should have a unique "key" prop.Reactで、リストを基に画面に要素を複数個表示する場合は、各要素に
key
を設定することが推奨されています。そのため、以下のように変更をしました。まず、表示基の
state
内のオブジェクトの各項目には、idを振るようにしました。this.state.dummyData = [ { "id":001, "key":"aa", "value":"bb" },{ "id":002, "key":"aa", "value":"bb" },{ "id":003, "key":"aa", "value":"bb" } ]そして、画面表示をする際は、
<tr>
にkey
としてidを設定します。render(){ // 省略 <table> <tbody> {this.state.dummyData.map((d,i) => <tr key={d.id}> <td>{d.key}</td> <td>{d.value}</td> </tr> )} </tbody> </table> // 省略 }こうすることで、表示基のオブジェクトが変更されたときに毎回render関数が走るようになり、きちんと画面が切り替わりました。
まとめ
Reactを使うのは初めてだったので、単純なところで引っかかってしまいました。
「React 配列 展開」とかでググって出てくるサンプルコードの多くは、
map
関数の中でHTML要素にkey
を設定していなかったのですが、表示内容が変更される可能性がある場合には、きちんとkey
を設定しなければならないようです。
- 投稿日:2020-07-29T16:04:55+09:00
【備忘録:javascript】HTMLcollectionでのforEach
document.getElementsByClassNameで取得したクラスが回せなかった
hoge.jslet elem = document.getElementsByClassName('m-hoge');上記のように特定のクラスを取得しそのテキストを手に入れたかったのですが、
hoge.jslet elem = document.getElementsByClassName('m-hoge'); elem.forEach(function(item) { console.log(item.innerHTML); });このようにforEachをしても取れませんでした。
HTMLcollectionは配列ではない
参考にさせていただいたページが分かりやすかったのですが、HTMLcollectionはそもそも配列ではないとのことです。
取得しているときは配列っぽいですが、配列でないため、配列で使用するメソッド(今回でいうとforEach)が使用できないのです。対策
対策として、Array.prototypeを使用して対応することができました。
hoge.jslet elem = document.getElementsByClassName('m-hoge'); Array.prototype.forEach.call(elem, function(item) { console.log(item.innerHTML); }まとめ
分からないことがたくさんあるので、しっかり理解して進めて行きたいと思います。
参考ページ
- 投稿日:2020-07-29T15:41:00+09:00
【JavaScript】ライブラリ化しておく便利な3つの自作共通関数を記録
前提
- ※記載の共通関数は、各環境によって動作しない場合がある。
→ES2015からES2019で利用できるメソッドも利用しているため1. chunk
- 指定の数に応じて、対象配列を分割する関数
コード
lib.jsconst chunk = (arr = [], size = 3) => { return Array.from({ length: Math.ceil(arr.length / size) }, (v, k) => arr.slice(k * size, k * size + size)) }使い方
index.jsimport { chunk } from './lib.js' // 対象配列定義 const target = [1,2,3,4,5,6]; // 利用 : chunk(対象配列, 分割サイズ) console.log(chunk(target,2)); console.log(chunk(target,4)); /* 出力結果: [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ] [ [ 1, 2, 3, 4 ], [ 5, 6 ] ] */内容
- 処理内容としては下記。
- 1. 引数から受け取る値から、対象配列の分割数を求める。
- 2. 求めた分割数からlengthプロパティを持つ配列風オブジェクトを作成
- 3. 作成した配列風オブジェクトの各値に対して、from処理で反復slice処理と変換を行う。
1. diff
- 指定の各配列の差分を求める関数
- つまり、重複していない一意の値を求める。
コード
lib.jsconst diff = (...manyArgs) => { const targetArr = manyArgs.flat(); const map = new Map(); targetArr.forEach(a => map.set(a, (map.get(a) || 0) + 1)); return targetArr.filter(a => map.get(a) === 1); }使い方
index.jsimport { diff } from './lib.js' // 対象配列定義 const first = [1,2,3,4,5]; const second = [1,2,3]; const third = [5,6,7]; // 利用 : diff(対象配列1, 対象配列2, ...) console.log(diff(first,second)); console.log(diff(first,second,third)); /* 出力結果: [4,5] [4,6,7] */内容
- 処理内容としては下記。
- 1. 可変引数で受け取った二次元配列をflat処理で一次元に変換
- 2. map定義して、反復処理で各値に存在確認処理を行った値を格納していく。
- 3. 格納したmapの値によって、一次元化した配列に対してfilter処理を行う。
1. filterObj
- 指定の条件で対象のオブジェクトを選別(フィルタリング)する関数
コード
lib.jsconst filterObj = (target,condition) => { return Object.fromEntries(Object.entries(target).filter(condition)) }使い方
index.jsimport { filterObj } from './lib.js' // 対象オブジェクト定義 const obj = { id: 11, name: '', score: 89, hasSkill: false } // 利用 : filterObj(対象オブジェクト, 条件) console.log(filterObj(obj, ([key,value]) => value === 11)); console.log(filterObj(obj, ([key,value]) => !!value)); // 値が正のものを抽出 /* 出力結果: { id: 11 } { name: 't-o-d', score: 80 } */内容
- 記載している処理内容としては下記。
- 1. 第一引数で受け取ったオブジェクトをentries処理で配列へ変換。
- 2. 変換した配列に対して、第二引数で受け取った条件式でfilter処理を行う。
- 3. fromEntries処理で、再度オブジェクトへの変換処理を行う。
参考
- 投稿日:2020-07-29T14:52:56+09:00
【npm】パッケージの脆弱性対処(備忘録)
nuxtでアプリをビルドしたときにパッケージの脆弱性に関する警告文が出た
npmの依存関係を修復したい
「警告文を1㍈も理解せずnpm install で胡麻化してきた」人にとって、一歩踏み出す補助輪になればとも思いメモを残します
根本的な理解については別途お調べください目次
package.json と package-lock.json
node_modules(パッケージの大群)はgitへpushするものではない
なので、再現するためにパッケージを記しておくファイル(package.json)が必要
package.json で指定している内容を元に npm install では各種パッケージがインストールされる
が、実際にインストールされるものは package.json への書き方やタイミングでインストールされるものが変わる
そのため、実際にインストールされたものが package-lock.json へ記録される(package-lock.jsonが存在しない場合は生成される)
これにより、他の開発環境で npm install する際は package-lock.json が参照され
固定されたバージョンで各パッケージがインストールがされるため、node_modulesが再現できる脆弱性の確認
脆弱性のチェックnpm audit
audit は監査という意味
パッケージの依存関係に脆弱性がある箇所を教えてくれるコマンド
※ただし、npm v6以上脆弱性の自動修正
脆弱な箇所の自動修正npm audit fix npm audit fix --force
ある程度は自動修正してくれる
high,criticalを潰しておけばとりあえずはOK
自動修正してくれない部分はログを見て手動で直した脆弱性の手動修正
パッケージの在り処を参照npm ls "パッケージ名"パッケージ名を指定して、どこにそのパッケージがあるか探すことができる
不必要なパッケージを削除して、インストールしなおすpackageのインストールnpm install "パッケージ名" npm install "パッケージ名@バージョン" ※バージョン指定する場合参考記事
Qiita記事
脆弱性の警告を受けたnpmパッケージの依存関係を力技で直す
あなたがnpm installをしてはいけない時
npmパッケージのvulnerability対応フロー
【Node.js】package.jsonとpackage-lock.jsonについて簡単にまとめる
package-lock.jsonについて知りたくても聞けなかったこと外部記事
"npm install"と "npm ci"の違いは何ですか?
package.jsonとpackage-lock.jsonの運用方法について以上
間違った理解や記述があればご指摘いただけると幸いです
- 投稿日:2020-07-29T14:43:06+09:00
JavaScript(jQuery)で動的に追加した要素をフェードアウトしてから削除する方法
jQueryで動的に追加した要素をフェードアウトしてから削除する方法
JavaScriptで動的に追加したDOM要素は通常の方法ではイベントが起こらなくなる。
そこでdocumentを使ってHTML読み込み完了後にイベントを発動するよう設定することでイベントが起こる。
また、queue()を使用することで、フェードアウトを実行してから要素を削除できる。
個人的にかなり躓いた部分なので記録に残します。$(document).on('click', 'div', function(){ $('div').fadeOut(500).queue(function(){ $(this).remove(); }); });
- 投稿日:2020-07-29T14:13:35+09:00
Reactにおける配列処理(for文・mapメソッド)
はじめに
現在Reactを用いて画面開発を行っているのですが、selectタグの中でoptionを使う際、以下のようにコンポーネントに直接記述してしまうと使い回しができなくなってしまうためpropsで値を渡してやりたいと考えました。
sample.js<select title="サンプル"> <option disabled selected>選択してください</option> <option value="0">サンプル1</option> <option value="1">サンプル2</option> <option value="2">サンプル3</option> <option value="3">サンプル4</option> </select>しかし、optionタグ1つ1つをpropsとして値渡しすると数が多くなってしまうため、propsで配列を受け取りその配列の要素をoptionタグ内に記述して表示してあげよう!と考えました。
①for文での記述
sample.jslist = [] for(let i=0;i<props.opt.length;i++){ list.push(<option value={i}>{props.opt[i]}</option> } return( <select title="サンプル"> {list} </select> )for文を用いて各配列の要素に変更を加え、それらを別の配列に格納することで実装いたしました。
ちなみに、
opt=["test","sample",...]
といった配列データが親コンポーネントから渡されています。②mapでの記述
for文でも実装できましたが、JavaScriptのES6以降ではmapメソッドを用いることでスマートに配列処理を行うことができます。
sample.jsvar list = props.opt; //見やすくするため、受け取った値を変数に格納 const option = list.map((data,index)=><option value={index}>{data}</option>) return( <select title="サンプル"> {option} </select> )こちらのほうが自分は見やすくおしゃれだなと思いました。
おまけ:mapメソッドのコールバック関数について
mapメソッドのコールバック関数の引数は3つあります。
sample.js配列.map((value,index,array)=> )value:配列の値
index:配列のインデックス番号
array:現在処理している配列これらを覚えておくと、配列処理をさらにスマートに行えます!
最後に
ありふれた内容ですが、開発を行う際に引っかかったため自分用の備忘録として書かせていただきました。始めたばかりの初心者ですのでご指摘があればコメントお願いします。
- 投稿日:2020-07-29T13:04:33+09:00
Lambda(node.js)とJavaScriptのタイムゾーン
Lambda(node.js)で日付の計算をしたら思ったより面倒だったのでまとめる。
やりたかったこと
日本標準時(JST)で先週/先月の範囲を、世界標準時(UTC)で取得したい。
例えば、今日が 2020-07-29 とすると先週の範囲は日曜日始まりでこんな感じ。
タイムゾーン 開始 終了 JST 2020-07-19T00:00:00+0900 2020-07-26T00:00:00+0900 UTC 2020-07-18T15:00:00Z 2020-07-25T15:00:00Z やりたくなかったこと
大した処理じゃないので余計なライブラリは使いたくない。
結論
- タイムゾーンの設定はそのままにプログラム内で対応する
- タイムゾーンオフセット値からJSTを自力で設定する
- UTCへの変換も自力でやる
それぞれのタイムゾーン
Lambdaのタイムゾーン
UTCに固定されている模様。
ググるとTZ環境変数を設定すればいいという記事が大量にヒットする。
ただ、TZ環境変数はAWSに予約されているので、それを変更するのはよろしくなさそう。
Lambda のタイムゾーンを環境変数TZで指定してはいけないっていう話 | Serverworks ENGINEER BLOG他の利用しているAWSサービスでUTCで動作するものもあったので、プログラム内で対応することにした。
JavaScriptのタイムゾーン
JavaScriptのDateクラスにはタイムゾーンを設定する方法はないようだった。
ただgetTimezoneOffset
で分単位の時差は取得できた。JSTは+9時間なので、なぜか-540分が返ってくる。取得した値を+しとけばUTCに変更できそう。
toLocaleString
でタイムゾーン指定の日付文字列を取得して、新しくDateオブジェクトを作る方法もあるようだが、Lambdaでは動作しないのとDateクラスのコンストラクタに日付文字列を渡すのは非推奨だったのでやめた。最終的にJavaScriptのタイムゾーンは無視して
getTimezoneOffset
を使って自力で日時を計算することにした。オレのMacのタイムゾーン
JSTだった。
AWS SAMを使ってローカルで開発していると、ローカルではJSTでLambda上ではUTCになる。
タイムゾーンを変更してテストしたい場合は、TZ環境変数を設定することで変更できる。実装
日本標準時で現在日時を取得
考慮するタイムゾーンはJSTかUTCの2つという前提で、タイムゾーン設定を無視して日本標準時での現在日時を取得することにした。
const jstOffset = 9 * 60; const now = new Date(); const offset = now.getTimezoneOffset() + jstOffset; now.setTime(date.getTime() + offset * 60 * 1000);先週の開始/終了日時を取得
日本標準時で先週の日曜日を取得。
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate()); start.setDate(start.getDate() - 7 - start.getDay());同様に今週の日曜日を取得。
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate()); end.setDate(end.getDate() - end.getDay());先月の開始/終了日時を取得
先月1日を取得。
const start = new Date(now.getFullYear(), now.getMonth() - 1, 1);今月1日を取得。
const end = new Date(now.getFullYear(), now.getMonth(), 1);JST→UTCの変換
それぞれの日付をUTCに変換。
実際はタイムゾーンを変換してないのでgetUTCFULLYear
などは使えない。const jstOffset = 9 * 60; date.setTime(date.getTime() - jstOffset * 60 * 1000); date.getFullYear(); date.getMonth() + 1; date.getDate(); date.getHours();
- 投稿日:2020-07-29T12:59:51+09:00
Svelte入門 環境構築・フォルダ構成について
Svelte入門 環境構築・フォルダ構成に関する忘備録
Vue.jsの50倍早いとかでフロントエンド界隈で注目を浴びている?svelte.jsを少し触ってみたので忘備録としてまとめました。
環境構築手順
Vue.jsのようにCDNを張り付けるだけで試せるような手軽さはありません。
まず、Node.jsがインストールされている事が前提条件としてあり、npxコマンドでプロジェクトを作成する必要があります。プロジェクトの生成
npxコマンドを実行して新規プロジェクトを作成します。
npx degit sveltejs/template my-projectnpxコマンドはnode.jsをインストールしたら一緒についてくるらしいのですが、自分の環境では認識されませんでした。その場合は別途npxをインストールします。
$ npm install -g npxプロジェクトフォルダに移動して
npm install
を実行します。cd my-project/ npm installpackage.jsonを開くと
npm install
コマンドでインストールされたパッケージが確認できます。{ "name": "svelte-app", "version": "1.0.0", "scripts": { "build": "rollup -c", "dev": "rollup -c -w", "start": "sirv public" }, "devDependencies": { "@rollup/plugin-commonjs": "^12.0.0", "@rollup/plugin-node-resolve": "^8.0.0", "rollup": "^2.3.4", "rollup-plugin-livereload": "^1.0.0", "rollup-plugin-svelte": "^5.0.3", "rollup-plugin-terser": "^5.1.2", "svelte": "^3.0.0" }, "dependencies": { "sirv-cli": "^1.0.0" } }サーバーの起動
インストールが完了したら、
npm run dev
コマンドを実行します。
Your application is ready~!
と表示されたらサーバーが起動しているので、http://localhost:5000
にアクセスします。
すると、svelteの初期画面 HELLO WORLD! が表示されます。Your application is ready~! - Local: http://localhost:5000 - Network: Add `--host` to exposeSvelteの環境構築は以上です。
HELLO WORLDを表示させるまでは約10分程度で完了します。フォルダ構成の確認
次にフォルダ構成を確認してみます。
プロジェクト作成後のフォルダ構成は以下のようになっています。
srcフォルダ以下に、App.svelte
とmain.js
が格納されており、この2つのファイルが初期起動時のHELLO WORLDの画面を作っているようです。project ├─node_modules │ ├─public │ └─build ├─scripts └─src ←ここにあるファイルを編集する各ファイルを開いてソースコードを確認してみました。
App.svelte
の{name}
部分にmain.js
で書かれているpropsの値がバインドされているようです。src/App.svelte<script> export let name; </script> <main> <h1>Hello {name}!</h1> <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p> </main> <style> main { text-align: center; padding: 1em; max-width: 240px; margin: 0 auto; } h1 { color: #ff3e00; text-transform: uppercase; font-size: 4em; font-weight: 100; } @media (min-width: 640px) { main { max-width: none; } } </style>src/main.jsimport App from './App.svelte'; const app = new App({ target: document.body, props: { name: 'world' } }); export default app;まとめ
今回は導入から、フォルダ構成の確認まで行いました。
VueのようにCDN読み込みで使えないので、バリバリフロント開発しているところでないと導入のハードルはやや高いように思いました。とはいえ実行速度はVueの50倍らしいし、サンプルを見る限り構文はとてもシンプルみたいなので日本語ドキュメントがもっと充実してきたら普及するのかも知れません。
次はSvelteの公式にある実装例を参考になにか作ろうかと思います。
https://svelte.dev/examples#hello-world
- 投稿日:2020-07-29T11:18:42+09:00
jsPsychで直前のトライアル(画面)での反応に基づいて何かする方法
jsPsych (de Leeuw, 2015)は、心理学のオンライン実験を行うときに便利なJavaScriptライブラリです。詳しい使用方法は、@snishymさんのjsPsychによる心理学実験作成チュートリアルまとめをご覧ください。すばらしくまとまっています。
この記事では、実験中の反応に基づいて何かをする方法について解説します。例として、ある画像に対して反応したあとに、その反応時間を参加者にフィードバックすることを考えてみます。
jsPsychのexamplesフォルダにはたくさんのサンプルプログラムが入っていますが、この中のjspsych-image-keyboard-responseを変更してみましょう。このファイルを別名で(同じ場所に)保存します。そして次のオブジェクトを作成してください。
var feedback = { type: 'html-keyboard-response', stimulus: 'Hello, World!', choices: ['space'], on_start: function(trial) { var all_data = jsPsych.data.get().values(); console.log(all_data); } }feedbackは、html-keyboard-responseプラグインを使っているので、冒頭で次のようにしてプラグインを読み込む必要があります。
<script src="../plugins/jspsych-html-keyboard-response.js"></script>最後に、jsPsych.initの中にあるtimelineを次のように変更します。trial_3をfeedbackと入れ替えています。
timeline: [trial_1, trial_2, feedback],以上の変更を終えたらサンプルプログラムを動かしますが、ここではコンソールの表示を確認したいので次の手順を行ってくうださい。
- Chromeでは,表示 → 開発/管理 → デベロッパーツール
- Firefoxでは,ツール → ウェブ開発 → 開発ツールを表示
- Safariでは,開発 → JavaScriptコンソールを表示
- Edgeでは,F12キーを押す
プログラムを実行すると、2枚の画像が順番に表示されて、最後に Hello, World! と表示されるはずです。
2試行分の反応時間(rt)や刺激として表示されたファイルの名前(stimulus)が確認できますね。これは、
var all_data = jsPsych.data.get().values(); console.log(all_data);の結果です。つまり、
jsPsych.data.get().values();
によって、それまでのすべての結果を得ることができるわけです。all_data[0]
とすれば最初に提示された画像に対する結果を、all_data[1]
とすれば2番目に提示された画像に対する結果を参照することが可能です。ではここで、Hello, World! と表示する代わりに、2番目の画像に対する反応時間を画面に表示することを考えてみましょう。
上記のコンソール画面を見ていただくと分かりやすいですが、2番目の画像に対する反応時間は、
all_data[1].rt
で参照することが可能です。そして、on_start (event) 関数を使うことで stimulus (Hello, World!) を上書きすることが可能です。ぜひ公式サイトの説明もご覧ください。trialオブジェクトがその試行の刺激や受け付けるキー情報などを持っています。
ということで、on_startを以下のように変更すると、Hello, World! ではなく、2番目の画像に対する反応時間が表示されるようになります。
on_start: function(trial) { var all_data = jsPsych.data.get().values(); console.log(all_data); trial.stimulus = all_data[1].rt; }ちなみに、以下のように書いても挙動は同じです。ただ汎用性が高い書き方は上のほうだと思います(すべての試行のデータにアクセスできるため)。
on_start: function(trial) { var last_trial_data = jsPsych.data.getLastTrialData().values(); trial.stimulus = last_trial_data[0].rt; }なお、基本的に上に書いたことと同じものですが、こちらの公式フォーラムでのやりとりも参考になると思います。
- 投稿日:2020-07-29T10:37:04+09:00
JavaScript: date-fnsでタイムゾーンを扱う
この投稿ではJavaScriptの日時ライブラリdate-fnsでタイムゾーンを扱う方法を説明します。
基本知識
date-fnsはJavaScriptのDateを扱うヘルパー関数のセットなので、そもそも
Date
についてよく知っておく必要があります。JavaScriptの
Date
にはタイムゾーンを表すデータが無いJavaScriptの
Date
オブジェクトはタイムゾーンを表すデータを持ちません。実行環境のタイムゾーン設定を変更したとしても、
new Date()
はUTC時刻になります:$ TZ=Asia/Tokyo node -e 'console.log(new Date())' 2020-07-29T00:27:44.573Z $ TZ=UTC node -e 'console.log(new Date())' 2020-07-29T00:27:50.167Z
Date
のコンストラクタはISO 8601の表記をパースできますが、タイムゾーン指定子(+09:00
など)をつけたとしても、UTC時刻に直されます:console.log(new Date('2000-01-01T00:00:00+09:00')) //=> 1999-12-31T15:00:00.000Z
Date
はUTC時刻の1970年1月1日0時0分0秒をnumber
の0
と規定し、その既定値からの相対的な秒数をラップして、日時処理のAPIを生やしたような素朴なオブジェクトなので、タイムゾーンを表すデータを持たないのも理解できるかと思います。date-fnsでのタイムゾーンの扱い
date-fnsとdate-fns-tz
date-fnsの姉妹パッケージにdate-fns-tzがあります。date-fns-tzはタイムゾーンを扱うためのパッケージですが、これが必要となるケースと不要なケースがあります。個別に見ていきます。
ISO 8601の表記をパースする
タイムゾーン指定子がある場合
タイムゾーン指定子を持ったISO 8601形式の日時のパースは、date-fnsの
parseISO
でできます。date-fns-tzは不要です。パースされた日時はUTCになります:import { parseISO } from 'date-fns' const utcDate: Date = parseISO('2000-01-01T00:00:00+09:00') console.log(utcDate) //=> 1999-12-31T15:00:00.000Zタイムゾーンに関して言うと、
Date()
コンストラクタと同じ挙動になります:const utcDate1: Date = parseISO('2000-01-01T00:00:00+09:00') const utcDate2: Date = new Date('2000-01-01T00:00:00+09:00') console.log(utcDate1.getTime() === utcDate2.getTime()) //=> trueタイムゾーン指定子が無い場合
タイムゾーン指定子を持たないISO 8601表記を、タイムゾーンを指定しながらパースする場合は、date-fns-tzの
zonedTimeToUtc
を使います。関数の名が示すとおり、戻り値のDate
オブジェクトはUTC時刻になります。import { zonedTimeToUtc } from 'date-fns-tz' const utcDate: Date = zonedTimeToUtc('2000-01-01T00:00:00', 'Asia/Tokyo') //=> 1999-12-31T15:00:00.000Zもし、この場合に、
parseISO
を使ってしまうと、この"2000-01-01T00:00:00"
はUTCのタイムゾーンを指すものと解釈されるので注意が必要です:const utcDate: Date = parseISO('2000-01-01T00:00:00') //=> 2000-01-01T00:00:00.000Z
Date
オブジェクトをフォーマットするdate-fnsの
format()
関数とは別に、date-fns-tzにはタイムゾーンを指定できるformat()
関数がありますが、この取扱には注意が必要です。date-fns-tzの
format
はタイムゾーン指定による時刻変更はしない
format
はタイムゾーン指定による時刻変更はしません。import { format } from 'date-fns-tz' const utcDate = new Date('2000-01-01T00:00:00') //=> 2000-01-01T00:00:00.000Z format(utcDate, 'yyyy-MM-dd HH:mm:ss', { timeZone: 'Asia/Tokyo' }) //=> "2000-01-01 00:00:00"ご覧のとおり、UTCの0時は日本時間で9時のはずですが、フォーマットされた時刻は0時のままです。
timeZone
を"UTC"
にすると全く同じ文字列にフォーマットされることから、タイムゾーン指定による時間帯の変更はこの関数では行われないことが分かります:format(utcDate, 'yyyy-MM-dd HH:mm:ss', { timeZone: 'UTC' }) //=> "2000-01-01 00:00:00" format(utcDate, 'yyyy-MM-dd HH:mm:ss', { timeZone: 'Asia/Tokyo' }) //=> "2000-01-01 00:00:00"ではdate-fns-tzの
format
関数は何をするかというと、z
やx
のようなタイムゾーン書式に指定されたタイムゾーンを出すことを行ってくれます。format(utcDate, 'yyyy-MM-dd HH:mm:ss xxx', { timeZone: 'UTC' }) //=> "2000-01-01 00:00:00 +00:00" format(utcDate, 'yyyy-MM-dd HH:mm:ss xxx', { timeZone: 'Asia/Tokyo' }) //=> "2000-01-01 00:00:00 +09:00"したがって、タイムゾーンを書式化する必要がない場合は、date-fns-tzの
format
関数を使う理由はありません。UTCな
Date
をAsia/Tokyoの日時にフォーマットするUTCな
Date
オブジェクトを日本時間の日時文字列にフォーマットする場合は、次の手順を踏みます:
- 一旦、date-fns-tzの
utcToZonedTime
関数で、UTCなDate
オブジェクトを日本時間にずらしたDate
オブジェクトに変換する。- 変換後の
Date
オブジェクトをformat
関数でフォーマットするimport { format } from 'date-fns' import { utcToZonedTime } from 'date-fns-tz' const utcDate = new Date('2000-01-01T00:00:00') //=> 2000-01-01T00:00:00.000Z const jstDate = utcToZonedTime(utcDate, 'Asia/Tokyo') //=> 2000-01-01T09:00:00.000Z const jstString = format(jstDate, 'yyyy-MM-dd HH:mm:ss') //=> "2000-01-01 09:00:00"この
format
関数はdate-fnsのものであることに注意してください。もし、タイムゾーン表記をフォーマット後の文字列に含める場合は、date-fns-tzのほうのformat
関数をtimeZone
オプションを指定しつつ使う必要があります。誤って、date-fnsのformat
関数を使うとUTCのタイムゾーン表記になってしまいます:import { format } from 'date-fns' import { format as formatTZ } from 'date-fns-tz' format(jstDate, 'yyyy-MM-dd HH:mm:ss xxx') //=> "2000-01-01 09:00:00 +00:00" ← UTCの表記 formatTZ(jstDate, 'yyyy-MM-dd HH:mm:ss xxx', { timeZone: 'Asia/Tokyo' }) //=> "2000-01-01 09:00:00 +09:00" ← 日本時間の表記
- 投稿日:2020-07-29T09:42:24+09:00
【はじめてのIoT】サイリウムを自動的に光らせてみた【ラブライブ】
はじめに
「推しが複数いるとき、どの色にすればいいか悩む・・・」
「サイリウムで盛り上げたいけど、もっとライブに集中したい」
「夢中になりすぎて、サイリウムの色を変えるのを忘れてしまう」ライブに行った時、このように思ったことはありませんか?
私はあります。私はラブライバーなのですが、箱推し(みんな好き)です。
なので全色光らせたいけど、持つのは2本が限界だし・・・
「次はオレンジだ!」とか考えてるとライブに集中できなかったり・・・ということで、今回は自動的に光るサイリウムを作ってみました!
理想イメージ
ライブと言っても、今はコロナの影響もあって、無観客ライブが増えてきました。
今回もYoutubeのライブをイメージして、曲が始まるとサイリウムが自動的に光ることをゴールにします!
Youtube IFrame Player APIを使って、node.jsでYoutubeを操作できるようにします。
そしてobnizでサイリウムを繋ぎ、Youtubeの再生をトリガーにobnizが動いてサイリウムが光るようにします!実際にできたもの
動画をご覧ください。
【はじめてのIoT】
— カレー大好き★たわちゃん (@unias_tawa) July 29, 2020
サイリウムを自動化してみました。
よりライブに集中できて、尊さが増した気がします。(キモチワルイ)https://t.co/h2GdBsCahX#protoout #ラブライブ #μs #IoT #obniz #駆け出しエンジニア pic.twitter.com/K6lNTO7BS0色々ツッコミどころはあるかと思いますが、
まず、サイリウムといえば、こんなイメージですよね。
すみません、イメージ通りのサイリウムをObnizに繋げるのはハードルが高すぎました。
ということで、フルカラーLED。小っちゃくても明るく光ります。さすがにこれを振るには小さすぎるので、私の分身を作りました。
(工作がとても苦手です。)
見た目こそアレですが、色はちゃんと思い通りに光っています!!!
歌っているメンバーの色が光るようにしていて、
複数メンバーで歌ってる場合は、その子たちの色を交互に点灯させました。
この曲の見せ場!センター真姫ちゃんのソロでは、ばっちり赤く光っております・・・(´;ω;`)真姫ちゃぁぁぁぁぁん?❣️(尊い)
— カレー大好き★たわちゃん (@unias_tawa) July 29, 2020
サイリウム自動化すれば、真姫ちゃんのソロにも全力待機できます。#protoout #ラブライブ #西木野真姫 #IoT #駆け出しエンジニア pic.twitter.com/AKpCxWkwxA動画ではうまくいったかのように見えるのですが、実はYoutube IFrame Player APIをうまく使うことができませんでした。
その結果、、、
Obnizにだけプログラムを仕込み、Youtubeの再生ボタンとObnizのボタンを同時に押すというアナログ戦法となりました。
変な工作しちゃったので、Obnizのボタンがスーパー押しづらかったです。(自業自得)コード
失敗したけど、アナログ版の自動サイリウムのソースコード貼っておきます!
▼秒単位で色指定しているため、かなり長いです。興味がある方はリンクからどうぞ。
https://gist.github.com/twtjudy1128/796cb4666e7cccc6bad327f76987dc11個人的に、関数にメンバー名を使ったのがテンションあがりました。
//メンバーごとに色を設定する // 他のカラーコード参考 http://www.netyasun.com/home/color.html function maki(){ //まきちゃん赤 rgbled.rgb(255, 0, 0); } function eri(){ //エリーチカ水色 rgbled.rgb(0, 186, 255); } function honoka(){ //ほのかオレンジ rgbled.rgb(255, 174, 0); } function umi(){ //うみちゃん青 rgbled.rgb(0, 0, 255); } function rin(){ //凜ちゃんと言えばイエローだよおおおおお rgbled.rgb(255, 255, 0); } function hanayo(){ //花陽グリーン rgbled.rgb(0, 255, 0); } function nozomi(){ //希バイオレット rgbled.rgb(166, 0, 255); } function kotori(){ //ことり白 rgbled.rgb(255, 255, 255); } function nico(){ //にこピンク rgbled.rgb(255, 95, 219); }おわりに
最後までご覧いただき、ありがとうございます!
前回書いた妄想記事がまさかのトレンド入り・・・!
(参照:「ジェイソン・ステイサムで妄想するのが日課になっていたので、いっそBOTにしてみた。」https://qiita.com/twtjudy1128/items/88f3e8f09c449f49456c )「好きこそ物の上手なれ」ということで、今回も自分の好きな「ラブライブ!」を題材に取り組んでみました。
そもそも工作も苦手意識があり、途中で何度も心折れかけたのですが、動画の中で歌うみんなを見ていると不思議と頑張れました。「好きドリブン」大事ですね。
プログラミングの勉強を始めて3週間のひよっこちゃんなのですが、LGTMをいただくと非常に励みになります。
Qiitaにいる皆さん、あったかくて幸せです!最後に、ことりちゃんから一言。
「ラブライバーのみんなぁ~!LGTMくれないと、ことりのおやつにしちゃうぞぉ~!!!」
- 投稿日:2020-07-29T09:42:24+09:00
【はじめてのIoT】サイリウムを自動的に光らせてみた【ラブライブ】
はじめに
「推しが複数いるとき、どの色にすればいいか悩む・・・」
「サイリウムで盛り上げたいけど、もっとライブに集中したい」
「夢中になりすぎて、サイリウムの色を変えるのを忘れてしまう」ライブに行った時、このように思ったことはありませんか?
私はあります。私はラブライバーなのですが、箱推し(みんな好き)です。
なので全色光らせたいけど、持つのは2本が限界だし・・・
「次はオレンジだ!」とか考えてるとライブに集中できなかったり・・・ということで、今回は自動的に光るサイリウムを作ってみました!
理想イメージ
ライブと言っても、今はコロナの影響もあって、無観客ライブが増えてきました。
今回もYoutubeのライブをイメージして、曲が始まるとサイリウムが自動的に光ることをゴールにします!
Youtube IFrame Player APIを使って、node.jsでYoutubeを操作できるようにします。
そしてobnizでサイリウムを繋ぎ、Youtubeの再生をトリガーにobnizが動いてサイリウムが光るようにします!実際にできたもの
動画をご覧ください。
【はじめてのIoT】
— カレー大好き★たわちゃん (@unias_tawa) July 29, 2020
サイリウムを自動化してみました。
よりライブに集中できて、尊さが増した気がします。(キモチワルイ)https://t.co/h2GdBsCahX#protoout #ラブライブ #μs #IoT #obniz #駆け出しエンジニア pic.twitter.com/K6lNTO7BS0色々ツッコミどころはあるかと思いますが、
まず、サイリウムといえば、こんなイメージですよね。
すみません、イメージ通りのサイリウムをObnizに繋げるのはハードルが高すぎました。
ということで、フルカラーLED。小っちゃくても明るく光ります。さすがにこれを振るには小さすぎるので、私の分身を作りました。
(工作がとても苦手です。)
見た目こそアレですが、色はちゃんと思い通りに光っています!!!
歌っているメンバーの色が光るようにしていて、
複数メンバーで歌ってる場合は、その子たちの色を交互に点灯させました。
この曲の見せ場!センター真姫ちゃんのソロでは、ばっちり赤く光っております・・・(´;ω;`)真姫ちゃぁぁぁぁぁん?❣️(尊い)
— カレー大好き★たわちゃん (@unias_tawa) July 29, 2020
サイリウム自動化すれば、真姫ちゃんのソロにも全力待機できます。#protoout #ラブライブ #西木野真姫 #IoT #駆け出しエンジニア pic.twitter.com/AKpCxWkwxA動画ではうまくいったかのように見えるのですが、実はYoutube IFrame Player APIをうまく使うことができませんでした。
その結果、、、
Obnizにだけプログラムを仕込み、Youtubeの再生ボタンとObnizのボタンを同時に押すというアナログ戦法となりました。
変な工作しちゃったので、Obnizのボタンがスーパー押しづらかったです。(自業自得)コード
失敗したけど、アナログ版の自動サイリウムのソースコード貼っておきます!
▼秒単位で色指定しているため、かなり長いです。興味がある方はリンクからどうぞ。
https://gist.github.com/twtjudy1128/796cb4666e7cccc6bad327f76987dc11個人的に、関数にメンバー名を使ったのがテンションあがりました。
//メンバーごとに色を設定する // 他のカラーコード参考 http://www.netyasun.com/home/color.html function maki(){ //まきちゃん赤 rgbled.rgb(255, 0, 0); } function eri(){ //エリーチカ水色 rgbled.rgb(0, 186, 255); } function honoka(){ //ほのかオレンジ rgbled.rgb(255, 174, 0); } function umi(){ //うみちゃん青 rgbled.rgb(0, 0, 255); } function rin(){ //凜ちゃんと言えばイエローだよおおおおお rgbled.rgb(255, 255, 0); } function hanayo(){ //花陽グリーン rgbled.rgb(0, 255, 0); } function nozomi(){ //希バイオレット rgbled.rgb(166, 0, 255); } function kotori(){ //ことり白 rgbled.rgb(255, 255, 255); } function nico(){ //にこピンク rgbled.rgb(255, 95, 219); }おわりに
最後までご覧いただき、ありがとうございます!
前回書いた妄想記事がまさかのトレンド入り・・・!
(参照:「ジェイソン・ステイサムで妄想するのが日課になっていたので、いっそBOTにしてみた。」https://qiita.com/twtjudy1128/items/88f3e8f09c449f49456c )「好きこそ物の上手なれ」ということで、今回も自分の好きな「ラブライブ!」を題材に取り組んでみました。
そもそも工作も苦手意識があり、途中で何度も心折れかけたのですが、動画の中で歌うみんなを見ていると不思議と頑張れました。「好きドリブン」大事ですね。
プログラミングの勉強を始めて3週間のひよっこちゃんなのですが、LGTMをいただくと非常に励みになります。
Qiitaにいる皆さん、あったかくて幸せです!最後に、ことりちゃんから一言。
「ラブライバーのみんなぁ~!LGTMくれないと、ことりのおやつにしちゃうぞぉ~!!!」
- 投稿日:2020-07-29T09:42:23+09:00
好きなキー好きなBPMでパプれるスピーカーをline botとobnizで作ってみた
はじめに
好きな曲とか聞いていて「あーこれ音域低めだし歌えるんじゃね?ヨユウッショ」って思ってカラオケ行ったら全然音域足らなかったみたいな経験ありませんか?
僕はかの有名なパプリカで実際ありました、裏切られた気分になりますよね。
原曲キーで歌わないにしても、どれぐらいのキーが程よいのか分かりませんよね。そこで、自宅でも自由にパプってあらかじめ自分にあったキーを探しておけるスピーカーをobnizとline botで作ってみました。
コードはGistに載せてあるので、是非コピーしてハンズオンしてエビバディパプろうぜ!
(Gist: https://gist.github.com/canonno/2209c7d68870b99d256cb4bac110b37d)完成デモ
メロディの送信
obnizの圧電スピーカは周波数を引数として関数を実行する必要がありますメンドクサイィィィ
ということで、lineから「ドレミ」のような身近な表記でobnizに音を送信できるようにしました。
ピアノの白鍵が「ドレミ」、オクターブは数字を付けて「ド1レ1ミ1」、半音はシャープsかフラットfをつけて「ドs」「ミf」と表記できます。
オクターブ高めの半音も「ドs1」といった表記で対応できます。obnizで再生
送った楽譜はnode.jsで周波数に変換されobnizに送られます。
画面に[play]が出ているところでスイッチを押すと再生できます。
obnizでパプリました#obniz#パプリカ #IoT #protoout pic.twitter.com/09Z97KZfF4
— canonno (@canonno_blog) July 28, 2020キー選択・BPM選択
モードを[play][key][BPM]の3つ用意しました。
モードは3つ
— canonno (@canonno_blog) July 28, 2020
keyでキーが変えられ、BPMで速さを変えられます#obniz pic.twitter.com/UpQCoOSQhw[key]を変えると曲の音程が変わります。
ハイパプリカ#obniz pic.twitter.com/zrHaHXDRQE
— canonno (@canonno_blog) July 28, 2020[BPM]を変えると曲の速さが変わります。
ハイパプリカ2#obniz pic.twitter.com/3gUBH2pHW7
— canonno (@canonno_blog) July 28, 2020さあ自分好みの自分だけのパプリカを探せ!(大袈裟)
環境
node v12.18.2
Visual Studio Code 1.47.1
obniz手順
linebotを作る
他記事を参考にlinebotを作成しました。
(参考:https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)
linebotへ投稿した「ドレミ」の楽譜をMessaging API経由で送信し、Node.jsで実装した処理を経てobnizの圧電スピーカへ送るという手順で実装しました。
Node.jsは今回ngrokを使って接続しました。「ドレミ」を周波数に変換するまで
「ドレミ」から周波数へ変換するにはどうするか。
オクターブ表記を含めると「ド」や「ドs」や「ド1」や「ドs1」といった様々なバリエーションに対応する必要があります。
そこで、3段階の表記変換を挟む方針にしました。
一つずつ見ていきますね。①「ドレミ」から三文字表記へ
まずはドレミから三文字表記に直す処理を考えました。
三文字表記のお作法は
一文字目:ドレミ
二文字目:ナチュラルn、シャープs、フラットf
三文字目:オクターブ0,1,2
「ファ」のみ二文字なので最初に「フ」に置き換え、lineテキストの左から一文字ずつひたすら判定と変換作業を行います。//lineからのテキストには、ただの「ド」から「ドs1」まで一音につき1文字から3文字まで表記がある //それらを「ド」なら「ドn0」といった、半音・全音やオクターブ情報を付与した3文字コードに置き換える function score_to_name(line_text){ //ファが2文字なので一文字に置き換え、最後にeeを付与し、最後であることを明示する score = line_text.replace(/ファ/g,"フ") +"ee" node_list = [] //左から一文字ずつ読んでいく for (i = 0; i < score.length-2;i++){ node = score.slice(i,i+3); //ドレミで始まっているか type1 = /^[ドレミファソラシー]/g //ドレミの次はsか数字か type2 = /^[ドレミファソラシー][fs\d]/g //ドレミの次にsがつくか type3 = /^[ドレミファソラシー][fs]/g //ドレミの後にsと数字の両方がつくか type4 = /^[ドレミファソラシー][fs]\d/g if (type1.test(node)){ if(type2.test(node)){ if(type3.test(node)){ if(type4.test(node)){ //ドs1みたいな表記>>そのまま格納 node_list.push(node); }else{ //ドsみたいな表記>>オクターブ情報を付与 node_list.push(node[0]+node[1]+"0"); } }else{ //ド1みたいな表記>>ナチュラルであるnを付与 node_list.push(node[0]+"n"+node[1]); } }else{ //ドみたいな表記>>ナチュラルnとオクターブ情報を付与 node_list.push(node[0]+"n"+"0"); } }else{ //音階から始まってない>>無視して次の文字へ ; } } return node_list }②三文字表記から数値表記へ
人によっては「三文字表記から周波数表記に直接変換すればいいんじゃない?」と思われる方もいるかもしれません。
ここであえて数値表記を挟むことで、keyを変えることができるようになります。
数値表記でいったん保持すれば、obniz側のkey+1や+2というキー操作を、ただ数値に+1や+2するだけでキー調整ができるようになります。
なので、少々面倒ですが数値表記に一度変換しております。//上記で3文字コードになったものを、数値に置き換える //一番低い音から1、2、3、と半音ごとに1ずつ上がる数字に置き換える function node_to_int(node_list){ //3文字コードと数字の対応表をインポート const dict = JSON.parse(fs.readFileSync('node_dict.txt', 'utf8')); name2int_dict = dict["name_to_int"] //ひたすら数値化 int_list = [] for (i=0;i<node_list.length;i++){ int_list.push(name2int_dict[node_list[i]]); } return int_list; }ここでインポートしている対応表は、三文字表記⇔数値表記⇔周波数表記を行うために作ったdictになっています。
こちらの記事を参考に作りました。
dictはGistに置いておきますので使われる方はぜひ使ってみてくださいね。
(Gist: https://gist.github.com/canonno/2209c7d68870b99d256cb4bac110b37d){"name_to_int":{"ドn-3":1,"ドs-3":2,"レn-3":3, ... "シf3":83}, "int_to_hertz":{"1":32.703,"2":34.648,"3":36.708, ..., "84":3951.066}}③数値表記から周波数表記
数値表記にした後は、obniz側の操作で変化するkeyを足し引きし、周波数表記にします。
「ドー」の「ー」は数値表記で100としており、周波数表記にする場合は「ひとつ前の音の長さを1のばす」という処理にしています。
こうすれば「ドーーー」でも「ドーーーーーー」でも対応可能になります。//上記で数値情報になったものを周波数と音の長さに置き換える //key情報を引数に入れ、keyの数値分音をずらす処理も行う function int_to_fre(int_list,key){ //数値と周波数との対応表のインポート const dict = JSON.parse(fs.readFileSync('node_dict.txt', 'utf8')); int2fre_dict = dict["int_to_hertz"] //ひたすら置き換える fre_list = [] for (i=0;i<int_list.length;i++){ //「ー」の場合(数値を100としている)、ひとつ前の音のlengthを1伸ばす if (int_list[i]==100){ last_length = fre_list[fre_list.length-1]["length"] fre_list[fre_list.length-1]["length"] = last_length + 1 //普通の音の場合そのまま変換 }else{ fre_list.push({"frequency":int2fre_dict[int_list[i]+key],"length":1}); } } return fre_list; }Obniz側での操作の処理
あとはobniz側での操作を実装します。
まずはobnizと接続する処理。
こちらの記事をかなり参考にしました。
(参考:https://qiita.com/Naru0607/items/b523cf9f67fa18bcf7d7)obniz側で操作するとobniz.switch.onchange以降の処理が走るので、keyやBPMといった変数をその範囲外で宣言する必要がある点に注意です。
最初はonchange以降で宣言し、keyやBPMを変えても反映されずかなり苦戦しました。
obniz側で操作したらどこがどう走るかはしっかり理解しないとだめですね・・・。//obnizで出力 function sound_with_obniz(line_text){ //もろもろ初期設定 const Obniz = require('obniz'); const { text } = require("express"); const obniz = new Obniz('Obniz_ID'); // Obniz_IDに自分のIDを入れます //obnizと接続 obniz.onconnect = async function () { const speaker = obniz.wired('Speaker', {signal:0, gnd:1}); //obniz上での設定パラメータ key = 0; BPM = 180; mode_list = ['play','key','BPM'] mode_idx = 300 mode = mode_list[mode_idx%3] // ディスプレイ処理 obniz.display.clear(); // 一旦クリアする obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); // スイッチの反応を常時監視 obniz.switch.onchange = async function(state) { //表示がplayの時の操作 if (mode == 'play'){ //押したら音が鳴る if (state === 'push') { one_tempo = Math.round(60/2/BPM*1000); node_list = score_to_name(line_text); int_list = node_to_int(node_list); fre_list = int_to_fre(int_list,key) for (i=0;i<fre_list.length;i++){ sound(fre_list[i]["frequency"],fre_list[i]["length"]); } obniz.display.clear(); obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); //離せば何も起こらない } else if (state === 'none') { speaker.stop(); //右にすればモードが変わる } else if (state === 'right'){ mode_idx += 1; mode = mode_list[mode_idx%3] obniz.display.clear() obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); //左にすればモードが変わる } else if (state === "left"){ mode_idx -= 1; mode = mode_list[mode_idx%3] obniz.display.clear() obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); } //表示がkeyの時の操作 } else if (mode == 'key'){ //押したらkeyの変更画面へ if (state === 'push') { mode = 'key_select' obniz.display.clear(); obniz.display.print(mode+"\nkey:"+key); //離せば何も起こらない } else if (state === 'none') { speaker.stop(); //右に倒せばモードが変わる } else if (state === 'right'){ mode_idx += 1; mode = mode_list[mode_idx%3]; obniz.display.clear(); obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); //左に倒してもモードが変わる } else if (state === "left"){ mode_idx -= 1; mode = mode_list[mode_idx%3]; obniz.display.clear(); obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); } //表示がBPMの時の操作 } else if (mode == 'BPM'){ //押せばBPMの設定画面になる if (state === 'push') { mode = 'BPM_select' obniz.display.clear() obniz.display.print(mode+"\nBPM:"+BPM) //離せば何も起こらない } else if (state === 'none') { speaker.stop(); //右に倒せばモードが変わる } else if (state === 'right'){ mode_idx += 1; mode = mode_list[mode_idx%3] obniz.display.clear() obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); //左に倒してもモードが変わる } else if (state === "left"){ mode_idx -= 1; mode = mode_list[mode_idx%3] obniz.display.clear() obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); } //キーセレクト画面での操作 }else if (mode == 'key_select'){ //押せば元の画面に戻る if (state === 'push') { mode = mode_list[mode_idx%3] obniz.display.clear() obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); //離せば何も起こらない } else if (state === 'none') { speaker.stop(); //右に倒せばキーが上がる } else if (state === 'right'){ key += 1; obniz.display.clear() obniz.display.print(mode+"\nkey:"+key) //左に倒せばキーが下がる } else if (state === "left"){ key -= 1; obniz.display.clear() obniz.display.print(mode+"\nkey:"+key) } //BPM設定画面での操作 } else if (mode == 'BPM_select'){ //押せば元の画面に戻る if (state === 'push') { mode = mode_list[mode_idx%3] obniz.display.clear() obniz.display.print(mode + "\nkey:"+ key+" BPM:"+BPM); //離せば何も起こらない }else if (state === 'none') { speaker.stop(); //右に倒せばBPMが上がる } else if (state === 'right'){ BPM += 20; obniz.display.clear() obniz.display.print(mode+"\nBPM:"+BPM) //左に倒せばBPMが下がる } else if (state === "left"){ BPM -= 20; obniz.display.clear() obniz.display.print(mode+"\nBPM:"+BPM) } } } //一つ一つの音を出力する処理 function sound(fre,length){ speaker.play(fre); obniz.wait(length*one_tempo); speaker.stop(); obniz.wait(100); } } }最後にlineと接続!
以上がすべてfunctionで実装できたので、あとは順番に実行していくだけ!
'use strict'; const fs = require("fs"); const express = require('express'); const line = require('@line/bot-sdk'); const { compileFunction } = require('vm'); const PORT = process.env.PORT || 3000; const config = { channelSecret: 'チャンネルシークレットトークン', channelAccessToken: 'チャンネルアクセストークン' }; const app = express(); //lineからくるテキスト初期値 var line_text = ""; //リクエストによる処理 app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用 app.post('/webhook', line.middleware(config), (req, res) => { events = req.body.events line_text = events[0].message.text Promise .all(req.body.events.map(handleEvent)) .then((result) => res.json(result)); }); const client = new line.Client(config); async function handleEvent(event) { //テキストでない場合は無視 if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } //lineテキストをobnizへ流し込む sound_with_obniz(line_text) //lineへの返信 return client.replyMessage(event.replyToken, { type: 'text', text: 'obnizでの操作をお楽しみください' //実際に返信の言葉を入れる箇所 }); } app.listen(PORT); console.log(`Server running at ${PORT}`);今後やりたいこと
パプリカ演奏できたことはできたけれど、低音がないと少し味気ないですよね。
次は和音に挑戦して、低音付きのガチパプリカを演奏できるか検証してみたい。
そしてあわよくばバズって米津玄師に会いたプラス今回の実装だと、lineから楽譜を一度インポートすると、関数を一度止めるまで楽譜が変更できない実装になっています。
関数が動いている状態でlineから別の楽譜を送信しても上書きすることができません。
関数のon/offをline側でできるようになったり、楽譜管理・保存とかできたら良いなあとか思ったりしています。いつできるやらという感じですが、生暖かい目で見守っていただけると嬉しいです!
最後までご覧いただきありがとうございました!
- 投稿日:2020-07-29T09:32:43+09:00
2020.7.28
JavaScriptの繰り返し処理について、
for ([①初期化式]; [②条件式]; [③加算式]) { // 繰り返す処理の内容 }初期化式
for文の中で使用する変数を定義している。
定義した変数は②条件式③加算式で使用されることで
「今何回目の処理か」を判定している。②条件式
この条件式の返り値が trueで有ると処理が行われる。
③加算式
初期化式として定義した変数の増減を記述できる。
i = i+1とすれば1周ごとに1が加算されて、処理が実行される。
i = i + 1の省略形として以下のような選択肢がある。
i += 1
i++以下に実例を記載する。
num = 1; for (let i = 1; i <= 10; i += 1) { console.log(`${num}回目の出力`) num += 1 }上記のコードでは10回処理が行われる。
numの値によって出力される文字が変わってくる。
上記のような簡単なコードなら
①初期化式を絡めて以下のようにも書けそうだ。for (let i = 1; i <= 10; i += 1) { console.log(`${i}回目の出力`) }今回は[①初期化式]; [②条件式]; [③加算式]が単純なものを学んだが、
今後は[①初期化式]; [②条件式]; [③加算式]が繰り返す処理の内容に絡んだ複雑なものもあるのではないかと予想する。
- 投稿日:2020-07-29T09:10:57+09:00
【obniz×LINE Messaging API】音と光を使い侵入防止システムを製作する
目的
私の実家では、夏場に玄関を開放し風通しを良くしていると、野良猫が侵入し、食べ物が被害にあいます、そこで今回はobnizで侵入防止システムを製作したいと思います。(効果は保証できません)
できたもの
距離センサーの50㎝以内に入り5秒経過したら、LEDとスピーカーで警告し、システムが作動したことをLINE Messaging APIを使用してプッシュ通知します。映像ではわかりませんが、スピーカーから11khzの音を鳴らしてます。
構成
全体像
配線図
使用部品
obniz Board 1Y
- https://obniz.io/ja/products/obnizboard1y/
- 6,300円(税抜)
抵抗内蔵5mm青色LED(5V用) OSB5SA5B64A-5V
圧電スピーカー(圧電サウンダ)(13mm)PKM13EPYH4000-A0
シャープ測距モジュール GP2Y0A21YK
100均のタッパー
USB タイプCのUSB2.0ケーブル(家にあったものを使用)
モバイルバッテリー(家にあったものを使用)
環境
- node.js v14.5.0
- @line/bot-sdk 7.0.0
- express 4.17.1
- obniz 3.7.0
コード
index.js'use strcit'; const Obniz = require('obniz'); const obniz = new Obniz('××××-××××'); // Obniz_IDに自分のIDを入れます const line = require("@line/bot-sdk"); const express = require("express"); const PORT = process.env.PORT || 3030; const config = { channelSecret: '{チャネルシークレット}', channelAccessToken: '{アクセストークン}' }; const userId = '{ユーザーid}'; const app = express(); app.post('/webhook', line.middleware(config), (req, res) => { console.log(req.body.events); Promise .all(req.body.events.map(handleEvent)) .then((result) => res.json(result)); }); const client = new line.Client(config); obniz.onconnect = async function () { // 各センサーを呼び出す // 距離センサーを呼び出す const GP2Y0 = obniz.wired("GP2Y0A21YK0F", { vcc:0, gnd:1, signal:2 }); // 指す場所を変えているので対応した数字に変更 // LEDを呼び出す const led = obniz.wired('LED', { anode:5, cathode:6 }); // スピーカーを呼び出す const speaker = obniz.wired('Speaker', { signal:10, gnd:11 }); obniz.display.clear(); // 一旦クリアする obniz.display.print('Hello obniz!'); // Hello obniz!という文字を出す // setIntervalで間隔を作る setInterval(async function () { const distance = await GP2Y0.getWait(); // 距離(mm)をコンソールに表示 console.log(distance + ' mm'); // displayに反映 obniz.display.clear(); // 一旦クリアする obniz.display.print(distance + ' mm'); // 英語が出力できる // 近づいてきたら判定する if (distance < 500) count++; else count = 0; switch(count) { case 5: obniz.display.clear(); // 一旦クリアする speaker.play(11000); // スピーカー On 11khz led.on();// LED ON //LINEにメッセージ送信 await client.pushMessage(userId, { type: 'text', text: '侵入防止システムを発動しました', }); break; default: speaker.stop();// スピーカー OFF led.off();// LED OFF } }, 1000); // 1000ミリ秒 = 1秒 }; app.listen(PORT); console.log(`Server running at ${PORT}`);参考にした記事
LINEmessagingAPIの基礎部分で参考にしました。
1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefestPush通知の部分を参考にしました。
LINE BOTでラズパイから部屋の温度を教えてもらう【Push API】【Node.js】失敗
LEDはフルカラーを使用し色を使い分ける予定でしたが、途中でショートさせて焼いてしまいました。ですので、今回は青色のLEDを使用しました。
終わりに
今回は距離センサを使ったので遮るものすべてにはんのうしてしまいます。
公式サイトでは、カメラで画像認識を使用している作例もあり、特定の対象物にのみ反応するようにAI機能を搭載できたらいいなと思いました。
- 投稿日:2020-07-29T04:06:51+09:00
ReactでAPIを使用する際によく見る"fetch(URL).then"のメソッドチェーンについて
背景
Reactからjson形式のAPIを利用する方法として下記のような方法が挙げられている。 (Reactに限った書き方ではないが)
useApi.js1 fetch('http://example.com/api/') 2 .then((response) => response.json()) 3 .then((response) => { 4 console.log(response.hoge); 5 })しかし、データ(response.hoge)のみを利用するのであれば、
useApi.js2 .then((response) => response.json())は不要なのでは?と思い、コメントアウトしたところ、
useApi.js4 console.log(response.hoge);がundefinedとなった。
2行目の有無で3行目の引数の中身が変わっているような挙動だ。調べた結果
Promiseの.then(というより、アロー関数)には、下記の特徴があるようだ。
- 単文の場合は、{}を省略可能。
- {}を省略した場合は、単文の処理結果をPromiseでwrapしたもの(?)が自動でreturnされる。
この特徴をメソッドチェーンとして活用したのが冒頭のコードとなっている。
ちなみに、自分で
useApi_2.js1 fetch('http://example.com/api/') 2 .then((response) => { 3 const res_json = response.json() 4 console.log(res_json.hoge); 5 })としても同じ実行結果にはならない。(Promiseでwrapしていないため)
結論
自分でPromise返す処理を書くより、メソッドチェーンを活用したほうが楽。
useApi.js1 fetch('http://example.com/api/') 2 .then((response) => response.json()) 3 .then((response) => { 4 console.log(response.hoge); 5 })参考にさせていただいたサイト様
- 投稿日:2020-07-29T00:54:01+09:00
React Native × Typescript のインストールをやってみた
0. Prepare Today's environment
Windows 10
Node v12.18.2
Android Studio 3.5
→Android StudioはAndroid SDKとAndroid Virtual Deviceを使用するため。1. Install react-native-community/cli
「react-native-community/cli」はReact Native × Typescriptのテンプレートを提供してくれて便利。
$ npm install -g @react-native-community/cli npm WARN deprecated @hapi/joi@15.1.1: This version has been deprecated and is no longer supported or maintained npm WARN deprecated @hapi/address@2.1.4: This version has been deprecated and is no longer supported or maintained npm WARN deprecated @hapi/topo@3.1.6: This version has been deprecated and is no longer supported or maintained npm WARN deprecated @hapi/bourne@1.3.2: This version has been deprecated and is no longer supported or maintained npm WARN deprecated @hapi/hoek@8.5.1: This version has been deprecated and is no longer supported or maintained npm WARN deprecated core-js@2.6.11: core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3. npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2. npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated C:\Users\takas\AppData\Roaming\npm\react-native -> C:\Users\takas\AppData\Roaming\npm\node_modules\@react-native-community\cli\build\bin.js > core-js@2.6.11 postinstall C:\Users\takas\AppData\Roaming\npm\node_modules\@react-native-community\cli\node_modules\core-js > node -e "try{require('./postinstall')}catch(e){}" Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library! The project needs your help! Please consider supporting of core-js on Open Collective or Patreon: > https://opencollective.com/core-js > https://www.patreon.com/zloirock Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -) npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.2.7 (node_modules\@react-native-community\cli\node_modules\jest-haste-map\node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) npm WARN @react-native-community/cli@4.10.1 requires a peer of react-native@^0.62.0-rc.0 but none is installed. You must install peer dependencies yourself. + @react-native-community/cli@4.10.1 added 614 packages from 353 contributors in 37.963s2. Check compatible versions between react-native-community/cli & react-native
「react-native-community/cli」のGitHubページでReact Nativeとの互換関係を確認。
インストールされたのは4.10.1なので0.62.0が対応。
3. Create react-native-template-typescript project
React Native × Typescriptのテンプレートでプロジェクトを作成。
ちなみに、プロジェクト名には - (ハイフン)とか使えない文字があるので注意。npx react-native init onlibrary_frontend --template react-native-template-typescript --version 0.62.04. Check Android SDK folder
Android Studioのインストール時に格納されたAndroid SDKのフォルダを確認。
5. Create local.properties
プロジェクト直下のandroidフォルダ配下にlocal.propertiesを作成。
Android SDKの格納先を記載。
ちなみに、local.propertiesの代わりにWindows環境変数でANDROID_SDK_ROOTを作成でもOK。$ cat android/local.properties sdk.dir = C:\\Users\\takas\\AppData\\Local\\Android\\Sdk6. Launch Android Virtual Device
Android Studioのインストール時に同梱されたAndroid Virtual Deviceを作成して起動。
作成する仮想モバイルデバイスは任意。
念のため、起動前にデータを削除。
7. Check Android Virtual Device ID
起動された仮想モバイルデバイスのIDを確認。
$ adb devices List of devices attached emulator-5554 device8. Run react-native-template-typescript project
React Nativeの実行はプロジェクト直下で「react-native run-android」のコマンド一発。
起動中の仮想モバイルデバイスのIDに対しての操作が標準出力に流れて表示。$ react-native run-android info Running jetifier to migrate libraries to AndroidX. You can disable it using "--no-jetifier" flag. Jetifier found 967 file(s) to forward-jetify. Using 4 workers... info Starting JS server... info Installing the app... Starting a Gradle Daemon, 1 busy Daemon could not be reused, use --status for details > Task :app:stripDebugDebugSymbols UP-TO-DATE Compatible side by side NDK version was not found. > Task :app:installDebug 12:51:41 V/ddms: execute: running am get-config 12:51:42 V/ddms: execute 'am get-config' on 'emulator-5554' : EOF hit. Read: -1 12:51:42 V/ddms: execute: returning Installing APK 'app-debug.apk' on 'Nexus_6_API_28(AVD) - 9' for app:debug 12:51:42 D/app-debug.apk: Uploading app-debug.apk onto device 'emulator-5554' 12:51:42 D/Device: Uploading file onto device 'emulator-5554' 12:51:42 D/ddms: Reading file permision of C:\workspace\onlibrary\onlibrary_frontend\android\app\build\outputs\apk\debug\app-debug.apk as: rwx------ 12:51:42 V/ddms: execute: running pm install -r -t "/data/local/tmp/app-debug.apk" 12:51:45 V/ddms: execute 'pm install -r -t "/data/local/tmp/app-debug.apk"' on 'emulator-5554' : EOF hit. Read: -1 12:51:45 V/ddms: execute: returning 12:51:45 V/ddms: execute: running rm "/data/local/tmp/app-debug.apk" 12:51:45 V/ddms: execute 'rm "/data/local/tmp/app-debug.apk"' on 'emulator-5554' : EOF hit. Read: -1 12:51:45 V/ddms: execute: returning Installed on 1 device. Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0. Use '--warning-mode all' to show the individual deprecation warnings. See https://docs.gradle.org/6.2/userguide/command_line_interface.html#sec:command_line_warnings BUILD SUCCESSFUL in 1m 43s 27 actionable tasks: 2 executed, 25 up-to-date info Connecting to the development server... info Starting the app on "emulator-5554"... Starting: Intent { cmp=com.onlibrary_frontend/.MainActivity }9. Display React Native App