20200729のJavaScriptに関する記事は28件です。

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');
    });
  });
});

再度挙動確認して完了です!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

obniz×LINE Messaging API】ラジコンをリッチメニューから遠隔操作する。自動ブレーキ付き

やったこと

前回作成した【obniz×LINE Messaging API】音と光を使い侵入防止システムを製作するの時に使用したタッパを乗せて走らせてみました。

できたもの

LINEのリッチメニューから操作でき、10㎝以内に障害物があると自動で停止します。

構成

全体像

今回製作したシステムの全体像です。
Image from Gyazo

配線図

obnizの配線図です。
Untitled Sketch2_ブレッドボード.png

使用部品

環境

  • 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

終わりに

公式サイトのラジコン類はスマートに動いていて次回の目標となります、できるかどうかはわかりませんが、次に作るときはカメラで画像認識を使用して走行させてみたいと思います。今回購入したキットは、格安品でまっすぐ進むよう調整するのが大変でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【obniz×LINE Messaging API】ラジコンをリッチメニューから遠隔操作する。自動ブレーキ付き

やったこと

前回作成した【obniz×LINE Messaging API】音と光を使い侵入防止システムを製作するの時に使用したタッパを乗せて走らせてみました。

できたもの

LINEのリッチメニューから操作でき、10㎝以内に障害物があると自動で停止します。

構成

全体像

今回製作したシステムの全体像です。
Image from Gyazo

配線図

obnizの配線図です。
Untitled Sketch2_ブレッドボード.png

使用部品

環境

  • 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

終わりに

公式サイトのラジコン類はスマートに動いていて次回の目標となります、できるかどうかはわかりませんが、次に作るときはカメラで画像認識を使用して走行させてみたいと思います。今回購入したキットは、格安品でまっすぐ進むよう調整するのが大変でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Hooksのメモ

・hooksは関数コンポーネント内でstateに値の追加や更新が行えるようにするもの?
・this.setState()のような処理を行う関数を自動で作成してくれる?

index.js
import 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.js
import 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"));

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

axiosライブラリを用いてクエリパラメータを指定する方法(GET,POST)

はじめに

時々忘れることがあるので、今回も自分用メモとして残します。
今回は、簡潔に非同期処理を記述する為にasyncとawaitを利用していますが説明等はしておりません。ご了承くださいませ🙇‍♂️

GETリクエスト

主にAPIを叩いて、データを取得する時に利用します。

const response = await axios.get(url, {
  params: {
    id: 1,
    name: 'hoge'
  }
})

上記のように記述すると、id=1とname='hoge'をクエリパラメータとして渡すことができます。
urlの箇所は、APIにアクセスしたいエンドポイントを記述してください:writing_hand:

また、直接URLに記述する方法もあります。

const response = await axios.get('url?id=1&name="hoge"')

複数のパラメータを指定する場合は、&記号(アンパサンド)でつないで記述します。

POSTリクエスト

主にAPIを叩いて、データを保存する時に利用します。

const response = await axios.post(url, {
  id: 1,
  name: 'hoge'
})

第二引数に保存したいデータをオブジェクトで指定します。

はい、以上になります。

おわりに

毎回、自分用のメモとして記述してしまってすみません...もし今回の記事で誤字や脱字等ありましたらご指摘頂けると助かります。閲覧して頂き、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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演算子の有無で動作が変わることに注意してください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptのreplaceで、置換後の文字列を動的に変えたい

(ぜんぜんわからないので、有識者に、教えてもらおう、という気持ちで記事書いてます)

Javascriptのreplaceで、置換後の文字列を動的に変えたい

https://github.com/santarou6/tikan/blob/master/tikan_v1.html

ss1.png

やりたいことは、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>

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js+Flaskで画像のアップロード機能

概要

今回はフロントエンドにVue.js、バックエンドにFlaskを用いた画像認識アプリを作ります。
ひとまず今回は画像アップロード機能までの実装です。

環境

  • Docker
  • Vue-cli
  • flask (pipenv)

上記の環境で環境構築しました。
手順や詳細は以下のリンクを参照してください。

Vue + Flask on Docker

要所の説明

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)
    })
  1. 取得した画像はBase64化がなされている。「data:image/jpeg:base64,〜」
  2. FormDataにより、データをHTTPリクエストで「キー:値」の形式へ。
  3. 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)
  1. FormDataの内部に「data:image/jpeg:base64,〜」が存在。ファイル名を取得。
  2. Pillow(PIL)で画像を取得。
  3. 画像の保存。

全体像

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)

様子

スクリーンショット 2020-07-29 18.47.42.png

こんな感じ

非常に参考になりました

https://developer.mozilla.org/ja/docs/Web/HTTP/CORS
画像をPOST、顔検出、canvasで顔にお絵かき

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者でもできる】仕事が忙しくても、赤ちゃんの熱中症の危機にすぐに気づけるIoTをつくった

はじめに

少し前に、2才のこどもを保育園に送り忘れ、
かつ、車の中に放置してしまい、亡くなってしまう
痛ましい事件が起きました。

本当に悲しい出来事であり、あってはならないことですが、
人ごとではないような気がしました。

何かに集中してしまって、
つい何かやるべきことが抜け落ちてしまうことはある人も多いのではないでしょうか。

今回は、それと類似のシーンとして、
「仕事が忙しく、赤ちゃんの部屋が高温になっていることに気づけなかった」ということがないように、高温になればアラートが出て、気づける仕組みをIoTを使って、できないかとチャレンジしました。

作りたいイメージ

温度センサーによって、常に部屋の温度を監視し、
赤ちゃんにとって快適な温度(26-28度らしい)から外れた時に、
自動でLINEに通知がくるもの。

さらに、LINEの通知に気づかない可能性があるので、
音やLEDの光でアラートを出す。

その2つの仕組みがあれば、仕事に集中していても気づくことができ、
すぐにエアコンをつけにいくことができるはずです。

実際につくった仕組み

自動でLINEに通知する仕組みは作れなかったので、
外出中の夫or妻がLINE botで温度を確認し、
快適温度から外れた時に、
自宅にいる夫or妻にLEDで通知をする仕組みを作りました。

<STEP1>外出先からLINE botで赤ちゃん部屋の温度を確認する

まずは、LINE botに「どう?」と問いかけます。

3221879_s.jpg

<STEP2>部屋の温度に応じて、LEDの反応を変える

①もし28度を超えていた場合は?

■LINEの通知

8913.jpg

■LEDの反応

②もし26度より下回っていた場合は?

■LINEの通知
※検証した日はなかなか26度を下回らなかったため、
コードの方の数字をいじって、強引に表示させてしまいました。

8914.jpg

■LEDの反応

③もし26度〜28度の間(快適温度)だった場合は?

■LINEの通知

8915.jpg

■LEDの反応

LEDが消えます。

IMG_7587.JPG

<STEP3>エアコンをつけるor消す。

IMG_1921.JPG

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つくってみました。

まだまだ狙ったものをつくるスキルはないですが、
少しずつ改良して、より良いものにしていきたい。

今後も自分にとって、誰かにとって必要なものを
色々作ってみたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 tag

parent 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 -> children

DOM 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.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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を持つ要素の位置情報を確実に取得し移動してくれるので万能そうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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を設定しなければならないようです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録:javascript】HTMLcollectionでのforEach

document.getElementsByClassNameで取得したクラスが回せなかった

hoge.js
let elem = document.getElementsByClassName('m-hoge');

上記のように特定のクラスを取得しそのテキストを手に入れたかったのですが、

hoge.js
let elem = document.getElementsByClassName('m-hoge');

elem.forEach(function(item) {
  console.log(item.innerHTML);
});

このようにforEachをしても取れませんでした。

HTMLcollectionは配列ではない

参考にさせていただいたページが分かりやすかったのですが、HTMLcollectionはそもそも配列ではないとのことです。
取得しているときは配列っぽいですが、配列でないため、配列で使用するメソッド(今回でいうとforEach)が使用できないのです。

対策

対策として、Array.prototypeを使用して対応することができました。

hoge.js
let elem = document.getElementsByClassName('m-hoge');

Array.prototype.forEach.call(elem, function(item) {
  console.log(item.innerHTML);
}

まとめ

分からないことがたくさんあるので、しっかり理解して進めて行きたいと思います。

参考ページ

https://hacknote.jp/archives/21892/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】ライブラリ化しておく便利な3つの自作共通関数を記録

前提

  • ※記載の共通関数は、各環境によって動作しない場合がある。
    →ES2015からES2019で利用できるメソッドも利用しているため

1. chunk

  • 指定の数に応じて、対象配列を分割する関数

コード

lib.js
const chunk = (arr = [], size = 3) => {
    return Array.from({
        length: Math.ceil(arr.length / size)
    }, (v, k) => arr.slice(k * size, k * size + size))
}

使い方

index.js
import { 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.js
const 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.js
import { 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.js
const filterObj = (target,condition) => {
  return Object.fromEntries(Object.entries(target).filter(condition))
}

使い方

index.js
import { 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処理で、再度オブジェクトへの変換処理を行う。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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

npmaudit1.png
audit は監査という意味
パッケージの依存関係に脆弱性がある箇所を教えてくれるコマンド
※ただし、npm v6以上

脆弱性の自動修正

脆弱な箇所の自動修正
npm audit fix
npm audit fix --force

npmaudit2.png
ある程度は自動修正してくれる
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の運用方法について

以上

間違った理解や記述があればご指摘いただけると幸いです

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript(jQuery)で動的に追加した要素をフェードアウトしてから削除する方法

jQueryで動的に追加した要素をフェードアウトしてから削除する方法

 JavaScriptで動的に追加したDOM要素は通常の方法ではイベントが起こらなくなる。
そこでdocumentを使ってHTML読み込み完了後にイベントを発動するよう設定することでイベントが起こる。
 また、queue()を使用することで、フェードアウトを実行してから要素を削除できる。
個人的にかなり躓いた部分なので記録に残します。

  $(document).on('click', 'div', function(){
    $('div').fadeOut(500).queue(function(){
      $(this).remove();
    });
  });

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.js
list = []

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.js
var 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:現在処理している配列

これらを覚えておくと、配列処理をさらにスマートに行えます!

最後に

ありふれた内容ですが、開発を行う際に引っかかったため自分用の備忘録として書かせていただきました。始めたばかりの初心者ですのでご指摘があればコメントお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Svelte入門 環境構築・フォルダ構成について

Svelte入門 環境構築・フォルダ構成に関する忘備録

1_YwhNJXizPs__GeqoivtbYg.png

Vue.jsの50倍早いとかでフロントエンド界隈で注目を浴びている?svelte.jsを少し触ってみたので忘備録としてまとめました。

環境構築手順

Vue.jsのようにCDNを張り付けるだけで試せるような手軽さはありません。
まず、Node.jsがインストールされている事が前提条件としてあり、npxコマンドでプロジェクトを作成する必要があります。

プロジェクトの生成

npxコマンドを実行して新規プロジェクトを作成します。

npx degit sveltejs/template my-project

npxコマンドはnode.jsをインストールしたら一緒についてくるらしいのですが、自分の環境では認識されませんでした。その場合は別途npxをインストールします。

$ npm install -g npx

プロジェクトフォルダに移動してnpm installを実行します。

cd my-project/
npm install

package.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 expose

Svelteの初期起動時の画面
hello_world.PNG

Svelteの環境構築は以上です。
HELLO WORLDを表示させるまでは約10分程度で完了します。

フォルダ構成の確認

次にフォルダ構成を確認してみます。
プロジェクト作成後のフォルダ構成は以下のようになっています。
srcフォルダ以下に、App.sveltemain.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.js
import 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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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! と表示されるはずです。

さらにコンソールでは次のような表示が確認できるはずです。
image.png

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;
  }

なお、基本的に上に書いたことと同じものですが、こちらの公式フォーラムでのやりとりも参考になると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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秒をnumber0と規定し、その既定値からの相対的な秒数をラップして、日時処理の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関数は何をするかというと、zxのようなタイムゾーン書式に指定されたタイムゾーンを出すことを行ってくれます。

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オブジェクトを日本時間の日時文字列にフォーマットする場合は、次の手順を踏みます:

  1. 一旦、date-fns-tzのutcToZonedTime関数で、UTCなDateオブジェクトを日本時間にずらしたDateオブジェクトに変換する。
  2. 変換後の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" ← 日本時間の表記
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【はじめてのIoT】サイリウムを自動的に光らせてみた【ラブライブ】                                                                                                      

はじめに

 「推しが複数いるとき、どの色にすればいいか悩む・・・」
 「サイリウムで盛り上げたいけど、もっとライブに集中したい」
 「夢中になりすぎて、サイリウムの色を変えるのを忘れてしまう」

ライブに行った時、このように思ったことはありませんか?
私はあります。

私はラブライバーなのですが、箱推し(みんな好き)です。
なので全色光らせたいけど、持つのは2本が限界だし・・・
「次はオレンジだ!」とか考えてるとライブに集中できなかったり・・・

ということで、今回は自動的に光るサイリウムを作ってみました!

理想イメージ

ライブと言っても、今はコロナの影響もあって、無観客ライブが増えてきました。
今回もYoutubeのライブをイメージして、曲が始まるとサイリウムが自動的に光ることをゴールにします!
Image from Gyazo
Youtube IFrame Player APIを使って、node.jsでYoutubeを操作できるようにします。
そしてobnizでサイリウムを繋ぎ、Youtubeの再生をトリガーにobnizが動いてサイリウムが光るようにします!

実際にできたもの

動画をご覧ください。

色々ツッコミどころはあるかと思いますが、
まず、サイリウムといえば、こんなイメージですよね。
image.png
すみません、イメージ通りのサイリウムをObnizに繋げるのはハードルが高すぎました。
image.png
ということで、フルカラーLED。小っちゃくても明るく光ります。

さすがにこれを振るには小さすぎるので、私の分身を作りました。
(工作がとても苦手です。)
image.png

見た目こそアレですが、色はちゃんと思い通りに光っています!!!
歌っているメンバーの色が光るようにしていて、
複数メンバーで歌ってる場合は、その子たちの色を交互に点灯させました。
この曲の見せ場!センター真姫ちゃんのソロでは、ばっちり赤く光っております・・・(´;ω;`)

動画ではうまくいったかのように見えるのですが、実は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にいる皆さん、あったかくて幸せです!

最後に、ことりちゃんから一言。
:hatched_chick:「ラブライバーのみんなぁ~!LGTMくれないと、ことりのおやつにしちゃうぞぉ~!!!」

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【はじめてのIoT】サイリウムを自動的に光らせてみた【ラブライブ】

はじめに

 「推しが複数いるとき、どの色にすればいいか悩む・・・」
 「サイリウムで盛り上げたいけど、もっとライブに集中したい」
 「夢中になりすぎて、サイリウムの色を変えるのを忘れてしまう」

ライブに行った時、このように思ったことはありませんか?
私はあります。

私はラブライバーなのですが、箱推し(みんな好き)です。
なので全色光らせたいけど、持つのは2本が限界だし・・・
「次はオレンジだ!」とか考えてるとライブに集中できなかったり・・・

ということで、今回は自動的に光るサイリウムを作ってみました!

理想イメージ

ライブと言っても、今はコロナの影響もあって、無観客ライブが増えてきました。
今回もYoutubeのライブをイメージして、曲が始まるとサイリウムが自動的に光ることをゴールにします!
Image from Gyazo
Youtube IFrame Player APIを使って、node.jsでYoutubeを操作できるようにします。
そしてobnizでサイリウムを繋ぎ、Youtubeの再生をトリガーにobnizが動いてサイリウムが光るようにします!

実際にできたもの

動画をご覧ください。

色々ツッコミどころはあるかと思いますが、
まず、サイリウムといえば、こんなイメージですよね。
image.png
すみません、イメージ通りのサイリウムをObnizに繋げるのはハードルが高すぎました。
image.png
ということで、フルカラーLED。小っちゃくても明るく光ります。

さすがにこれを振るには小さすぎるので、私の分身を作りました。
(工作がとても苦手です。)
image.png

見た目こそアレですが、色はちゃんと思い通りに光っています!!!
歌っているメンバーの色が光るようにしていて、
複数メンバーで歌ってる場合は、その子たちの色を交互に点灯させました。
この曲の見せ場!センター真姫ちゃんのソロでは、ばっちり赤く光っております・・・(´;ω;`)

動画ではうまくいったかのように見えるのですが、実は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にいる皆さん、あったかくて幸せです!

最後に、ことりちゃんから一言。
:hatched_chick:「ラブライバーのみんなぁ~!LGTMくれないと、ことりのおやつにしちゃうぞぉ~!!!」

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

好きなキー好きな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」といった表記で対応できます。

Screenshot_20200728-214218.png

obnizで再生

送った楽譜はnode.jsで周波数に変換されobnizに送られます。
画面に[play]が出ているところでスイッチを押すと再生できます。

キー選択・BPM選択

モードを[play][key][BPM]の3つ用意しました。

[key]を変えると曲の音程が変わります。

[BPM]を変えると曲の速さが変わります。

さあ自分好みの自分だけのパプリカを探せ!(大袈裟)

環境

node v12.18.2
Visual Studio Code 1.47.1
obniz

手順

linebotを作る

他記事を参考にlinebotを作成しました。
(参考:https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)
Screenshot_20200728-213845.png

linebotへ投稿した「ドレミ」の楽譜をMessaging API経由で送信し、Node.jsで実装した処理を経てobnizの圧電スピーカへ送るという手順で実装しました。
Node.jsは今回ngrokを使って接続しました。

「ドレミ」を周波数に変換するまで

「ドレミ」から周波数へ変換するにはどうするか。
オクターブ表記を含めると「ド」や「ドs」や「ド1」や「ドs1」といった様々なバリエーションに対応する必要があります。
そこで、3段階の表記変換を挟む方針にしました。
image.png
一つずつ見ていきますね。

①「ドレミ」から三文字表記へ

まずはドレミから三文字表記に直す処理を考えました。
三文字表記のお作法は
 一文字目:ドレミ
 二文字目:ナチュラル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側でできるようになったり、楽譜管理・保存とかできたら良いなあとか思ったりしています。

いつできるやらという感じですが、生暖かい目で見守っていただけると嬉しいです!
最後までご覧いただきありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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}回目の出力`)
}

今回は[①初期化式]; [②条件式]; [③加算式]が単純なものを学んだが、
今後は[①初期化式]; [②条件式]; [③加算式]が繰り返す処理の内容に絡んだ複雑なものもあるのではないかと予想する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【obniz×LINE Messaging API】音と光を使い侵入防止システムを製作する

目的

私の実家では、夏場に玄関を開放し風通しを良くしていると、野良猫が侵入し、食べ物が被害にあいます、そこで今回はobnizで侵入防止システムを製作したいと思います。(効果は保証できません)

できたもの

距離センサーの50㎝以内に入り5秒経過したら、LEDとスピーカーで警告し、システムが作動したことをLINE Messaging APIを使用してプッシュ通知します。映像ではわかりませんが、スピーカーから11khzの音を鳴らしてます。
Image from Gyazo

構成

全体像

今回製作したシステムの全体像です。
Image from Gyazo

配線図

obnizの配線図です。
Untitled Sketch_ブレッドボード.png

使用部品

環境

  • 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 #nodefest

Push通知の部分を参考にしました。
LINE BOTでラズパイから部屋の温度を教えてもらう【Push API】【Node.js】

失敗

LEDはフルカラーを使用し色を使い分ける予定でしたが、途中でショートさせて焼いてしまいました。ですので、今回は青色のLEDを使用しました。

終わりに

今回は距離センサを使ったので遮るものすべてにはんのうしてしまいます。
公式サイトでは、カメラで画像認識を使用している作例もあり、特定の対象物にのみ反応するようにAI機能を搭載できたらいいなと思いました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactでAPIを使用する際によく見る"fetch(URL).then"のメソッドチェーンについて

背景

Reactからjson形式のAPIを利用する方法として下記のような方法が挙げられている。 (Reactに限った書き方ではないが)

useApi.js
1 fetch('http://example.com/api/')
2   .then((response) => response.json())
3   .then((response) => {
4     console.log(response.hoge);
5   })

しかし、データ(response.hoge)のみを利用するのであれば、

useApi.js
2   .then((response) => response.json())

は不要なのでは?と思い、コメントアウトしたところ、

useApi.js
4     console.log(response.hoge);

がundefinedとなった。
2行目の有無で3行目の引数の中身が変わっているような挙動だ。

調べた結果

Promiseの.then(というより、アロー関数)には、下記の特徴があるようだ。

  • 単文の場合は、{}を省略可能。
  • {}を省略した場合は、単文の処理結果をPromiseでwrapしたもの(?)が自動でreturnされる。

この特徴をメソッドチェーンとして活用したのが冒頭のコードとなっている。

ちなみに、自分で

useApi_2.js
1 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.js
1 fetch('http://example.com/api/')
2   .then((response) => response.json())
3   .then((response) => {
4     console.log(response.hoge);
5   })

参考にさせていただいたサイト様

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.963s
2. Check compatible versions between react-native-community/cli & react-native

「react-native-community/cli」のGitHubページでReact Nativeとの互換関係を確認。
インストールされたのは4.10.1なので0.62.0が対応。
image.png

3. Create react-native-template-typescript project

React Native × Typescriptのテンプレートでプロジェクトを作成。
ちなみに、プロジェクト名には - (ハイフン)とか使えない文字があるので注意。

npx react-native init onlibrary_frontend --template react-native-template-typescript --version 0.62.0
4. Check Android SDK folder

Android Studioのインストール時に格納されたAndroid SDKのフォルダを確認。
image.png

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\\Sdk
6. Launch Android Virtual Device

Android Studioのインストール時に同梱されたAndroid Virtual Deviceを作成して起動。
作成する仮想モバイルデバイスは任意。
念のため、起動前にデータを削除。
image.png

7. Check Android Virtual Device ID

起動された仮想モバイルデバイスのIDを確認。

$ adb devices
List of devices attached
emulator-5554   device
8. 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

成功なら、デフォルト画面が表示。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む