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

Promise Test

function sampleResolve(value) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(value);
        }, 1000);
    })
}

function sample() {
    let result = 0;

    return sampleResolve(5)
        .then(val => {
            console.log(result, val)
            result += val;
            return sampleResolve(10);
        })
        .then(val => {
            console.log(result, val)
            result *= val;
            return sampleResolve(20);
        })
        .then(val => {
            console.log(result, val)
            result += val;
            return result;
        });
}

sample().then((v) => {
    console.log(v); // => 70
});
0 5
5 10
50 20
70

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

Chrome拡張でJSONファイルを手軽に読み込んでみよう!

以下のような構造のChrome拡張のファイルがあったとします。

 my-extension
 ├── src
 │   ├── config.json
 │   └── main.js
 └── manifest.json

では、main.jsからconfig.jsonを読み込んでみましょう。なに、難しいことは考えず、以下の関数をコピペしてください。

コード

main.js
function getJSON(filename) {
    return new Promise(function(r) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', chrome.extension.getURL(filename), true);
        xhr.onreadystatechange = function() {
            if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
                r(xhr.responseText);
            }
        };
        xhr.send();
    });
}

getAssets('src/config.json').then(function(r) {
    //JSONファイルを読み込んだ後の処理
    var config = JSON.parse(r);
    console.log(config);
})

チャンチャン。

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

【Vue】配列の追加・削除には注意が必要?

【Vue】オブジェクト追加・削除には注意が必要? の配列版です。
オブジェクトと同じく、参照するだけなら普通のJSと同だが、要素の追加・削除で嵌るポイントがあったので備忘録として。
↓の記事も参考に。

次のようなVueインスタンスのdata()charasという空の配列がセットしてあるとする。

export default {
  data () {
    return {
      charas: []
    }
  }
}

要素を追加する

オブジェクトでは$setを使ったが、追加するときは末尾であれば通常通りpush()でOKらしい。先頭に追加などは試していないのでわからず。

// 通常通り、`push`を使えばOK
this.charas.push('範馬刃牙')

要素をまとめて追加したい

スプレッド演算子...を使って配列を展開した要素をpush()で追加すればOK。

// まとめて要素を追加する
this.charas.push(...['範馬勇次郎', '列海王'])

要素を変更する

要素を変更したい時、いつも通りインデックスを使ってしまいたくなるがこれだとリアクティブにデータが反映されない(=データ更新してもテンプレート(画面)上に反映されない)。

splice()を使う。

構文は<配列>.splice(<始まりのインデックス>, <変更する要素数>, <変更後の要素>)と書くとのこと。
(書き方をいつも忘れてしまう)

見ての通り複数個まとめて変更することもできる。

// リアクティブにデータが反映されないのでこの書き方はダメ
this.charas[0] = '範馬勇一郎'
// これだとリアクティブになる
this.charas.splice(0, 1, '範馬刃牙')

要素を削除する

変更のときと一緒でsplice()を使う。

書き方は<配列>.splice(<始まりのインデックス>, <削除する要素数>)
見ての通り複数の要素をまとめて削除できる。

ちなみに戻り値は削除した要素を含む配列で破壊的メソッドとして働く。

// リアクティブにデータを削除
this.charas.splice(0, 2)

全削除したい

通常通り、arr.length = 0のイディオムを使いたくなるが、これだと配列の長さを変える操作なのでいけないみたい。
(一見、動いているようにみえるが、Vue作者もそう言っているので使うのはやめた方がよさそう)

// これは本当はダメ
this.charas.length = 0

要素をまとめて削除できるsplice()を使う。第二引数に配列の長さを渡せば全削除できる。

// これで全削除できる。
this.charas.splice(0, this.charas.length)  // => []

要素を全て入れ替えたい

配列を一度空にしてからまとめて追加すればよい

// 一旦、全削除して要素を入れ替える
this.charas.splice(0, this.charas.length)
this.charas.push(...['範馬刃牙', '範馬勇次郎', '列海王'])

配列自体を削除したい

あまり使うケースは無さそうだが、要素ではなく配列自体を削除したい場合はどうするか?

この場合はオブジェクトと同じでnullを入れればよいみたい。

// charasを削除する
this.charas= null
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【GAS×LINEmessagingAPI】秘書BOTを作ってみた。JavaScriptのアウトプット

はじめに

この記事はプログラミング未経験だった僕が、プログラミングを学び、アウトプットとして作ったLINEBOTのお話です。

初めての投稿なので、改善点やアドバイス、感想などあればコメントお願いします

目次

1.きっかけ
2.つくるもの
3.使う技術
4.作り方

1.きっかけ

コロナウイルスの影響でオンライン授業が増え、就活も始まり、増えたタスクをもっとシンプルに管理したいと思ったからです。

Inteeリアルカレッジ中級編のポートフォリオとして作成しました!
(リアルカレッジについての記事もまた書きます!)

2.つくるもの

・毎朝自動でスケジュールを通知してくれるLINE BOT
・Twitterの積み上げ投稿に生かせる型
・スプレッドシートで管理可能
・秘書として人間味を感じること!!
・作った後、毎日使いたくなるほど便利なこと!

3.使う技術

主にこの3つを使いました!

・LINE messaging API
・Googleスプレッドシート
・gas

次に具体的な作り方について紹介します!

4.作り方

4-1.LINE developersの登録

まずは「LINE messaging API 利用」と検索し、LINE developersのページを開いてください。

ここで、自分のLINEアカウントを使いログインしましょう!

そして、開発者登録、新規プロバイダー作成、チャネル作成を説明に沿って済ませてください。

チャネルの作成をしました。
すると、「チャネル基本設定」画面が開けます。
そこで以下の情報の設定とメモをお願いします!

〇アプリ名:LINEを友達追加したときの名前です。

〇アクセストークン:最初はないので、再発行してメモしましょう。4-3で使います。

〇webhook送信:オンにする

〇webhook URL:後でgasで得たURLをコピペします。

〇自動応答メッセージ:利用しない

〇友達追加時挨拶:利用しない

〇QRコード:友達追加するときに使います

〇Your user id:コーディング時にuser-idのところに書きます。要メモ。

抑えるべきは以上です!!次に進みましょう!

4-2.gasを使う準備

そもそもgasとは、Googleスプレッドシートシートで使えるプログラミング言語みたいなものです!(ExcelのVBAみたいな感じ...)

JavaScriptで書けるので、JavaScriptの練習やアウトプットにもなります!

まずは、Googleスプレッドシートのページを検索し、Googleアカウントでログインしてください。

スプレッドシートの編集画面を開きます。
以下のように名前を付けスケジュール表を作ってください。

みさ子サンプル.jpg

次に、スプレッドシートのメニューバーから
「ツール」⇒「スクリプトエディタ」を選んでください。
下のようなスクリプト編集画面が表示されたら、好きな題名をつけてください。

エディタ.jpg

次からいよいよコードを書きます!!!!

4-3.gasでコーディング!!

まずコードの全文を以下に示します。
アクセストークン と user-id の部分には4-1でメモしたものを使ってください。

misako.gs
var CHANNEL_ACCESS_TOKEN = 'アクセストークン'; 
var USER_ID = 'user-id';
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var today = Utilities.formatDate(new Date(), "JST", "yyyy/MM/dd");
var TEXT_MESSAGE = '本日の予定はありません';

for(var i = 1; i <= lastRow; i++) {
 if(today == Utilities.formatDate(sheet.getRange(i, 1).getValue(), "Asia/Tokyo", "yyyy/MM/dd")
    && !sheet.getRange(i, 2).getValue() == '') {
     TEXT_MESSAGE = "秘書報告\nおはようございます!\n#今日の積み上げ予定\n"
     + sheet.getRange(i, 2).getValue() 
     +"\n今日も1日頑張ってください!!\n by 秘書みさ子☆"
 }
} 
function pushMessage() {
 var postData = {
   "to": USER_ID,
   "messages": [{
     "type": "text",
     "text": TEXT_MESSAGE,
   }]
 };

 var url = "https://api.line.me/v2/bot/message/push";
 var headers = {
   "Content-Type": "application/json",
   'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
 };

 var options = {
   "method": "post",
   "headers": headers,
   "payload": JSON.stringify(postData)
 };
 var response = UrlFetchApp.fetch(url, options);
}

それではコードの意味や注意することについて順に説明していきます。

misako.gs
var CHANNEL_ACCESS_TOKEN = 'アクセストークン'; 
var USER_ID = 'user-id';
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var today = Utilities.formatDate(new Date(), "JST", "yyyy/MM/dd");
var TEXT_MESSAGE = '本日の予定はありません';

ここではメッセージ送信に必要なデータを入れる変数を定義しています。
アクセストークンとuser-id によってgasとLINEBotが繋がります。
他も簡単に説明します

【sheet】:スプレッドシートを取得します
【lastRow】:文字が入力されている範囲を確認します
【today】:日本時間で本日の日付を取得します
【TEXT_MESSAGE】:LINEに送るメッセージを入れます(デフォルトは予定がない状態)

misako.gs
for(var i = 1; i <= lastRow; i++) {
 if(today == Utilities.formatDate(sheet.getRange(i, 1).getValue(), "Asia/Tokyo", "yyyy/MM/dd")
    && !sheet.getRange(i, 2).getValue() == '') {
     TEXT_MESSAGE = "秘書報告\nおはようございます!\n#今日の積み上げ予定\n"
     + sheet.getRange(i, 2).getValue() 
     +"\n今日も1日頑張ってください!!\n by 秘書みさ子☆"
 }
} 

ここでは、本日の日付と予定があるのかを確認します。
そしてあった場合、そこの内容を取得し、文章の型に埋め込み、"TEXT_MESSAGE" に代入します。

処理としては簡単で、以下の流れです。

スプレッドシートの日付の列をforループで1行ずつ読み込みます。
if文で本日の日付と一致するかどうかを判定します。
if文でその日付のセルに予定が入力されているかを判別します。

スケジュールが入力されていない場合TEXT_MESSAGEに値をセットせずデフォルトで出力します。

misako.gs
function pushMessage() {
 var postData = {
   "to": USER_ID,
   "messages": [{
     "type": "text",
     "text": TEXT_MESSAGE,
   }]
 };

この関数によって、先ほどの処理が実行され、TEXT_MESSAGEがセットされます。

misako.gs
var url = "https://api.line.me/v2/bot/message/push";
 var headers = {
   "Content-Type": "application/json",
   'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
 };

LINE APIのURLとアクセストークンをセットします。

misako.gs
var options = {
   "method": "post",
   "headers": headers,
   "payload": JSON.stringify(postData)
 };
 var response = UrlFetchApp.fetch(url, options);
}

LINE APIで送信します。

コーディングが終わったら、忘れずに保存しましょう!

4-4.トリガーで自動化

gasにはトリガーという便利な機能があります。
条件を設定しておけば、自動で実行してくれます。

今回は時刻の条件を設定し、毎日その時刻に自動送信される設定にしましょう!

メニューから「編集」⇒「現在のプロジェクトのトリガー」を選びます。
管理画面が表示されたら、「トリガーを追加」をクリックします。
下の動画のように設定画面が表示されます。
(URLをクリックしてもらえると、動画が見れます!)

動画と同じように設定すると、毎日、朝の5時から6時に、その日のスケジュールを自動通知してくれます!!

4-5.友達追加してみよう!

LINEdevelopersのチャネル基本設定画面からQRコードを読み込んでみましょう!!
友達追加ができるはずです!!

もしすぐに使ってみたい場合は、先ほどのトリガー設定の時刻を、すぐあとの時刻に設定すれば動作のテストができます!!

アカウントの画像や、通知の時のコメントなど自分好みにカスタマイズして、オリジナルの秘書botを作ってみてください!!

さいごに

ここまで記事を読んでくださりありがとうございました!!
初投稿で至らない部分が多かったと思います(-_-;)

実際に作ってみて、やはりインプットだけでなくアウトプットすることがとても大切だと感じました。今後もプロダクトや記事投稿、情報発信も頑張っていければと思っています!!

ここで紹介をさせていただきます。
僕がプログラミングをはじめ、実際にプロダクトを作れるようになったり、他の学生とつながりを作り、ハッカソンに出るようになれたのは、inteeリアルカレッジの存在があったからです。

チャレンジしたい人、力をつけたい人にはかなりおススメです。
次々に目標が生まれるような、とても刺激的な環境です!!

学生限定のサービスにはなるけれど、たくさん成長できるとてもいい環境です!気になった方はぜひ参加してみてください!

僕のTwitterに活動や、学んだことなど投稿しているので、もしよければフォローお願いします!!

また今回の記事がいいな!と思っていただけたら、Twitterなどで共有とLGTM!お願いします!!ありがとうございました!!

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

ウソ穴 Ver 5 Type A / GStreamer

はじめに

個人開発ウソ穴の作り方を紹介します。

ウソ穴とは

ウソ穴は、ライブ映像 or 動画とARを組み合わせて、壁に穴が空いた錯覚を作り出します。

Webサイトなので、ユーザーはアプリのインストール無くウソ穴を使用できます。

ウソ穴 Ver 5 Type A / GStreamer

今回は、GStreamer によるライブ配信と組み合わせる ウソ穴 Ver 5 Type A を紹介します。

  • Webサイトは、Windows10環境のブラウザ Chrome, FireFox で動作を確認
  • iPhone,Androidでは正常に動作せず

構成図

image.png

※ウソ穴 Ver 5 Type A は、iPhone, Android 非対応

デモ映像

line.png

ウソ穴の構築方法

ここから、ウソ穴 Ver 5 Type Aの構築方法を紹介します。

  • 構築概要
    • ラズパイにカメラモジュールを接続
    • ラズパイに、ウソ穴の構成要素を集約
    • GStramerで、カメラモジュールの映像をストリーミング
    • Nodejsで、HTTPS対応のWebサーバーを構築
    • A-FrameでWebサイトでAR機能を実装

ラズパイのセットアップ

ラズパイOSのインストールとセットアップで使っているリンク

ストリーミング

GStreamerインストールで使っているリンク

HTTPS対応のWebサイト

Nodejsインストールと、オレオレ証明書でHTTPS対応

Webサイト(ウソ穴 Ver 5 Type A / GStreamer)の準備

以下のソースをHTTPS対応のWebサイトにコピーします。

画像データが不足しているので、追加します。
parts\img\tex001.jpg
parts\img\invisible.png ※透過率100%の完全透明な画像ファイル

tex001.jpgのダウンロード
invisible.pngのダウンロード
invisible.png

これで、ウソ穴 Ver 5 Type A / GStreamerの準備ができました。

ファイル構造はこのようになります。

$ pwd
/home/pi/nodejs/01/wwwroot/usoana5/usoana5TypeA-GStreamer
$ tree
.
├── parts
│   ├── gstreamer
│   │   ├── output.m3u8
│   │   ├── segment00031.ts <--ストリーミング開始で追加される
│   │   ├── segment00032.ts <--ストリーミング開始で追加される
│   ├── img
│   │   ├── invisible.png
│   │   └── tex001.jpg
│   └── js
│       └── hls-load-texture
│           └── hls.js@latest
└── usoanaVer5TypeA.html

line.png

ウソ穴 Ver 5 Type A / GStreamer 動かす

ストリーミングを開始する

parts\gstreamer をカレントディレクトリにし、以下コマンドを実行しストリーミングを開始します。

sudo gst-launch-1.0 -v -e v4l2src device=/dev/video0 \
 ! video/x-h264,width=480,height=480,framerate=15/1 \
 ! h264parse ! mpegtsmux \
 ! hlssink max-files=8 target-duration=5 \
 location=./segment%05d.ts \
 playlist-location=output.m3u8 \
 playlist-root=./

ストリーミングを開始すると、parts/gstreamer配下に、output.m3u8とsegmentXXXXX.tsファイルが作成されます。

$ pwd
/home/pi/nodejs/01/wwwroot/usoana5/usoana5TypeA-GStreamer/parts/gstreamer
$ ls
output.m3u8      segment00032.ts  segment00034.ts  segment00036.ts  segment00038.ts
segment00031.ts  segment00033.ts  segment00035.ts  segment00037.ts

Webサービスを開始する

以下コマンドでWebサービスを開始します。ポート3000はhttp通信(非暗号化通信)で、ポート3001はhttps通信(暗号化通信)です。ウソ穴は、https通信が必須なので3001番ポートを使います。

$ pwd
/home/pi/nodejs/01
$ node app.js
        サーバがポート3000で起動しました。モード:development
        サーバがポート3001で起動しました。モード:development

ブラウザでWebサイトを開く

ブラウザで以下のURLを指定すると、ウソ穴が動きます。

https://{ラズパイのIPアドレス}:3001/.../usoanaVer5TypeA.html

usoanaVer5TypeA.htmlファイルが以下パスの場合、URLは、https://{ラズパイのIPアドレス}:3001/usoana5/usoana5TypeA-GStreamer/usoanaVer5TypeA.htmlになります。

$ pwd
/home/pi/nodejs/01/wwwroot/usoana5/usoana5TypeA-GStreamer
$ ls
parts  usoanaVer5TypeA.html

line.png

今回はこれでおわり、iPhone対応版とか、動作再生版とか、別バージョンのウソ穴も公開する予定です。

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

Vue.js/Nuxt.jsでパララックス効果を実装

Vue/Nuxtでパララックスしようぜ!

※パララックス効果とは:スクロールしたときに、スクロール量に合わせてずれたり動いたりするやつ。

こういうやつ
sukiyaki2.gif

Vue.jsプラグインもいくつかあるけど、自前で実装した方が自由度高くて楽だった。勉強にもなるし。

実装方法概要

実装方法の大まかな説明

  • scrollイベントをlisten
  • スクロール量やら色々パラメータを取って移動量を計算
  • imgのstyleに object-position: {移動量X}% {移動量Y}% をぶち込む
index.vue
<template>
  <img
    ref="backgroundImg"
    class="background-img"
    src="~/assets/img/background.png"
    :style="`object-position: 50% ${objPosY}%;`"
  >
</template>

<script>
export default {
  data() {
    return {
      objPosY: 0
    }
  },
  mounted(){
    window.addEventListener('scroll', this.calculateScrollY) // スクロールイベントのlisten
  },
  methods: {
    calculateScrollY() {
      // this.objPosY に計算した移動量をぶち込む
    }
  }
}

以上の実装方法ですが、場合によって移動量の計算方法が異なります。

  1. img要素が完全に隠れている場合。(スクロールしないとimg要素が現れない場合)
  2. img要素が最初から見えている場合。(スクロールしなくてもimg要素が少しでも見えていて、これからスクロールする場合。)

1. img要素が完全に隠れている場合

この場合、画像の移動量は、以下の通りである。

  • スクロールして要素が見え始めた時は、移動量が0%である。
    • = ブラウザの表示領域のbottomが、img要素のtop に到達した時
  • スクロールして要素が見えなくなった時は、移動量が100%である。
    • = ブラウザの表示領域のtopが、img要素のbottom に到達した時

以上のように、this.objPosY がスクロール量に合わせて0から100まで動けばよい。

image.png

1.1 要素が見え始めた時にスクロール量が0となるやつを作る

getBoundingClientRect() を使ってスクロール量を取得し、新たに変数 topVisibleScrollY を定義した。

const rect = this.$refs.backgroundImg.getBoundingClientRect();
console.log(rect.top) // ブラウザの表示領域を基準とする、img要素の絶対座標(top) スクロールによって可変

const innerHeight = window.innerHeight // ブラウザの表示領域の高さ。

const topVisibleScrollY = -rect.top + innerHeight // 要素が見え始めた時にスクロール量が0。スクロールによって可変
console.log(topVisibleScrollY)

image.png

getBoundingClientRect().top は、ブラウザの表示領域を基準とする、img要素の絶対座標である。
つまり、ブラウザの表示領域のtopimg要素のtopが重なった時に0になる。
「img要素のtop」を「img要素のbottom」に変えるために、img要素の高さを引いてやれば良い。

ただ、getBoundingClientRect().top は、下へスクロールするごとに値が小さくなっていくので、マイナスにして正負反転させていることに注意。こうすることで、imgが画面内に現れた後、下へのスクロールによって値は正の方へ増えていく。

これで、「スクロールして要素が見え始めた時は、移動量が0%である。」の条件がクリアできたので、今度は「じゃぁtopVisibleScrollYが何pxのときに100%とみなせばいいのか」を求めていきます。

1.2 topVisibleScrollYが何pxのときに100%とみなせばいい?

要素が見えなくなった時に topVisibleScrollY が何pxかがわかれば良い。
要素が見え始めた時を0としたスクロール量がtopVisibleScrollYだから、計算は簡単。

答えは、img要素の高さ表示領域の高さを足したやつになる。

// img要素の高さ
const height = this.$refs.backgroundImg.clientHeight

// `topVisibleScrollY`が何pxのときに100%とみなせばいいか
const bottomVisibleEndY = height + innerHeight 

image.png

1.3 要素が見えなくなった時に、移動量が100%となるやつを作る

topVisibleScrollYbottomVisibleEndY に達した時に100になるようにすればいい。

this.objPosY = topVisibleScrollY / bottomVisibleEndY * 100 // 見え始めた時は0、見えなくなった時100

topVisibleScrollYbottomVisibleEndY も単位がピクセル量なのでパーセントに変換。
おしまい!

1.4 全体のコード

index.vue
<template>
  <img
    ref="backgroundImg"
    class="background-img"
    src="~/assets/img/background.png"
    :style="`object-position: 50% ${objPosY}%;`"
  >
</template>

<script>
export default {
  data() {
    return {
      objPosY: 0
    }
  },
  mounted(){
    window.addEventListener('scroll', this.calculateScrollY) // removeEventListenerは別で書きましょう。
  },
  methods: {
    calculateScrollY() {
      const rect = this.$refs.backgroundImg.getBoundingClientRect();
      const innerHeight = window.innerHeight // 表示領域の高さ
      const topVisibleScrollY = -rect.top + innerHeight // 要素が見え始めた時にスクロール量が0。スクロールによって可変。

      const height = this.$refs.backgroundImg.clientHeight // img要素の高さ
      const bottomVisibleEndY = height + innerHeight // topVisibleScrollYが何pxのときに100%とみなせばいいか

      this.objPosY = topVisibleScrollY / bottomVisibleEndY * 100 // 見え始めた時は0、見えなくなった時100
    }
  }
}
</script>
<style>
.background-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  overflow: hidden;
}
</style>

2. img要素が最初から見えている場合

この場合、imgが上側へ隠れることがないので、これまでの実装方法だと移動量が0%になることがない。スクロールしてない状態でも、40%とか30%とかになってしまう。

スクロールしてない状態なら0%にしたい。ということで以下に変えれば解決。

  calculateScrollY() {
    const scrollY = window.scrollTop;
    const rect = this.$refs.backgroundImg.getBoundingClientRect();
    const height = this.$refs.backgroundImg.clientHeight
    const bottomVisibleEndY = height + rect.top + scrollY
    this.objPosY = scrollY / bottomVisibleEndY * 100
  }

image.png

スクロール量については、途中から0で始める必要がなくなったので、rect.topをやめて window.scrollTop に変更。
bottomVisibleEndY(scrollTopが何pxのときに100%とみなせばいいか) はimg要素の高さimg要素のY座標を足し合わせればOK。
img要素のY座標rect.top + scrollY で求められます。

事前知識 : CSS object-position について

今回は CSS の object-position を使って画像をずらしていたのでその説明。
参考:https://developer.mozilla.org/ja/docs/Web/CSS/object-position

画像の width と height を指定した上で、画像自体は cover にしたい時、よく使われるのは以下のようなコード。
backgroundに画像を指定する方法である。

.hoge-img {
  /* img要素ではなくdiv要素とかにつける */
  width: 100px;
  height: 100px;
  background: url('image.png') no-repeat;
  background-position: 50% 50%;  /* center; でも可 */
  background-size: cover;
}

image.png

ただこの方法は、CSSに画像のパスを配置する必要がある。なので、パスが動的に変わるような場合は、imgタグを使いたいときもある。
その際に登場するのが、object-fitobject-position

img.hoge-img {
  /* img要素につける */
  width: 100px;
  height: 100px;
  object-fit: cover;
  object-position: 50% 50%;  /* center; でも可 */
  overflow: hidden;
}

これでimg要素でも自由なサイズにクリッピングできる。

今回はobject-positionを使ったが、background-positionを使っても同様にパララックスできる。
こうすればいいだけ↓↓↓

index.vue
<template>
  <div
    ref="backgroundImg"
    class="background-img"
    :style="`background-position: 50% ${objPosY}%;`"
  >
</template>
...

ついでに:addEventListenerについて

mixins.jsをpluginsフォルダにつくって、nuxt.config.jsにplugins: plugins: ['~/plugins/mixins'],とやる。
これでEventListenerを使う時は、this.listen(window, 'scroll', this.calculateScrollY) とするだけでよい。

~/plugins/mixins.js
import Vue from 'vue'

Vue.mixin({
  destroyed() {
    if (this._eventRemovers) {
      this._eventRemovers.forEach(function(eventRemover) {
        eventRemover.remove()
      })
    }
  },
  methods: {
    listen(target, eventType, callback) {
      if (!this._eventRemovers) {
        this._eventRemovers = []
      }
      target.addEventListener(eventType, callback)
      this._eventRemovers.push({
        remove() {
          target.removeEventListener(eventType, callback)
        }
      })
    }
  }
})

remove()をいちいち書かなくて良いの天才すぎる。以下から参考にさせていただきました。(多分)

PS : Twitterで「パララックス、SafariとiOSで動かなかったはずでは?」って言われたのでビビってます。とりあえずSafariでは動作確認できました。

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

Node.jsで作る簡単なWebアプリケーション

今回はNode.jsで作成する簡単なアプリケーションの作り方を解説していきます。
※nodeとnpmはインストール済みであることを前提としてます。

プロジェクト作成

まず、プロジェクトを作成していきます。
今回はsampleという名前のプロジェクトを作成します。

package.json作成

vacode上で先ほど作成したフォルダを開き、
ターミナルで下記のコマンドを実行し、package.json を作成していきます。

npm init

このとき、プロジェクト名などを聞かれまずが、
entrypointのみ"app.js"に変えましょう。

必要となるパッケージをインストール

必要となるパッケージをインストールしていきます。

今回使うのは下記の3つのパッケージです。
・express
・ejs
・bootstrap

それではインストールしていきましょう。

インストールは以下のコマンドで実行できます。
npm install express ejs bootstrap --save

インストールが完了すれば準備OKです。

必要なディレクトリを準備

次に、必要なディレクトリやファイルを用意します。
スクリーンショット 2020-07-11 18.01.07.png

このような状態にしましょう。

bootstrapの中身については、
node_module/bootstrap/distの中をコピーしてください

app.jsの中身を記述する

まず、必要最低限の中身を書いていきます。

app.js
var express = require('express');
var app = express();

app.listen(3000);

続いて、テンプレートエンジンの読み込みを行います。

app.js
var express = require('express');
var app = express();

app.set("view engine", "ejs" );//テンプレートエンジンの読み込み

app.listen(3000);

次に、静的ファイルの配信を行います。

app.js
var express = require('express');
var app = express();

app.set("view engine", "ejs" );//テンプレートエンジンの読み込み

app.use('/public', express.static(__dirname + '/public'));//静的ファイルの読み込み

app.listen(3000);

最後に、ルーティングを記述したファイルを呼び出します。

app.js
var express = require('express');
var app = express();

app.set("view engine", "ejs" );//テンプレートエンジンの読み込み

app.use('/public', express.static(__dirname + '/public'));//静的ファイルの読み込み

app.use('/', require('./routes/index.js'));//ルートにアクセスしてきたときのルートファイル

app.listen(3000); 

これで、app.jsの中身はOKです。

ルーターを作成

次に、routes/index.jsの中身を書いていきます。

routes/index.js
var router = require('express').Router();

router.get('/', (req, res) => { //getでルートにアクセスしてきたときの処理
  res.render("index.ejs");
});

module.exports = router;

Viewを作成

最後にアクセスした際に表示されるページのViewを整えます

views/index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="/public/third_party/bootstrap/css/bootstrap.min.css" />
</head>
<body>
  <div class= "container">
    <h1>サンプル</h1>
    <P>Node.js + Expressで簡単なWebアプリケーションができました</P>
  </div>
</body>
</html>

※注意点
・bootstrapを使用するので"container"を忘れずに
・拡張子はhtmlではなくejs

これで全ての準備が整いましたので、以下のコマンドを実行して確認してみましょう。

node app.js

ターミナル上では待機状態になっているはずなので、Chromeを立ち上げてlocalhost:3000にアクセスしてみましょう。

スクリーンショット 2020-07-11 18.23.04.png

うまく表示されたのではないでしょうか。

以上になります。

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

引数について(100 days of code)

はじめに

100 days of codeを始めて7日目。
今日は引数について復習しました。

引数とは?

引数とは、関数を実行するときに関数に渡す値を指します。
他にも引数の特徴として、値や変数をセットすることができます。
引数を受け取る値の個数は関数によって変わってきます。
alertは引数を1つだけ受け取れる。
console.logであれば引数を複数受け取れる。(何個でもOK)

index.js
const hello = function (name, age) {
  console.log(name + 'さんは' + age + '歳です。');
}
hello(Yuki, 27);

// 出力結果
"Yukiさんは27歳です。"

終わりに

引数を用いたコールバック関数はJavaScriptをやってて避けて通れないので、簡単に復習しました。
今日学習した引数は初歩的な部分なので、実践で使えるようにできればと思います。
明日も頑張ります。

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

Vuexでstoreのstateを初期化する

はじめに

Vuexを使用しているとき、ログアウト処理などでstoreのstateを初期状態に戻したいことがあると思います。いくつかやり方はあると思うのですが、私が手軽だと思った情報を記載します。

方法

こんな感じです。実際に利用する際はactionsなどから呼び出すことになると思いますが、ここでは最低限のコードのみ記載しています。
こちらを参考にしました。参考というよりそのまま持ってきただけですが。。

store/sample.js
// stateの初期値としたい任意のデータを定義する
function getDefaultState() {
  return {
    idToken: null,
    uid: null
  }
}

// stateを初期化する
export const state = getDefaultState()

export const mutations = {
  // stateを初期化するmutationを定義
  clearAuthData(state) {
    Object.assign(state, getDefaultState())
  }
}

おわり

とくに問題ないと思っていますが、何かご指摘あれば教えてくださいm(__)m

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

ReduxのReducerでのState操作の基本

目次

概要

この記事では、Reduxを使用したときのStoreにあるStateをReducerで操作する際の基本として、主にCRUDアプリケーションをはじめとした非常に多くの場面で利用される「追加、更新、削除」についてまとめています。
ReducerでのState操作の方法が分からなくなった時にこの記事を参考にしていただけるようであれば幸いです。
なおこの記事では、Reactを使用していることを前提としています。

前提知識

  • Reduxの基本的な知識を身につけている(Reduxの基本についてはこちらの記事にまとめています)
  • JavaScript(ES2015以降)で使用できる構文をある程度知っている

環境

導入ライブラリ version
React 16.13.1
react-redux 7.2.0
redux 4.0.5
lodash 4.17.15

ReducerでState操作

State操作の掟

Reduxで扱うStateはすべて、Storeというところに格納されています。
そしてそのStoreに格納されているStateは直接変更してはいけません。(イミュータブル)
ではStateを変更したい場合にはどのようにStateを変更すればいいのか以下に操作例をまとめます。

Stateの操作例

配列のState操作

悪い例

// Reducer
export default (state=[], action) => {
  switch(action.type){
    // 追加する場合
    case 'ADD':
      return state.push('hi');
    // 更新する場合
    case 'REPLACE':
      return state[0] = 'bye';
    // 削除する場合
    case 'REMOVE':
      return state.pop();
    default:
      return state;
  };
};

この場合、エラーとなるかあるいは正しくStateが変更されません。

正しい例

// Reducer
export default (state=[], action) => {
  switch(action.type){
    // 追加する場合
    case 'ADD':
      return [...state, 'hi'];
    // 更新する場合
    case 'REPLACE':
      return state.map(element => element === 'hi' ? 'bye' : element);
    // 削除する場合
    case 'REMOVE':
      return state.filter(element => element !== 'hi');
    default:
      return state;
  };
};

このように書くことで配列のStateを正しく変更することができます。

オブジェクトのState操作

悪い例

// Reducer
export default (state={}, action) => {
  switch(action.type){
    // 追加する場合
    case 'ADD':
      return state.age = '30';
    // 更新する場合
    case 'UPDATE':
      return state.name = 'Sum';
    // 削除する場合
    case 'REMOVE':
      return delete state.name;
    default:
      return state;
  };
};

この場合、配列のときと同様にエラーとなるか正しくStateが変更されません。

正しい例

// Reducer
import _ from 'lodash';

export default (state={}, action) => {
  switch(action.type){
    // 追加する場合
    case 'ADD':
      return {...state, age: 30};
    // 更新する場合
    case 'UPDATE':
      return {...state, name: 'Sum'};
    // 削除する場合
    case 'REMOVE':
      return {...state, age: undefined};
      // または
      return _.omit(state, 'age');  // lodashを使用
    default:
      return state;
  };
};

オブジェクト構造のStateの操作はこのように記述します。

配列やオブジェクトの操作で...stateといったようにスプレッド構文を使用していることに注目しましょう。これは現在あるStateを変更するのではなく、新たに現在のStateをコピーを生成して、その中で変更を行いそれを新たにStateとしてStoreに格納していることになります。

ReduxにおけるStateのようにオブジェクトや配列などをイミュータブルに扱いたい場合にスプレッド構文が活躍します。

オブジェクトのキー補間構文

一部ではKey interpellation syntaxと呼ばれていました。
これは一体なんなのかというと、コードを見ていただいた方が早いと思うので以下で紹介します。

const countryCapital = { japan: 'Tokyo', france: 'Paris' };
const country = 'china';
const capital = 'Beijing';

{...countryCapital, [country]:capital}
// -> { japan: 'Tokyo', france: 'Paris', china: 'Beijing' }

ここで注目してもらいたいのが、{...countryCapital, [country]:capital}です。{...countryCapital}は先程説明したスプレッド構文ですが、その後に続く[country]:capitalの部分が見慣れない構文なのではないかと思います。この[country]配列ではありません。今回の場合ですと、オブジェクトのキーとしてcountryを渡していることになります。すなわち[country]:capital === china:'Beijing'ということになります。
この構文を知っていれば、ReducerのState操作を便利にしてくれるはずなので、覚えておきましょう。

まとめ

今回はReduxでのState操作の方法についてまとめました。
操作方法の例を紹介しましたが、前提として最も重要なことはStateは直接操作せずイミュータブルに扱うということです。
そのための手段としてスプレッド構文やlodashライブラリ、キー補間構文などがあるということを抑えておきましょう。

参考資料

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

jsで配列を分解した全パターンを取得する

やりたいこと

わかりやすく言葉で説明できないので、完成イメージをかきます!

console.log(allSubArray([1,2,3]);
/* 
[
[[1], [2], [3]], 
[[1, 2], [3]],
[[1], [2, 3]],
[[1, 2, 3]]
] 

(順番の入れ替え([[1, 3], [2]]など)は考慮しません)
*/

何に使うの?とか言わないでください :)

考え方

再帰使えるかな・・・

const allSubArray(array) => {
     // 再帰終了条件
    if(array.length === 0) return [[]];

    cosnt result = [];
    // array = [1,2,3,4]とする
    /* 最初は0番目の要素[1]とそれ以外[2,3,4]に分割して考える */
    const subArrays = allSubArray([2,3,4]);
    // subArrays = [[[2], [3], [4]], [[2,3],[4]], [[2],[3,4]], [[2,3,4]] ] が期待される

    // [1]とsbuArraysの各要素をくっつけたものをresultにpush
    /* 期待されるresultは
   [
       [[1], [2], [3], [4]],
       [[1], [2,3], [4]],
       [[1], [2], [3,4]],
       [[1], [2,3,4]]
     ]
   */

  /* 次に、1番目までのの要素[1, 2]とそれ以外[3,4]に分割して考える*/
  /* 今感じで行けるか・・・? */

}

やってみよう

const allSubArray = (array) => {
    // 再帰終了条件
    if(array.length === 0) return [[]];


    const result = [];
    // array = [1,2,3,4]とする
    /* 最初は0番目の要素[1]とそれ以外[2,3,4]に分割して考える */
    for(let i = 1; i <= array.length; i++) {
      const fixedArray = array.slice(0, i);  //i=1の時:fixedArray = [1], i=2の時:fixedArray =[1,2]
      const otherArray = array.slice(i); //i=1の時:otherArray = [2,3,4]

      // i=1の時:subArrays = [[[2], [3], [4]], [[2,3],[4]], [[2],[3,4]], [[2,3,4]] ] が期待される
      const subArrays = allSubArray(otherArray);
      for(const subArray of subArrays) {
        result.push([fixedArray].concat(subArray));
      }
    }
    return result;
}

実行結果

できてる!!(何に使うんだろう)

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

【javascript】デフォルトパラメータとレスパラメータ

書籍のアウトプットとして

デフォルトパラメータ

以下はデフォルトパラメータを使用した例

function approvalChart(rating,width=800,height=with/2){
  //処理  
}
const nationalChart=approvalChart(narionalRatings)//800x400
const geogiaChart=approvalChart(geogiaRatings,400)//800x400

デフォルトパラメータには式が割り当てることができる。
式が評価されるのは関数が評価されるとき。

パラメータのデフォルト値はパラメータリストのインデックスに紐付けられる。

レストパラメータ

function avg(...args){
  //...
}

これで引数がargsという変数の配列として格納される。

レストパラメータの後にパラメータを配置するとエラーになる。

レスト演算子を使って関数間で引数を渡す

何らかの画像処理を行うライブラリを使用していてログ機能を追加するために処理関数をフックする必要があるとする。多くのライブラリにはミドルフックがあるが、常にそうとは限らない。
そういう場合はモンキーパッチを実行する。

モンキーパッチ

元の関数を呼び出す前にカスタムロジックを注入して関数を再定義するプロセス

{
  //元のメソッドへ参照を取得
  const originProcess = imageDoctor.process;
  //引数をすべて集める新しい関数を定義
  imageDoctor.process = function(...args) {

    //ロブ機能を注入
    console.log('imageDoctor processing', args)

    //argsを使って呼びだされた元の関数の結果を返す。
    return originProcess.apply(imageDoctor, args)
  }
}

process関数にモンキーパッチを実行し、レスト演算子をまとめた上で元の関数に返している。
これにより、元の関数が常にラッピング関数を同じ引数を使って呼び出されるようになる。

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

【Anglar】Angularで簡単なアプリ作成

以前、Vueで簡単なアプリを作成した。

【vue.js】某SRPGシミュレータ作った。
上記記事は以前作成したものです。
ゲームとかの、キャラがレベルアップしたときのパラメータ上昇のシミュレーターです。
 
現物:https://femaker-f4bc9.firebaseapp.com/

Angularで作り直した。

Angularのチュートリアルを終えたので、軽くなにかつくるか、
ということで以前つくったもののコピーをとりあえず作成しました。
 
Firebaseにデプロイしてます。
https://ngfemaker.firebaseapp.com/simulation

github
https://github.com/crane121212/NgSample_LvUpSim

Angularチュートリアルのプロジェクトを流用しているので
不要なものが残ってたりします。

主に使ったのは@input@outputを使った親と子の値の受け渡しですね。
データの処理は親で行って、値の表示は子で行っています。

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

【Angular】Angularで簡単なアプリ作成

以前、Vueで簡単なアプリを作成した。

【vue.js】某SRPGシミュレータ作った。
上記記事は以前作成したものです。
ゲームとかの、キャラがレベルアップしたときのパラメータ上昇のシミュレーターです。
 
現物:https://femaker-f4bc9.firebaseapp.com/

Angularで作り直した。

Angularのチュートリアルを終えたので、軽くなにかつくるか、
ということで以前つくったもののコピーをとりあえず作成しました。
 
Firebaseにデプロイしてます。
https://ngfemaker.firebaseapp.com/simulation

github
https://github.com/crane121212/NgSample_LvUpSim

Angularチュートリアルのプロジェクトを流用しているので
不要なものが残ってたりします。

主に使ったのは@input@outputを使った親と子の値の受け渡しですね。
データの処理は親で行って、値の表示は子で行っています。

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

GAS でAPIを作って公開する。

この記事に目的。

表題の備忘

手順

1 コード

下記のようにJsonを返すWebアプリを作成

function doGet(e){
  var json;
  json = {
    "hoge" : "hoge",
    "foo" : "bar"
  }
  json = ContentService.createTextOutput(JSON.stringify(json));
  return json;
}

2 webアプリケーションとして公開

公開したらブラウザからアクセスしてみると、jsonが表示できるはず。
Chromeの拡張機能 json Viewer を使うと表示が楽ちん。

3 クライアント JS

var request = new XMLHttpRequest();

request.open('GET', "ここにGASで発行されたWebアプリのurl", true);
request.responseType = 'json';

request.onload = function () {
  var data = this.response;
  console.log(data);
  var o = document.createElement("span");
  o.innerHTML = data["foo"];
  document.body.appendChild(o);
};

request.send();

補足

外部ドメインからだと、クロスドメインの制約に引っかかると思う。

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

【AWS】EC2インスタンスにnode.jsをインストールしてサンプルプログラムを実行

環境

Windows 10 Home
Tera Term 4.105(Macの人はTerminal)

前提

・Amazon Linux EC2
 -SSH接続可能
 -アウトバウンドのHTTP接続が可能

node.jsをインストール

ec2-linux
$ cd ~
$ wget https://nodejs.org/dist/v12.18.2/node-v12.18.2-linux-x64.tar.xz

公式サイトから最新バージョンのリンクを取得
image.png

node-v12.18.2-linux-x64.tar.xz

ec2-linux
$ cd ~
$ wget https://nodejs.org/dist/v12.18.2/node-v12.18.2-linux-x64.tar.xz

tarファイルがあることを確認

ec2-linux
$ ls
$ node-v12.18.2-linux-x64.tar

拡張子がxzになっているのでtarに変換

ec2-linux
$ mv node-v12.18.2-linux-x64.tar.xz node-v12.18.2-linux-x64.tar

解凍

ec2-linux
$ tar xvf node-v12.18.2-linux-x64.tar

色々出力されて止まったら解凍完了
image.png

実行コマンドがインストールされるnode-v12.18.2-linux-x64/binにパスを設定して移動

ec2-linux
$ export PATH=$PATH:~/node-v12.18.2-linux-x64/bin
$ cd node-v12.18.2-linux-x64/bin

app.jsというサンプルプログラムを用意

app.js
const http = require('http');

const hostname = 'localhost';
const port = 8080;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

実行

ec2-linux
$ touch app.js
$ vim app.js

~~~~~~サンプルプログラムを貼付け(vimコマンドは各自お調べください)~~~~~~

$ node app.js
Server running at http://localhost:8080/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScriptの組み込み型関数 Pickの使いどころ

Pickの使いどころ

Pickについて考えたので書く

最小サンプル

type Product = {
    name: string
    price: number
    brand:  { 
        id: number,
        name: string
    }
}
//type Pick<T, K extends keyof T>
type PickedProduct = Pick<Product,"name"|"brand">

PickedProduct は以下の型として展開される

{
    name:string
    brand: {
        id:number
        name:string
    }    
}

つまりTの型から K に指定したキーだけの部分型を切り出せる。

サンプルコード

実際単独でPickを使うことは個人的にあまり無く、ジェネリクスとの併用で使う事が多いんじゃ無いかと思います。
よくある商品APIを例にして実用例を説明します。

例えば /product/:id をリクエストすることで 商品(Product)を返すAPIでクエリパラメーターで
レスポンスに含まれるプロパティを指定できるものがあるとします。

// 
/*
 * https://localhost/product/1
 * クエリパラメーターを指定しない場合は全プロパティを返す
 */

// 返却レスポンス
{
    name : "令和最新 第3世代 bluetoothイヤホン",
    price : 3400,
    brand : { 
        id: 234,
        name: "M.I.C"
    }
}
// 
/*
 * https://localhost/product/1?fields[]=name&fields[]=brand
 * クエリパラメーターを指定した場合は
 */

// 返却レスポンス ※price を指定していないので レスポンスから取り除かれます。
{
    name : "令和最新 第3世代 bluetoothイヤホン",
    brand : { 
        id: 234,
        name: "M.I.C"
    }
}

これをFetchAPI で取得する関数を考えます。

まず単純に書きます。

type Product = {
    name: string
    price: number
    brand: {
        id:number
        name:
    } 
}

const getProduct = async( id : number ,fields? :string[] ) => {

    const query = (fields) 
        ? "?" + fields.map(field => `fields[]=${field}`).join("&") 
        : ""
    const uri = `https://localhost/product/${id}${query}`
    const response = await fetch(uri)

    //エラー処理は省略
    return await response.json() as Product
}

response.json() は Promise<any> になってしまうので as で型をつけてみました。
一見良いですが これでは、fields を指定した際の絞り込まれたレスポンスと異なり実行時エラーが発生します。

// 実行時エラーが発生するコード

;(async() => {
    const product = await getProduct(1,["name"])
    // product.brand が存在しないため エラーになります。 
    console.log(product.brand.id)
})()

対処としてレスポンスのキーがあるかどうかわかならないなら省略可能型にしてしまえという発想がまず浮かびます。

type Product = {
    name?: string
    price?: number
    brand?: {
        id:number
        name:string
    } 
}



const getProduct = async( id : number ,fields? :string[] ) => {

    const query = (fields) 
        ? "?" + fields.map(field => `fields[]=${field}`).join("&") 
        : ""
    const uri = `https://localhost/product/${id}${query}`
    const response = await fetch(uri)

    //エラー処理は省略
    return await response.json() as Product
}

Product は そのままにし await response.json() as Partial<Product> にしても良いでしょう。
実行時は 存在チェックをすればコンパイルエラーになりません。

//実行コード
;(async() => {
    const product = await getProduct(1,["name","brand"])

    //プロパティにアクセスするときは存在チェックをしてから!
    if(product.brand)
        console.log(product.brand.id)
    }

})()

実際これでも間違いではなく、コンパイルエラーにもならず、実行時も安全なのですが
ただただただ面倒でこういったコードだらけになると確実にTypescriptを嫌いになります。

加えて、fieldsにProductに存在しないkeyが指定できることにも問題があります。

PickとGenericsを併用して書く

こういった場合はジェネリクス(引数からの推論)とPickを使う事で表現できます。

以下のように書き換えます。 やっとPickが出てきます。

type Product = {
    name: string
    price: number
    brand: {
        id:number
        name:string
    } 
}

const getProduct = async<T extends keyof Product>( id : number ,fields? : T[] ) => {

    const query = (fields) 
        ? "?" + fields.map(field => `fields[]=${field}`).join("&") 
        : ""
    const uri = `https://localhost/product/${id}${query}`
    const response = await fetch(uri)

    //エラー処理は省略

    return await response.json() as Pick<Product,T>
}


  • T extends keyof Product は 型T が Productに存在するキーである事を強制します。
  • fields? :T[] の箇所の記述で型Tは型引数で明示しない場合はfiledsに指定された引数から推論されます。(つまり T は fieldsに指定された要素のUnion型を取る)
  • Pick<Product,T> でレスポンス絞り込みます。

使用感

;(async() => {

const product = await getProduct(1)


/*
product は 以下の型とみなされる
    {
    name: string
    price: number
    brand: {
        id:number
        name:string
    } 
    }
*/

const product2 = await getProduct(2,["name"])

/*
product2 は 以下の型とみなされる
    {
    name: string
    }
*/


const product3 = await getProduct(3,["name","brand"])

/*

product3 は 以下の型とみなされる
   {
    name: string
    brand: {
        id:number
        name:string
    } 
    }
*/

})()

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

カブトムシを様々な種類にトランスフォームさせたくなったときの簡単なTIPS

【 完成形 】

See the Pen MWKXrmO by daisukeibi (@daisukeibi) on CodePen.

まず初めにGSAPのライブラリを読み込む。

index.html
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/gsap-latest-beta.min.js?r=2314"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/MorphSVGPlugin3.min.js"></script>

使いたいSVGをダウンロードしてコードエディタで開き、
svgタグのviewBoxとpathタグのd属性をコピーして貼り付ける。

index.html
<svg id="svg" viewBox="(viewBox属性を貼り付ける)" fill="#4F392B">
  <path id="icon1" d="(一匹目のカブトムシのd属性を貼り付ける)">
  <path id="icon2" d="(二匹目のカブトムシのd属性を貼り付ける)">
  <path id="icon3" d="(三匹目のカブトムシのd属性を貼り付ける)">
</svg>

cssで初期表示しないpath要素を隠す。

style.css
#icon2,#icon3{
visibility: hidden;
}

jsで動かす。

script.js
var tl = gsap.timeline({repeat:-1});

tl.to("#icon1", 1,{
  morphSVG: "#icon2"
}).to("#icon1", 1,{
  morphSVG: "#icon3"
}).to("#icon1", 1,{
  morphSVG: "#icon1"
})

以上で完成です!

morphSVGPluginは有料ですが、他にもAnime.jssnap.svgなど無料で使えるのもいっぱいあるので、「SVG モーフィング」で調べてみてください。(モーフィング時の滑らかさとか実装の難しさに差があって面白いです)

JS苦手な方や手っ取り早く動かしたいという方には短い記述で簡単に実装できるので、有料にはなりますがmorphSVGPluginがおすすめです。


使用ライブラリ:https://greensock.com/morphsvg/

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

Xamarin.Forms ガワネイティブアプリで、C# から JavaScript へのリクエスト~レスポンスを Task<T> にする

メモ書きなので雑。

シナリオ

  • Xamarin.Forms + WebView(HTML, JavaScript な SPA) のガワネイティブアプリである
  • Android アプリの BACK キーの有効無効を、WebView 内のページ状態によって切り替えたい
    • データ編集中(未保存)なので Back キーで history.back やアプリ終了されたら困る、とか

流れ

1. [Forms側] Action<TaskCompletionSource<bool>> OnRequestIsEnableBackKey { get; set; } を生やす

2. [Forms側] WebViewEx(WebViewを拡張したもの)に Task<bool> IsEnableBackKeyAsync() も生やす。実装は次のように。

public Task<bool> IsEnableBackKeyAsync()
{
    var comp = new TaskCompletionSource<bool>();

    if (OnRequestIsEnableBackKey != null)
    {
        OnRequestIsEnableBackKey.Invoke(comp);
    }
    else
    {
        comp.SetResult(false);
    }

    return comp.Task;
}

3. [Android側] WebViewRenderer を拡張した MyWebViewRenderer を作る。

4. Xamarin.Forms の WebView で JavaScript 連携を行う(with iOS/Android共通化) - Qiita を参考に JavaScriptHandler も作る。コンストラクタで Control に加え e.NewElement as WebViewEx も渡す。引数の名前は WebViewEx outer とする。AddJavascriptInterface の第2引数は GawaApp とでもしておく。

5.JavaScriptHandler のコンストラクタで OnRequestIsEnableBackKey を受信するコードを書く。

private TaskCompletionSource<bool> isEnableBackKeyComp = null;

public JavaScriptHandler(Android.Webkit.WebView webView, WebViewEx outer)
{
    outer.OnRequestIsEnableBackKey = comp => 
    {
        isEnableBackKeyComp = comp;

        // メインスレッドから呼ばないとエラー
        webView.Post(() =>
        {
            webView.EvaluateJavascript("window.requestIsEnableBackKey()", null);
            // webView.LoadUrl("javascript:window.requestIsEnableBackKey();"); ←これでもOKっぽい
        });
    };
}

ここまでの処理で、Forms 側で await webViewEx.IsEnableBackKeyAsync() が呼び出されたら、JavaScript の window.requestIsEnableBackKey() 関数が呼び出される。

6.JavaScriptHandler 内に onResultCanExitApp を生やす。JavaScript側 から結果が通知されるメソッドである。次のように。

[Export]
[Android.Webkit.JavascriptInterface]
public void onResultIsEnableBackKey(bool value)
{
    isEnableBackKeyComp?.SetResult(value);
    isEnableBackKeyComp = null; // one shot
}

JavaScript 側で GawaApp.onResultIsEnableBackKey(true or false) を呼び出すと、このメソッドがコールバックされる。
TaskCompletionSource である isEnableBackKeyComp に値を設定すれば、Forms 側の await webViewEx.IsEnableBackKeyAsync() の結果が返される。

7.JavaScript 側はきっとこんな感じ

window.requestIsEnableBackKey = () => {

  // 何かの処理

  GawaApp.onResultIsEnableBackKey(true or false);
}

途中まで実装したけど、面倒になって(JavaScript→C# に単方向の方がシンプルでいいやと思って)やめちゃったので、アイデアだけ残しておきます。

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

【備忘録:jQuery】ブロックスコープ

関数に引数について

今回csvを読み込みそれをJSONに変換しフロント部分を対応していたのですが、csv読み込みのajax内のdone内で自分で書いていた、変数がなぜ読まれるのかと思っていたので備忘録です。

ソース

hoge.js
for (var num = 0; num < jsonArray.length; num++) {

  if(jsonArray[num].hogehoge === '1'){
    htmlOutPut();//①これが呼び出す関数
  }

//ここが呼び出される関数
  function htmlOutPut() {
    const $jsonHoge = $('#hogehoge');

    let $html = '<li>';
        $html += '<a href="#" data-modal="' + num + '">';//②ここのnumが呼ばれているのが不思議だった
        $html += '<p>' + jsonArray[num].ttl + '</p>';//②ここのnumが呼ばれているのが不思議だった

//以下省略

    }
  }

この②ように「for内で関数呼び出しをしているため参照できるだろうな」と思ってました。

ブロックスコープ

var、letについて以下参照させていただいたのですが、
https://qiita.com/masarufuruya/items/096e51c3e4c36c86ae27

{}内のブロックのみで使うブロックスコープが作れるか作れないかが今回の要因だったようです。

hoge.js
for (var num = 0; num < jsonArray.length; num++) {
//ここのnumの宣言がvarのため、ブロックスコープが作れない
  if(jsonArray[num].hogehoge === '1'){
    htmlOutPut();//
  }

  function htmlOutPut() {
    const $jsonHoge = $('#hogehoge');

    let $html = '<li>';
        $html += '<a href="#" data-modal="' + num + '">';//ブロックスコープが作れないのでここでアクセスできた
        $html += '<p>' + jsonArray[num].ttl + '</p>';//ブロックスコープが作れないのでここでアクセスできた

もしこれが以下のように、letでの宣言であればブロックスコープとなりアクセスができませんでした。

hoge.js
for (let num = 0; num < jsonArray.length; num++) {
//宣言がletのため、ブロックスコープが作れる
  if(jsonArray[num].hogehoge === '1'){
    htmlOutPut();//
  }

  function htmlOutPut() {
    const $jsonHoge = $('#hogehoge');

    let $html = '<li>';
        $html += '<a href="#" data-modal="' + num + '">';//ブロックスコープなのでアクセスできない
        $html += '<p>' + jsonArray[num].ttl + '</p>';//ブロックスコープなのでアクセスできない

正直、上記の状態でアクセスできたのですが、
- どこで宣言しているものを参照しているのかがわからなくなる
- コードの保守性が落ちる
ということもあり、関数の引数で渡すようにしました。

引数

jsonArray[num]自体はそのまま関数内で使用したかったので、

hoge.js
for (let num = 0; num < jsonArray.length; num++) {
  if(jsonArray[num].hogehoge === '1'){
    htmlOutPut(jsonArray[num]);//jsonArray[num]を引数で渡す

  function htmlOutPut(jsonNum) {//渡された値を使用する
    const $jsonHoge = $('#hogehoge');

    let $html = '<li>';
        $html += '<a href="#" data-modal="' + num + '">';
        $html += '<p>' + jsonNum.ttl + '</p>';//先程同じように指定するが、引数の値を参照させる

  }

このように、
・呼び出す関数の引数に、jsonArray[num]を渡し、関数で値を使用する
ということで対応しました。

ただ、変数numだけ(カウントされた数字が格納された変数)も使用したかったので、

hoge.js
for (let num = 0; num < jsonArray.length; num++) {
  if(jsonArray[num].hogehoge === '1'){
    htmlOutPut(jsonArray[num], num);//jsonArray[num]とnumを引数で渡す

  function htmlOutPut(jsonNum, index) {//渡された値を使用する
    const $jsonHoge = $('#hogehoge');

    let $html = '<li>';
        $html += '<a href="#" data-modal="' + num + '">';//ここはindexで参照する
        $html += '<p>' + jsonNum.ttl + '</p>';//ここはjsonNumで参照する

  }

上記のように、引数を2つにし関数内に渡してあげることで解決しました。

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