- 投稿日:2020-07-11T23:26:16+09:00
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
- 投稿日:2020-07-11T22:35:05+09:00
Chrome拡張でJSONファイルを手軽に読み込んでみよう!
以下のような構造のChrome拡張のファイルがあったとします。
my-extension ├── src │ ├── config.json │ └── main.js └── manifest.jsonでは、
main.js
からconfig.json
を読み込んでみましょう。なに、難しいことは考えず、以下の関数をコピペしてください。コード
main.jsfunction 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); })チャンチャン。
- 投稿日:2020-07-11T19:45:23+09:00
【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
- 投稿日:2020-07-11T19:29:00+09:00
【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アカウントでログインしてください。
スプレッドシートの編集画面を開きます。
以下のように名前を付けスケジュール表を作ってください。次に、スプレッドシートのメニューバーから
「ツール」⇒「スクリプトエディタ」を選んでください。
下のようなスクリプト編集画面が表示されたら、好きな題名をつけてください。次からいよいよコードを書きます!!!!
4-3.gasでコーディング!!
まずコードの全文を以下に示します。
アクセストークン と user-id の部分には4-1でメモしたものを使ってください。misako.gsvar 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.gsvar 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.gsfor(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.gsfunction pushMessage() { var postData = { "to": USER_ID, "messages": [{ "type": "text", "text": TEXT_MESSAGE, }] };この関数によって、先ほどの処理が実行され、TEXT_MESSAGEがセットされます。
misako.gsvar url = "https://api.line.me/v2/bot/message/push"; var headers = { "Content-Type": "application/json", 'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN, };LINE APIのURLとアクセストークンをセットします。
misako.gsvar options = { "method": "post", "headers": headers, "payload": JSON.stringify(postData) }; var response = UrlFetchApp.fetch(url, options); }LINE APIで送信します。
コーディングが終わったら、忘れずに保存しましょう!
4-4.トリガーで自動化
gasにはトリガーという便利な機能があります。
条件を設定しておけば、自動で実行してくれます。今回は時刻の条件を設定し、毎日その時刻に自動送信される設定にしましょう!
メニューから「編集」⇒「現在のプロジェクトのトリガー」を選びます。
管理画面が表示されたら、「トリガーを追加」をクリックします。
下の動画のように設定画面が表示されます。
(URLをクリックしてもらえると、動画が見れます!)gasのトリガー設定
— しもんもす@NoCodeハッカソン参戦 (@Lv_up_SmnmS) July 11, 2020
Qiitaの記事用
#Qiita #初投稿 #intee #gas #LINEbot
pic.twitter.com/2H1tvirRzW動画と同じように設定すると、毎日、朝の5時から6時に、その日のスケジュールを自動通知してくれます!!
4-5.友達追加してみよう!
LINEdevelopersのチャネル基本設定画面からQRコードを読み込んでみましょう!!
友達追加ができるはずです!!もしすぐに使ってみたい場合は、先ほどのトリガー設定の時刻を、すぐあとの時刻に設定すれば動作のテストができます!!
アカウントの画像や、通知の時のコメントなど自分好みにカスタマイズして、オリジナルの秘書botを作ってみてください!!
さいごに
ここまで記事を読んでくださりありがとうございました!!
初投稿で至らない部分が多かったと思います(-_-;)実際に作ってみて、やはりインプットだけでなくアウトプットすることがとても大切だと感じました。今後もプロダクトや記事投稿、情報発信も頑張っていければと思っています!!
ここで紹介をさせていただきます。
僕がプログラミングをはじめ、実際にプロダクトを作れるようになったり、他の学生とつながりを作り、ハッカソンに出るようになれたのは、inteeリアルカレッジの存在があったからです。チャレンジしたい人、力をつけたい人にはかなりおススメです。
次々に目標が生まれるような、とても刺激的な環境です!!学生限定のサービスにはなるけれど、たくさん成長できるとてもいい環境です!気になった方はぜひ参加してみてください!
僕のTwitterに活動や、学んだことなど投稿しているので、もしよければフォローお願いします!!
また今回の記事がいいな!と思っていただけたら、Twitterなどで共有とLGTM!お願いします!!ありがとうございました!!
- 投稿日:2020-07-11T19:11:50+09:00
ウソ穴 Ver 5 Type A / GStreamer
はじめに
個人開発
ウソ穴
の作り方を紹介します。ウソ穴とは
ウソ穴
は、ライブ映像 or 動画とARを組み合わせて、壁に穴が空いた錯覚を作り出します。Webサイトなので、ユーザーはアプリのインストール無くウソ穴を使用できます。
ウソ穴 Ver 5 Type A / GStreamer
今回は、GStreamer によるライブ配信と組み合わせる
ウソ穴 Ver 5 Type A
を紹介します。
- Webサイトは、Windows10環境のブラウザ Chrome, FireFox で動作を確認
- iPhone,Androidでは正常に動作せず
構成図
※ウソ穴 Ver 5 Type A は、iPhone, Android 非対応
デモ映像
壁に穴があいたように錯覚させる AR 『ウソ穴』の作り方をQiitaに投稿しました。
— j4amountain (@zsipparu) July 11, 2020
ウソ穴 Ver 5 Type A / GStreamerhttps://t.co/tKZUmspUrL#ウソ穴 pic.twitter.com/L6p8Cs35riウソ穴の構築方法
ここから、
ウソ穴 Ver 5 Type A
の構築方法を紹介します。
- 構築概要
- ラズパイにカメラモジュールを接続
- ラズパイに、ウソ穴の構成要素を集約
- GStramerで、カメラモジュールの映像をストリーミング
- Nodejsで、HTTPS対応のWebサーバーを構築
- A-FrameでWebサイトでAR機能を実装
ラズパイのセットアップ
ラズパイOSのインストールとセットアップで使っているリンク
- ラズパイOSインストール[2020年版]
- ラズパイOSのインストール
- ラズビアン(Raspbian)初期設定
- Wi-Fi接続など初期設定
- ストリーミングサーバを構築してみた
- カメラモジュールの認識
ストリーミング
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のダウンロード
これで、
ウソ穴 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ウソ穴 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.tsWebサービスを開始する
以下コマンドで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今回はこれでおわり、iPhone対応版とか、動作再生版とか、別バージョンのウソ穴も公開する予定です。
- 投稿日:2020-07-11T18:44:12+09:00
Vue.js/Nuxt.jsでパララックス効果を実装
Vue/Nuxtでパララックスしようぜ!
※パララックス効果とは:スクロールしたときに、スクロール量に合わせてずれたり動いたりするやつ。
Vue.jsプラグインもいくつかあるけど、自前で実装した方が自由度高くて楽だった。勉強にもなるし。
実装方法概要
実装方法の大まかな説明
- scrollイベントをlisten
- スクロール量やら色々パラメータを取って移動量を計算
- imgのstyleに
object-position: {移動量X}% {移動量Y}%
をぶち込む
- ※
object-position
を知らない方は先に 事前知識 : CSS object-position について をどうぞ。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 に計算した移動量をぶち込む } } }以上の実装方法ですが、場合によって移動量の計算方法が異なります。
- img要素が完全に隠れている場合。(スクロールしないとimg要素が現れない場合)
- img要素が最初から見えている場合。(スクロールしなくてもimg要素が少しでも見えていて、これからスクロールする場合。)
1. img要素が完全に隠れている場合
この場合、画像の移動量は、以下の通りである。
- スクロールして要素が見え始めた時は、移動量が0%である。
- = ブラウザの表示領域のbottomが、img要素のtop に到達した時
- スクロールして要素が見えなくなった時は、移動量が100%である。
- = ブラウザの表示領域のtopが、img要素のbottom に到達した時
以上のように、
this.objPosY
がスクロール量に合わせて0から100まで動けばよい。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)
getBoundingClientRect().top
は、ブラウザの表示領域を基準とする、img要素の絶対座標である。
つまり、ブラウザの表示領域のtopとimg要素の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 + innerHeight1.3 要素が見えなくなった時に、移動量が100%となるやつを作る
topVisibleScrollY
がbottomVisibleEndY
に達した時に100になるようにすればいい。this.objPosY = topVisibleScrollY / bottomVisibleEndY * 100 // 見え始めた時は0、見えなくなった時100
topVisibleScrollY
もbottomVisibleEndY
も単位がピクセル量なのでパーセントに変換。
おしまい!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 }スクロール量については、途中から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; }ただこの方法は、CSSに画像のパスを配置する必要がある。なので、パスが動的に変わるような場合は、imgタグを使いたいときもある。
その際に登場するのが、object-fit
とobject-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.jsimport 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()をいちいち書かなくて良いの天才すぎる。以下から参考にさせていただきました。(多分)
- Nuxt.jsで異なるコンポーネントから共通で利用できる関数を定義する(mixin編) - Qiita
- Vue.js 外側をクリックすると閉じるドロップダウンメニュー - Qiita
- ここでイベントリスナーの知見を得た。あざます。
PS : Twitterで「パララックス、SafariとiOSで動かなかったはずでは?」って言われたのでビビってます。とりあえずSafariでは動作確認できました。
- 投稿日:2020-07-11T18:33:20+09:00
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です。
必要なディレクトリを準備
このような状態にしましょう。
bootstrapの中身については、
node_module/bootstrap/distの中をコピーしてくださいapp.jsの中身を記述する
まず、必要最低限の中身を書いていきます。
app.jsvar express = require('express'); var app = express(); app.listen(3000);続いて、テンプレートエンジンの読み込みを行います。
app.jsvar express = require('express'); var app = express(); app.set("view engine", "ejs" );//テンプレートエンジンの読み込み app.listen(3000);次に、静的ファイルの配信を行います。
app.jsvar express = require('express'); var app = express(); app.set("view engine", "ejs" );//テンプレートエンジンの読み込み app.use('/public', express.static(__dirname + '/public'));//静的ファイルの読み込み app.listen(3000);最後に、ルーティングを記述したファイルを呼び出します。
app.jsvar 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.jsvar 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-11T17:13:06+09:00
引数について(100 days of code)
はじめに
100 days of codeを始めて7日目。
今日は引数について復習しました。引数とは?
引数とは、関数を実行するときに関数に渡す値を指します。
他にも引数の特徴として、値や変数をセットすることができます。
引数を受け取る値の個数は関数によって変わってきます。
alertは引数を1つだけ受け取れる。
console.logであれば引数を複数受け取れる。(何個でもOK)例
index.jsconst hello = function (name, age) { console.log(name + 'さんは' + age + '歳です。'); } hello(Yuki, 27); // 出力結果 "Yukiさんは27歳です。"終わりに
引数を用いたコールバック関数はJavaScriptをやってて避けて通れないので、簡単に復習しました。
今日学習した引数は初歩的な部分なので、実践で使えるようにできればと思います。
明日も頑張ります。
- 投稿日:2020-07-11T16:48:42+09:00
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
- 投稿日:2020-07-11T16:30:50+09:00
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ライブラリ、キー補間構文などがあるということを抑えておきましょう。参考資料
- 投稿日:2020-07-11T16:15:07+09:00
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; }実行結果
できてる!!(何に使うんだろう)
- 投稿日:2020-07-11T16:12:55+09:00
【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関数にモンキーパッチを実行し、レスト演算子をまとめた上で元の関数に返している。
これにより、元の関数が常にラッピング関数を同じ引数を使って呼び出されるようになる。
- 投稿日:2020-07-11T14:45:48+09:00
【Anglar】Angularで簡単なアプリ作成
以前、Vueで簡単なアプリを作成した。
【vue.js】某SRPGシミュレータ作った。
上記記事は以前作成したものです。
ゲームとかの、キャラがレベルアップしたときのパラメータ上昇のシミュレーターです。
現物:https://femaker-f4bc9.firebaseapp.com/Angularで作り直した。
Angularのチュートリアルを終えたので、軽くなにかつくるか、
ということで以前つくったもののコピーをとりあえず作成しました。
Firebaseにデプロイしてます。
https://ngfemaker.firebaseapp.com/simulationgithub
https://github.com/crane121212/NgSample_LvUpSimAngularチュートリアルのプロジェクトを流用しているので
不要なものが残ってたりします。主に使ったのは@inputと@outputを使った親と子の値の受け渡しですね。
データの処理は親で行って、値の表示は子で行っています。
- 投稿日:2020-07-11T14:45:48+09:00
【Angular】Angularで簡単なアプリ作成
以前、Vueで簡単なアプリを作成した。
【vue.js】某SRPGシミュレータ作った。
上記記事は以前作成したものです。
ゲームとかの、キャラがレベルアップしたときのパラメータ上昇のシミュレーターです。
現物:https://femaker-f4bc9.firebaseapp.com/Angularで作り直した。
Angularのチュートリアルを終えたので、軽くなにかつくるか、
ということで以前つくったもののコピーをとりあえず作成しました。
Firebaseにデプロイしてます。
https://ngfemaker.firebaseapp.com/simulationgithub
https://github.com/crane121212/NgSample_LvUpSimAngularチュートリアルのプロジェクトを流用しているので
不要なものが残ってたりします。主に使ったのは@inputと@outputを使った親と子の値の受け渡しですね。
データの処理は親で行って、値の表示は子で行っています。
- 投稿日:2020-07-11T14:11:04+09:00
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();補足
外部ドメインからだと、クロスドメインの制約に引っかかると思う。
- 投稿日:2020-07-11T13:18:51+09:00
【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公式サイトから最新バージョンのリンクを取得
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.xztarファイルがあることを確認
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実行コマンドがインストールされる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/binapp.jsというサンプルプログラムを用意
app.jsconst 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/
- 投稿日:2020-07-11T11:43:52+09:00
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 } } */ })()
- 投稿日:2020-07-11T11:03:01+09:00
カブトムシを様々な種類にトランスフォームさせたくなったときの簡単な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.jsvar tl = gsap.timeline({repeat:-1}); tl.to("#icon1", 1,{ morphSVG: "#icon2" }).to("#icon1", 1,{ morphSVG: "#icon3" }).to("#icon1", 1,{ morphSVG: "#icon1" })以上で完成です!
morphSVGPluginは有料ですが、他にもAnime.jsやsnap.svgなど無料で使えるのもいっぱいあるので、「SVG モーフィング」で調べてみてください。(モーフィング時の滑らかさとか実装の難しさに差があって面白いです)
JS苦手な方や手っ取り早く動かしたいという方には短い記述で簡単に実装できるので、有料にはなりますがmorphSVGPluginがおすすめです。
使用ライブラリ:https://greensock.com/morphsvg/
- 投稿日:2020-07-11T00:52:43+09:00
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# に単方向の方がシンプルでいいやと思って)やめちゃったので、アイデアだけ残しておきます。
- 投稿日:2020-07-11T00:07:45+09:00
【備忘録:jQuery】ブロックスコープ
関数に引数について
今回csvを読み込みそれをJSONに変換しフロント部分を対応していたのですが、csv読み込みのajax内のdone内で自分で書いていた、変数がなぜ読まれるのかと思っていたので備忘録です。
ソース
hoge.jsfor (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.jsfor (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.jsfor (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.jsfor (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.jsfor (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つにし関数内に渡してあげることで解決しました。