- 投稿日:2020-06-01T23:59:26+09:00
Javascript 初めてのGSAPアニメーションの使い方 その2
前回の記事はこちら
Javascript 初めてのGSAPアニメーションの使い方 その1Tween.from
Tween.fromでは指定したプロパティを起点に要素をアニメーションすることができます。
htmlとcssをを以下に追記します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="styles/style.min.css" /> <title>Document</title> </head> <body> <div class="circle"></div> <div class="square"></div> <div id="rectangle"></div> //追加 <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script> <script src="scripts/main.js"></script> </body> </html>body{ display: flex; justify-content: center; align-items: center; height: 100vh; } .circle{ background-color: orangered; height: 150px; width: 150px; border-radius: 50%; } .square{ width: 150px; height: 150px; background-color: green; } #rectangle{ //追記 width: 150px; height: 50px; background-color: skyblue; }jsシート配下のように追記します。
TweenMax.to($('.circle'), 1, { x:150, y:150, backgroundColor: 'blue' }); TweenMax.to($('.square'), 3, { x: -150, y:-150, scale: 2, delay: 1, ease:Back.easeOut }); TweenMax.from($('#rectangle'), 2, { y: 200, rotation: 180 , scale:1.5 }); //追記実行結果
水色の長方形は起点として
y軸方向200pxから、180度回転しながら、1.5倍の大きさから
アニメーションしてきます。
Jqueryを合わせて使う
Jqueryのセレクター指定と合わせて使うこともできます。
htmlに以下を追記します(CSSは変更なしです)
JqueryもCDNで読み込んで使います。<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="styles/style.min.css" /> <title>Document</title> </head> <body> <div class="circle"></div> <div class="square"></div> <div id="rectangle"></div> <ul> //追記 <li>Bacon</li> <li>Cheese</li> <li>Jam</li> <li>Bread</li> <li>Eggs</li> </ul> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> //追記 <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script> <script src="scripts/main.js"></script> </body> </html>要素の取得をJqueryの$()に書き換えてみます。
TweenMax.to($('.circle'), 1, { x:150, y:150, backgroundColor: 'blue' }); TweenMax.to($('.square'), 3, { x: -150, y:-150, scale: 2, delay: 1, ease:Back.easeOut }); TweenMax.from($('#rectangle'), 2, { y: 200, rotation: 180 , scale:1.5 }); TweenMax.to($('li'),1,{ x:50 });jqueryの記述によりli全体を取得してアニメーションすることができています。
以下のような記述も動作します。
TweenMax.to($('li:first-child'),1,{ x:50 }); //liの最初の要素を動かす(Bacon) TweenMax.to($('li:last-child'),1,{ x:50 }); //liの最後の要素を動かす(Eggs) TweenMax.to($('li:nth-child(3)'),1,{ x:50 }); //liの3番めの要素を動かす(Jam) TweenMax.to($('li:nth-child(odd)'),1,{ x:50 }); //liの奇数の要素を動かす(Cheese,Bread) TweenMax.to($('li:nth-child(even)'),1,{ x:50 }); //liの偶数の要素を動かす(Bacon,Jam,Eggs)次回はTimelinemaxです
Javascript 初めてのGSAPアニメーションの使い方 その3
- 投稿日:2020-06-01T23:59:26+09:00
Javascript 初めてのGSAPアニメーションの使い方 その2 Tween.from/Jqueryとの併用
前回の記事はこちら
Javascript 初めてのGSAPアニメーションの使い方 その1 Tweenmax.toの使い方Tween.from
Tween.fromでは指定したプロパティを起点に要素をアニメーションすることができます。
htmlとcssをを以下に追記します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="styles/style.min.css" /> <title>Document</title> </head> <body> <div class="circle"></div> <div class="square"></div> <div id="rectangle"></div> //追加 <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script> <script src="scripts/main.js"></script> </body> </html>body{ display: flex; justify-content: center; align-items: center; height: 100vh; } .circle{ background-color: orangered; height: 150px; width: 150px; border-radius: 50%; } .square{ width: 150px; height: 150px; background-color: green; } #rectangle{ //追記 width: 150px; height: 50px; background-color: skyblue; }jsシート配下のように追記します。
TweenMax.to($('.circle'), 1, { x:150, y:150, backgroundColor: 'blue' }); TweenMax.to($('.square'), 3, { x: -150, y:-150, scale: 2, delay: 1, ease:Back.easeOut }); TweenMax.from($('#rectangle'), 2, { y: 200, rotation: 180 , scale:1.5 }); //追記実行結果
水色の長方形は起点として
y軸方向200pxから、180度回転しながら、1.5倍の大きさから
アニメーションしてきます。
Jqueryを合わせて使う
Jqueryのセレクター指定と合わせて使うこともできます。
htmlに以下を追記します(CSSは変更なしです)
JqueryもCDNで読み込んで使います。<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="styles/style.min.css" /> <title>Document</title> </head> <body> <div class="circle"></div> <div class="square"></div> <div id="rectangle"></div> <ul> //追記 <li>Bacon</li> <li>Cheese</li> <li>Jam</li> <li>Bread</li> <li>Eggs</li> </ul> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> //追記 <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script> <script src="scripts/main.js"></script> </body> </html>要素の取得をJqueryの$()に書き換えてみます。
TweenMax.to($('.circle'), 1, { x:150, y:150, backgroundColor: 'blue' }); TweenMax.to($('.square'), 3, { x: -150, y:-150, scale: 2, delay: 1, ease:Back.easeOut }); TweenMax.from($('#rectangle'), 2, { y: 200, rotation: 180 , scale:1.5 }); TweenMax.to($('li'),1,{ x:50 });jqueryの記述によりli全体を取得してアニメーションすることができています。
以下のような記述も動作します。
TweenMax.to($('li:first-child'),1,{ x:50 }); //liの最初の要素を動かす(Bacon) TweenMax.to($('li:last-child'),1,{ x:50 }); //liの最後の要素を動かす(Eggs) TweenMax.to($('li:nth-child(3)'),1,{ x:50 }); //liの3番めの要素を動かす(Jam) TweenMax.to($('li:nth-child(odd)'),1,{ x:50 }); //liの奇数の要素を動かす(Cheese,Bread) TweenMax.to($('li:nth-child(even)'),1,{ x:50 }); //liの偶数の要素を動かす(Bacon,Jam,Eggs)次回はTimelinemaxです
Javascript 初めてのGSAPアニメーションの使い方 その3 Timelinemax
- 投稿日:2020-06-01T23:49:59+09:00
iTunesAPIを使ってiTunesメディアからリンクを見つけ出す
大まかな処理内容
ショートカット側
1.テキストもしくはURLが渡される
→StoreIDが含んでいるかチェック、含んでいたらScriptableに渡す{ "how2": "lookup", "id": "< StoreID >" }2.iTunesメディアが渡される
→タイトル名、アルバム名、アーティスト名、メディアタイプを取得してScriptableに渡す{ "how2": "search", "title": "< タイトル >", "album": "< アルバム >", "artist": "< アーティスト >", "mediatype": "< メディアタイプ >" }3.何も渡されない
→エラー通知Scriptable側
how2 == lookup
https://itunes.apple.com/lookup?id=<StoreID>&country=jp&lang=ja_jp
にGETリクエスト。
json貰ったらスクリプトを終了して出力。終わり!how2 == search
https://iTunes.apple.com/search?term=<タイトル> <アルバム> <アーティスト>&media=<メディアタイプ>&country=jp&lang=ja_jp
にGETリクエスト。
jsonを貰ったら、findメゾットでtrackName
,collectoonName
,artistName
が<タイトル>
,<アルバム>
,<アーティスト>
と一致する要素を見つける。
見つかった要素を(さぞlookupしたからのように)フォーマットして、スクリプトを終了して出力。おしまい!ショートカット
スクリプト
Musica.jsconst INPUT = JSON.parse(args.plainTexts[0]); const HOWTO= INPUT.how2; const LIMIT = String(30); const COUNTRY = 'jp'; const LANG = 'ja_jp'; const BASEURL = 'https://itunes.apple.com/' + HOWTO; if (HOWTO == 'lookup') { let id = INPUT.id; let url = BASEURL + '?id=' + id + '&country=' + COUNTRY + '&lang=' + LANG; Pasteboard.copy(url) // debug let req = new Request(url); let response = await req.loadJSON(); Script.setShortcutOutput(response); Script.complete(); } else if (HOWTO == 'search') { let title = INPUT.title; let album = INPUT.album; let artist = INPUT.artist; let mediatype = INPUT.mediatype; let term = [ title, album, artist ].join(' ') let query = '?term=' + encodeURIComponent(term) + '&media=' + mediatype + '&country=' + COUNTRY + '&lang=' + LANG + '&limit=' + LIMIT; let url = BASEURL + query; Pasteboard.copy(url) // debug let req = new Request(url); let response = await req.loadJSON(); let rslts = response.results let found = rslts.find(function(rslts) { return rslts.artistName == artist && rslts.trackCensoredName }); if (found != null) { let formatted = { "resultCount": 1, "results": [ found ] }; // await QuickLook.present(found) Script.setShortcutOutput(formatted); Script.complete(); } else { Script.setShortcutOutput('formatted is null') Script.complete(); }; } else { console.error('HOWTO isn’t lookup or search'); Script.complete(); };半年間ずっとこいつと格闘した証です。とりあえず足跡が残せてよかった…
- 投稿日:2020-06-01T22:57:01+09:00
stateを直接参照しないシンプルなVuexサイクル
Vuexとは?
Vue.jsアプリケーションにおける状態管理パターンライブラリです。
コンポーネント間のデータや関数の受け渡しには、propsやemitを使いますが、Vuexは全てのコンポーネントのための集中型のStoreになります。
propsやemitだけでデータの受け渡しをするのは、バケツリレーに似ていて、vueファイルに何度も同じコードを書く必要があったり、無駄にロジックが発生します。Vueコンポーネント層とVuexはどう繋がるのか?
今回の記事のタイトルは「stateを直接参照しないシンプルなVuexサイクル」です。
Vueコンポーネント層からStore内のstateにアクセスするためには
- store.stateやmapStateを使ってstateを直接する方法
- store.gettersやmapGettersを用いて、取得する方法
があります。
ただ、Stateの変更をどこからでも直接、自由に変更、取得可能だと、全コンポーネント共通で参照可能なStateの秩序を保つことが出来ません。
そのため、公式ドキュメントにも掲載されている以下の図の通り、VuexのライフサイクルをルールとしてStateを変更、取得します。stateの変更はMutationsが行い、vueコンポーネント層からはgetterを使用してStateの値を取得します。
そのMutaitionsはVueコンポーネント層から呼ばれるActionsによって実行されます話がややこしくなってきたのでもう少し詳細にしてみます。
Vue.jsやNuxt.jsとVuexを使ってStateを管理したい。
そんなときに登場してくるのは Actions Mutaitions State Getters この4つです。Vuex それぞれの役割
今回は、Firestoreに保存されているItemsを取得し、それをVuexのStateに格納して、コンポーネント層で使用する例を挙げます。
store/items
以下に
- state.js
- action.js
- mutaition.js
- getter.jsを準備します。
State
Stateでは初期値を格納します。
基本的には配列が入る想定であれば[]、文字列を想定すれば''を初期値にします。
state.jsを用意して以下のように記述するとexport const state = () => { allItems: [] }stateの初期値はから配列になります。
Getters
GettersではStateの値をコンポーネント層で取得する際に使います。
例えば、Stateに格納されているallItemsを取得するなら以下のようにしてexport const getters = () => { getAllItems() { state => state.allItems, } }vueコンポーネントからは
computed: { allItems() { return this.$store.getters['items/getAllItems'] } }のようにして取得できます。
ただこのままだと、allItemsは空配列なのでMutaitionsからStateを更新します。
Mutations
Actionsを定義する前に、Mutationsを用意します。
export const mutations = () => { setAllItems(state, items) { state.allItems = items } }setAllItemsという関数を作ります。
第1引数は値を変更するstateで、第2引数はActionsからもらうitemsとなります。
そのitemsをStateのallItemsの代入することでStateを更新できます。Actions
次にVueコンポーネント層からActionsを呼ぶ必要があるので定義します。
export const actions = () => { async fetch({ commit }) { const docs = await Firestore.collection('items').get() const items = docs.map(doc => doc.data()) commit('setAllItems', items) }, }commit関数の第1引数にMutaitionsの実行したい関数を文字列で指定し、第2引数にFirestoreから取得したitemsを指定します。
コンポーネント層でからは以下のようにActionsを呼ぶことが出来ます。
fetch({store}) { store.dispatch('items/fetchAllItems') }所感
今回はState Getters Mutations Actionsの順で実装しましたが、どこから実装するのかに問題はありません。
名称からどんな役割があってどこからどんなふうに呼び出せば良いのか覚えるまでに時間がかかるかもしれませんが、使っていくうちに分かってきます。ちなみにmentaというコーチングサービスでVue.jsを中心にメンターをしています。
もう少し詳しい内容を聞きたい方や、Vue.jsの勉強に行き詰まっている方など、ぜひmentaでwatsuyo_2にメッセージをしてください!参考
- 投稿日:2020-06-01T21:30:50+09:00
【JavaScript】forEachを用いてDOM操作で作成した要素たちにイベントハンドラーをかける
やりたいこと
①配列の中身である値をforEachで取り出し、値を反映させた<li>要素を作成する。
②<li>要素をクリックすると、値を反映した処理を行う詰まったポイント
①単純にli要素にaddEventListenerをかけても、それぞれの値を反映した処理をしてくれない
②親要素(<ul>)にイベントハンドラをかけても、それぞれの値を反映した処理をしてくれない
実装
<ポイント>
forEachで要素を作成する時に、それぞれのaddEventListenerの処理も記述する!例)
配列の中身をリストとして表示させる。
リストをクリックすると、表示されている値に応じて奇数か偶数か判定する。const numbers = [1, 2, 3, 4, 5]; numbers.forEach(number => { const li = document.createElement('li'); li.appendChild(number); li.addEventListener('click', () => { if(number % 2 === 0){ console.log('偶数') }else{ console.log('奇数') } }) }
- 投稿日:2020-06-01T20:59:07+09:00
Javascriptの復習(3)
この記事について
これはJavascript初学者(現在progateで学習中)がアウトプットの場として書いている記事なので、間違いがあったらご指摘ください。また、Rubyを学習した後なのでRubyと比較して書いているかもしれないです。
アロー関数
関数の名前と言うよりは書き方といったほうがしっくりくると思います。
script.js//これが従来の書き方 const greet = function(){ console.log("こんにちは") }; //"function()"を"()=>"と省略した書き方をアロー関数と言います。 const greet = ()=>{ console.log("こんにちは") };引数
Rubyと同様に関数を呼び出した時に関数に渡す値のことです。同じ関数でも引数によって処理の結果が変わります。
script.js//()の中に引数名を入れます。定数(値)の値が引数名に代入されます。 const greet = (name)=>{ console.log(`こんにちは${name}`) }; //関数の呼び出し(定数名(値)) greet("赤ちゃん"); //コンソール上に"こんにちは赤ちゃん"と出力 greet("さようなら"); //コンソール上に"こんにちはさようなら"と出力戻り値
呼び出した関数の処理の結果を"戻り値"と言います。
script.jsconst add = (a,b)=> { return a+b; //"return"を書くことで、aとbを足した値が戻り値として返される console.log("足し算しました"); //これは実行されない。 }; const sum = add(1,3); console.log(sum);if文で使用するような条件式を関数に書くと真偽値が戻り値として返されます。
script.jsconst search = (a)=> { return a%2 === 0; //returnの値がtrueかfalseかで返される。 }; console.log(search(6)) //trueと表示される。スコープ
関数の引数や関数内の変数及び定数は、その関数内でしか使用できません。関数の外で定義した変数及び定数は関数内でも使用する事ができます。関数のみならず、条件式(if文)や繰り返し文(while文)の{}内の構文にもスコープがあり、構文中で定義した変数及び定数は構文外では使用できない。
script.jsconst greet = ()=> { const name = "太郎"; //この中では定数nameが使用できる。 }; //関数の外では定数nameを使用できない。 const math = "数学"; const subject = ()=> { console.log(math) //関数外で定義しているので定数mathを内外問わず使用できる。 }; const number = 2; if(number%2 === 0) { const value = "偶数"; //この構文中でのみ定数valueを使用できる。 }感想
引数や戻り値、スコープはRubyでも扱われているのですんなり理解できました。アロー関数はプログラミングスクールでも学習していない新規の内容でした。引き続きJavascriptの勉強を進めていきたいです。
- 投稿日:2020-06-01T20:39:18+09:00
【JavaScript】ブラウザでTwitterのタイムラインに自動いいね!
TwitterのTLに表示されているツイートに対して自動いいね
TwitterAPIを使用せずにブラウザ版でJavaScriptを使って自動いいね!します。
※google chromeブラウザを例にします。!!注意!!
実行するとアカウントがBANされる可能性があります。実行は自己責任でお願いします。自動いいね!処理
// 3秒毎に処理を実行する。 setInterval( function () { // いいね!の要素を取得 var elems = document.body.querySelectorAll('div[data-testid="like"]'); // 取得できたいいね!の要素の回数分実行 for (var i = 0; i < elems.length; i++) { // いいね!ボタンをクリック elems[i].click(); }; // 現在位置+3000pxに縦スクロール window.scroll(0,window.scrollY + 3000); }, 3000);実行手順
- Twitterホーム画面を表示する。
- Developer ToolsのConsoleタブを表示する。(windows:Ctrl+Shift+J,Mac:Cmd+Opt+J)
- Consoleに[自動いいね!処理]を入力する。
- 実行すると3秒に9個ずつくらいいいね!されていきます。
- 投稿日:2020-06-01T20:31:32+09:00
fetchを用いてAPIからJSONデータを取得しDOM操作に利用する
fetch()とは
非同期のネットワーク通信を簡単にわかりやすく記述できるメソッド。クライアント側のJavaScritから、HTTPと通信しデータを取得することができるようになる。
(参考:MDN web docs 『Fetchを使う』 https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch )fetchで取得したデータからJSONを取得しDOM操作に利用する
例)以下のデータ構造を持つJSONデータから、nameとageを取得し画面に表示させる
{
"results":[
{ "name": "太郎",
"age": 20},
{ "name": "次郎",
"age": 25},
{ "name": "三郎",
"age": 30},
]
}const url = //任意のAPIのhttpアドレス //操作したいHTML領域を取得 const name = document.getElementById('name'); const age = document.getElementById('age'); //APIからJSONデータを取得する fetch(url) .then((response) => { return response.json() //ここでBodyからJSONを返す }) .then((result) => { Example(result); //取得したJSONデータを関数に渡す }) .catch((e) => { console.log(e) //エラーをキャッチし表示 }) }) //JSONデータを引数に受け取ってDOM操作を行う関数を作成 function Example(jsonObj){ const data = jsonObj.results[0] name.textContent = data.name; age.textContent = data.age; }ポイント1
then()メソッドを利用してfetch()の非同期処理を記述することにより、ネストが深くなるのを避けることができる。
エラーの処理も、例外処理であるcatch()を利用することで、チェーンとしてメソッドを繋ぐことができる。
(参考:MDN web docs 『Promise.prototype.then()』
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)ポイント2
fetchで取得してくるデータそのものはPromiseオブジェクトである。
const data = fetch() .then((response) => { return response.json() //ここでBodyからJSONを返す }) .then((result) => { Example(result); //取得したJSONデータを関数に渡す }) .catch((e) => { console.log(e) //エラーをキャッチし表示 }) }) console.log(data); //コンソールに"Promise"と表示されるfetchで持ってくる値そのものはPromiseオブジェクト。DOM操作に利用したいのは、取得したJSONオブジェクトそのもの。
そのため、fetch()メソッドの中に、JSONデータを渡す関数を記述し、引数としてJSONデータを渡す必要がある。
- 投稿日:2020-06-01T20:31:32+09:00
【JavaScript】fetchを用いてAPIからJSONデータを取得しDOM操作に利用する
fetch()とは
非同期のネットワーク通信を簡単にわかりやすく記述できるメソッド。クライアント側のJavaScritから、HTTPと通信しデータを取得することができるようになる。
(参考:MDN web docs 『Fetchを使う』 https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch )fetchで取得したデータからJSONを取得しDOM操作に利用する
例)以下のデータ構造を持つJSONデータから、nameとageを取得し画面に表示させる
{
"results":[
{ "name": "太郎",
"age": 20},
{ "name": "次郎",
"age": 25},
{ "name": "三郎",
"age": 30},
]
}const url = //任意のAPIのhttpアドレス //操作したいHTML領域を取得 const name = document.getElementById('name'); const age = document.getElementById('age'); //APIからJSONデータを取得する fetch(url) .then((response) => { return response.json() //ここでBodyからJSONを返す }) .then((result) => { Example(result); //取得したJSONデータを関数に渡す }) .catch((e) => { console.log(e) //エラーをキャッチし表示 }) }) //JSONデータを引数に受け取ってDOM操作を行う関数を作成 function Example(jsonObj){ const data = jsonObj.results[0] name.textContent = data.name; age.textContent = data.age; }ポイント1
then()メソッドを利用してfetch()の非同期処理を記述することにより、ネストが深くなるのを避けることができる。
エラーの処理も、例外処理であるcatch()を利用することで、チェーンとしてメソッドを繋ぐことができる。
(参考:MDN web docs 『Promise.prototype.then()』
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)ポイント2
fetchで取得してくるデータそのものはPromiseオブジェクトである。
const data = fetch() .then((response) => { return response.json() //ここでBodyからJSONを返す }) .then((result) => { Example(result); //取得したJSONデータを関数に渡す }) .catch((e) => { console.log(e) //エラーをキャッチし表示 }) }) console.log(data); //コンソールに"Promise"と表示されるfetchで持ってくる値そのものはPromiseオブジェクト。DOM操作に利用したいのは、取得したJSONオブジェクトそのもの。
そのため、fetch()メソッドの中に、JSONデータを渡す関数を記述し、引数としてJSONデータを渡す必要がある。
- 投稿日:2020-06-01T20:11:52+09:00
JavaScriptオブジェクトのプロパティへのアクセス方法
概要
JavaScriptオブジェクトについてのまとめ
参考:https://jsprimer.net/basic/object/オブジェクトとは
オブジェクトはプロパティの集合です。プロパティとは名前(キー)と値(バリュー)が対になったものです。 プロパティのキーには文字列またはSymbolが利用でき、値には任意のデータを指定できます。 また、1つのオブジェクトは複数のプロパティを持てるため、1つのオブジェクトで多種多様な値を表現できます。
こういうやつ
const obj = { hoge:'huga', foo:'bar' };プロパティへのアクセス方法
オブジェクトはプロパティの集合なので、プロパティにアクセスしましょう。
アクセス方法は以下の2通りですドット記法
オブジェクトに対してドットで繋げる記法。
ドット記法では、変数名と同じく識別子の命名規則を満たす必要がある。
具体的には以下の通り。const obj = { hoge:'huga', 123:456, 'my-house':'house' }; console.log(obj.hoge); // OK console.log(obj.123); // NG (数字から始まっているため) console.log(obj.my-house); // NG (ハイフンを含んでいるため)ブラケット記法
[]
の間に任意の式を書く。
プロパティ名は文字列へと暗黙的に変換される。
具体的には以下の通りconst obj = { hoge:'huga', 123:456, 'my-house':'house' }; const is_hoge = 'hoge'; console.log(obj['hoge']); // 'huga' console.log(obj[123]); // 456 console.log(obj['my-house']); // 'house' console.log(obj[is_hoge]); // 'huga'オブジェクトと分割代入
何度もプロパティを指定するのはめんどくさいので、プロパティを変数として定義できる。
const obj = { hoge:'huga', number:456, house:'house' }; const a = obj.hoge; const b = obj.number; const c = obj.house; console.log(a) // 'huga' console.log(b) // 456 console.log(c) // 'house'上記のように変数にプロパティを代入する場合は、分割代入を使用すると短く書ける。
const obj = { hoge:'huga', number:456, house:'house' }; const {hoge, number, house} = obj; console.log(hoge) // 'huga' console.log(number) // 456 console.log(house) // 'house'プロパティ名と変数名が一致していないとできないので注意です。
- 投稿日:2020-06-01T19:40:51+09:00
React[基礎知識編]
Reactとは
世界的に有名なJavaScriptのライブラリです。
Reactはサイトの見た目の部分を作ることができ、
Facebook、Airbnb、Dropboxなどの有名企業も多く使用しています。雛形
// Reactをimport import React from 'react'; // React.Compornentを継承するクラスの定義 class アプリ名 extends React.Compornent { constructor(props) { super(props); this.state = {変数: 値} } イベント名() { } // JSXを戻り値とするrenderメソッドを定義 render() { return ( この中でJSXを記述する。 ) } } // クラスをexport export default アプリ名;JSXとは
Reactで使うHTMLのことを言います。ほぼHTMLと同じですが呼び方が違います。
複数の要素がある場合には < div >タグを使う。
render() { return ( <h1>見出し1</h1> <h2>見出し2</h2> // <img>タグには閉じタグ(/)が必要 <img src = '----'/> ) }イベントの書き方
イベント名 = {() => { 処理 }} とすることでイベントを設定できます。
<button onClick = {() => { 処理 }}stateとは
ユーザーの動きに合わせて変わる値のことをいいます。
stateは、constructorの中で、オブジェクトとして定義します。
ここで定義したオブジェクトがstateの初期値となります。
その他の部分の、定型文として覚えておけば大丈夫です。constructor(props) { super(props); this.state = {変数: 値} }stateの表示
this.state.プロパティ名とすることで、指定したstateのプロパティ名に対応する値を取得できます。
constructor(props) { super(props); this.state = {name: '田中'} } render() { return ( <h1>こんにちは、{this.state.name}さん!</h1> ) }stateの変更
this.setState({プロパティ名: 変更する値})とすることで、指定されたプロパティに対応するstateの値が変更される。
Reactでは、「stateの値に直接代入することで値を変更してはいけない」という決まりがあります。
値を変更したい場合は、setStateを使いましょう。<button onClick = {() => {this.setState({name: '佐藤'})}}>佐藤</button>コンポーネントとは
コンポーネントは「部品」や「パーツ」という意味です。
Reactでは、見た目を機能ごとにコンポーネント化して、コンポーネントを組み合わせることでWebサイトの見た目を作ります。
クラス名がコンポーネント名となり、下記の場合はLanguageがコンポーネント名となる。Language.jsimport React from 'react'; class Language extends React.Component { render() { return( JSXの記述 ); }コンポーネントの表示
1.作成したコンポーネントをexportする
Language.jsexport default Language;2.App.jsで、コンポーネントをインポートし、JSX内に記述する。
App.jsimport React from 'react'; // コンポーネントをインポート import Language from './Language.js'; class Language extends React.Component { render() { return( // JSX内に<コンポーネント名 />を記述する <Language /> ); }propsとは
propsは、「props名=値」という形で、コンポーネントを呼び出す箇所で渡します。
タグの中身は、改行してあげることで見やすくなります。
渡されたpropsは、this.propsで取得できます。
this.propsは{ props名: 値}というオブジェクトになります。<Language prop名 = 値 />propsの取得
this.propsと書くことで{props名: 値}というオブジェクトを取得できるので、「this.props.props名」とすることでpropsの値を取得できます。
<div className='language-item'> <div className='language-name'>{this.props.name}</div> <img className='language-image' src={this.props.image} /> </div>mapメソッドを利用する
mapメソッドで配列fruitListの各要素に対して順に処理を行い、各要素を < p >タグで囲んで表示しています。mapメソッドの戻り値はJSXなので、引数であるfruitItemは中括弧{}で囲むことに注意しましょう。
{languageList.map((languageItem) => { return ( <Language name = {languageItem.name} image = {languageItem.image} /> ) })}コンポーネントが表示される流れ
コンポーネント
↓jsx
App.js
↓jsx
index.js
↓html
index.html
index.jsの中身は以下の記述を記載する。
index.jsimport App from './compornents/App'; ReactDOM.render(<App />,document.getElementById('root');index.htmlの中身は以下の記述を記載する。
<div id = 'root'> // 指定したid名の要素の中に挿入される </div>
- 投稿日:2020-06-01T18:50:55+09:00
【Rails5】[ Turbolinks ] ページ遷移やブラウザバックでJSが動かないときの対処法
実装した機能
開発環境
ruby > 2.6.5 rails > 5.2.4.2実装したJS
$(document).ready(function(){ $("#menu").on("click", function() { $(this).next().slideToggle(); }); });jqueryを使って、なんの変哲もない開閉式ハンバーガーメニューをつけました。
JSのコード自体の良し悪しはおいておいて、動作としては問題ないはずです。状況
初期ロード時には問題なく動作する。
ページ遷移、ブラウザバックのときに挙動がおかしい。
クリックで発火はしているが、開閉をループしたり、不安定。
リロードすると通常動作する。考察と対策
動いてはいるので、おそらく読み込みのタイミングが間違っている?
→ready
onload
ajaxStop
など一通り試してみてもダメ。グーグル先生に相談したら、こんな記述を発見
$(document).on('turbolinks:load', function () { ... });
turbolinks:load
なにこれ見たことない…
どうやらRails独自の記述らしいです。Turbolinksの扱い
こちらの記事を参考にさせていただきました。
turbolinksチートシートTurbolinksとは?
- Ajaxによるページ遷移の高速化のためのライブラリ(Gem)
- ユーザ側から見て、通常のページ遷移と同じように表示される/動作する
- Rails4からデフォルトでインストールされている
つまり、この機能が今回のJSに影響してしまっているようです。
turbolinksをどう扱うか
Gemなので、消してしまえば解消はできますが解決にはならないので
どう扱うべきかリサーチしてみました。主にこのような扱いがあります。
- <a>タグごとにturbolinksを無効にする
- turbolinks自体を無効化(削除)する
- JSの読み込み時にturbolinksを適応しない、タイミングを変える
1.<a>タグごとにturbolinksを無効にする
リンク自体に
{"turbolinks" => false}
を指定すると
そのリンクはturbolinksが無効になります。<%= link_to "HOGE", root_path, data: {"turbolinks" => false} %> <%# => <a data-turbolinks="false" href="/">HOGE</a> %>これを記述すれば、間違いなくturbolinksを外すことができます。
特定のスクリプトのみ制御する場合は良さそうですが、さすがに全部に記述するのは厳しそうですね…2.turbolinks自体を無効化(削除)する
Gemを削除
この一行を削除
Gemfile#gem 'turbolinks', '~> 5'
ターミナル$ bundle updateapplication.jsを編集
app/assets/javascripts/application.js//= require turbolinks #この行を削除
application.html.erbを編集
'data-turbolinks-track': 'reload'
を削除します。app/views/layouts/application.html.erb<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>これで無効化されました。
JSなどを多用するサイトでなければ無効化してしまうのが確実かもしれません。3.JSの読み込みにturbolinksを適応しない
ready
onload
などと同じように、
この記述でturbolinksを適応せずにロードできます。$(document).on('turbolinks:load', function () { ... });他にも、turbolinksを適応するタイミングも変更ができます。
詳しく知りたい方はこちらを参照ください。
その他のライフサイクルイベントをとるスクリプトごとに微調整が効くので、今回はこれが最適解だと思います。
まとめ
果たして、Turbolinksは優れた機能なのか、おせっかい機能なのか…
今の所どちらとも言えません笑
デフォルトでインストールされているということは、きっとあったほうが良いのだろうと思いますが…
もっと効果的な使用法をご存じの方はぜひコメントを下さい!
- 投稿日:2020-06-01T18:02:23+09:00
Javascriptでクリップボード書き込み
※CodePenでは失敗します。
See the Pen Clipboard Write by 匠君 (@takumikunn15) on CodePen.
- 投稿日:2020-06-01T17:01:43+09:00
定番の顔メッシュ貼り替えを、ブラウザで動くfacemeshで作ってみた
顔メッシュ貼り替えといえば、iPhoneのFace Trackingですね。
今年の3月に登場したGoogleのfacemeshでもそこそこ似たようなことができます。iPhoneのFace Trackingから早3年。顔メッシュ貼り替えは廃れたネタではありますが、今回はThree.jsで顔メッシュ貼り替えを試しました。
昨夜作り始めて割とさくっとできました。
誰得ではありますがイラスト屋さんの顔になれます。https://t.co/BNmTQ1wa5O#facemesh pic.twitter.com/wyTayzUfO7
— o2 (@mb_otsu) June 1, 2020Three.jsでの顔メッシュ生成
実装のコア部分となるThree.jsの顔メッシュですが、facemeshの推論の戻り値と、TRIANGULATIONのindexを混ぜ合わせると顔のメッシュは作れます。
// facemeshの戻り値 var keypoints = prediction.scaledMesh; var texture = new THREE.CanvasTexture(canvas); texture.flipY = false; mesh.geometry.vertices = new Array(keypoints.length); for (let i = 0; i < keypoints.length; i++) { const [x,y,z] = keypoints[i]; mesh.geometry.vertices[i] = new THREE.Vector3(x,y,z); } mesh.geometry.faces = new Array(TRIANGULATION.length / 3); for (let i = 0; i < TRIANGULATION.length / 3; i++) { let id0 = TRIANGULATION[i*3+0]; let id1 = TRIANGULATION[i*3+1]; let id2 = TRIANGULATION[i*3+2]; mesh.geometry.faces[i] = new THREE.Face3(id0,id1,id2); // uvはcanvasのサイズに合わせて正規化 let uv = [ new THREE.Vector2(keypoints[id0][0] / videoWidth, keypoints[id0][1] / videoHeight), new THREE.Vector2(keypoints[id1][0] / videoWidth, keypoints[id1][1] / videoHeight), new THREE.Vector2(keypoints[id2][0] / videoWidth, keypoints[id2][1] / videoHeight), ]; mesh.geometry.faceVertexUvs[0][i] = uv; } mesh.material = new THREE.MeshBasicMaterial({ map: texture , side: THREE.DoubleSide });実装ではcanvasの画像を変更すると他の顔のメッシュも自動生成できますのでお試しください。
オンラインデモ
https://mbotsu.github.io/facemask/
コード
https://github.com/mbotsu/facemask
その他
よろしければこちらも合わせてご覧ください。
TensorFlow.jsのfacemeshで顔向き推定を試してみた その2
- 投稿日:2020-06-01T16:57:00+09:00
イベントデータの設定
document.getElementById('form').select.onchange = function(){onchangeイベント:フォーム内容が変わった時に発生する
部品を設定するにはname属性で取得をするdocument.getElementById('form').select❸valueをつくる
document.getElementById('form').select.valueプルダウンメニューの場合は親要素のvalueを調べに行ってくれる
❹location.hrefに代入する
location.href = document.getElementById('form').select.value; location.href = 新しいURL
- 投稿日:2020-06-01T15:56:16+09:00
axios-hooksで楽々非同期処理!reduxにthunkもsagaもいらない?
はじめに
最近暇な時でGraphQLを触っていて、フロントエンドでApollo-clientをはじめて使ったんですが、
すごく使い勝手がいいhooksがあって(多分GraphQL分からなくても伝わるかなぁと):// Apollo公式より抜粋 import gql from 'graphql-tag'; import { useQuery } from '@apollo/react-hooks'; const GET_DOGS = gql` { dogs { id breed } } `; function Dogs({ onDogSelected }) { const { loading, error, data } = useQuery(GET_DOGS); // これです if (loading) return 'Loading...'; if (error) return `Error! ${error.message}`; return ( <select name="dog" onChange={onDogSelected}> {data.dogs.map(dog => ( <option key={dog.id} value={dog.breed}> {dog.breed} </option> ))} </select> ); }これ凄くないか?
非同期処理を一行で終わらせてるぞ。そこで考えたんですが、reduxのプロジェクトで使えたらいいなと。
…ん?待てよ。非同期関連のaxiosを使う人が山ほどいて、
同じことを考える人絶対いるんでしょ!?調べたらいました、めちゃくちゃいました、山ほどいました(※そこまではいないです)。
やったぜ。axios-hooks
さて、今回紹介するのはその一つ:axios-hooksです
なぜこれを選んだのは単純に見比べてほしい機能が一番揃ってるからです。(自分なりに)
以下の例は大体公式のものです。一番シンプルな例:
import useAxios from 'axios-hooks' function App() { const [{ data, loading, error }, refetch] = useAxios( 'https://api.myjson.com/bins/820fc' ) if (loading) return <p>Loading...</p> if (error) return <p>Error!</p> return ( <div> <button onClick={refetch}>refetch</button> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ) }useAxios(url|config, options)
引数
- url | config - configはaxiosのconfig objectです、なので基本的にaxiosで設定できるものであれば使えます。
- options - 手動実行とキャッシュの設定です
- manual - デフォルトは
false
です。true
に設定すればdidMountの時は自動実行しない。大体GETはfalse
でCUD(Create, Update, Delete)ではtrue
で使うはず。- useCache ( true ) - デフォルトは
true
です。Return object
[{ data, loading, error, response }, execute]
- data - axiosのresponse.dataです。
- loading - 名前の通り
pending
中ではtrue
です.- error - axios error object
- response - axios response object
- execute([config[, options]]) - 手動実行用のfunctionです、引数は基本的に前のconfig、optionsと同じです。
もっと完全な例
自分で書いたものですが、注釈は英語になってます、時間ができたら日本語に直したいと思います。
テスト用APIはwww.mocky.io使わせていただきました。
試したいであれば公式のCodePenにコピペすればできると思います。
(ReactDOMのインポートと下のrender部分は残ってください。)import React from 'react'; import Axios from 'axios'; import useAxios, {configure} from 'axios-hooks' // Define axios instance, you could use env to split up production & development // The useAxios provides directly by axios-hooks use the same instance // If you need more than one axios instance, explain later const axiosInstance = Axios.create({baseURL: 'https://www.mocky.io/v2'}) configure({axios: axiosInstance}) // You should define your api url somewhere in your project const api = { getMail: { url: () => '/5ed0a6ea3500005d00ff9d7b?mocky-delay=2000ms', method: 'GET'}, putMail: { url: (id) => `/5ed0a49e3500009300ff9d6b/${id}`, method: 'POST'} } function App() { // The example to get some data // Execute once the component did mount const [{data = {iam: 'default data'}, loading, error}] = useAxios({ url: api.getMail.url(), method: api.getMail.method, data: {haha: "yes"} }) // The example to CUD data // Pass { manual: true } into useAxios, then it won't execute when component did mount const [{data: updatedData}, updateData] = useAxios({method: api.putMail.method}, {manual: true}) return ( <div > {error && <div>error</div>} {data && <div>{`${JSON.stringify(data)} ${loading?'loading':''}`}</div>} {updateData && <div>{JSON.stringify(updatedData)}</div>} <button onClick={() => { // You can set the common authorization header like this way axiosInstance.defaults.headers.authorization='test101' // Example to update data updateData({url: api.putMail.url('myid'), data: {thedata: 'you want to put'}, headers: {'another-header': 'test202'}}) .then((data) => {console.log(data)}) // Use it by the way you want, even with redux store }}> test </button> </div> ); } // If you need more than one axios instance, set up with makeUseAxios & export it // const anotherInstance = Axios.create({baseURL: 'http://some.another.api.url'}) // export const useAnotherAxios = makeUseAxios({axios: anotherInstance}) export default App;終わりに
正直
redux-saga
を使ったことないので、タイトルではクエスチョンマークを付けてます、ごめんなさい。
余談ですが公式では:axios-hooks
is heavily inspired bygraphql-hooks
と記載しています。
同じgraphql
からの発想で作ったみたいで嬉しいです。CodePenと注釈は時間ができたら直します。
- 投稿日:2020-06-01T13:27:03+09:00
[LINE BOT]FizzBuzz体験BOT
目次
- はじめに
- サンプルコード
- サンプル画像と使用例
- おわりに
- 参考にしたサイト
はじめに
今回は、LINE BOTでFizzBuzzをやってみた。
なお、当LINE BOTを作成する際に、この記事をベースにした。サンプルコード
'use strict'; // 使用パッケージ群 const express = require('express'); // Node.jsで利用できるWebアプリケーションフレームワーク const line = require('@line/bot-sdk'); // ボットサーバへのリクエストが当LineBOTからきたものかどうかを検証してくれる(要は署名検証) const PORT = process.env.PORT || 3000; //Node.jsアプリケーションを使用するポートを設定 // LineBOT用定数群 const config = { channelSecret: 'YourChannelSecret', channelAccessToken: 'YourChannelAccessToken' }; 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); async function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return client.replyMessage(event.replyToken, { type: 'text', text: "数字を入力してください。" }); } else { if ((event.message.text % 3 === 0) && (event.message.text % 5 === 0)) { // LineBOTに返信されるメッセージを設定 return client.replyMessage(event.replyToken, { type: 'text', text: 'FizzBuzz' // LineBOTに返信されるメッセージ }); } else if (event.message.text % 3 === 0) { return client.replyMessage(event.replyToken, { type: 'text', text: 'Fizz' }); } else if (event.message.text % 5 === 0) { return client.replyMessage(event.replyToken, { type: 'text', text: 'Buzz' }); } else { return client.replyMessage(event.replyToken, { type: 'text', text: event.message.text }); } } }; app.listen(PORT); console.log(`Server running at ${PORT}`);サンプル画像と使用例
おわりに
次は、VercelでLINE BOTを動かす 2020年5月版を参考に、Vercelを介してLINE BOTを動かしてみようと思う。
参考にしたサイト
- 使用したパッケージ群について
Node.jsのフレームワーク「Express」とは【初心者向け】
LINE BOTをHeroku + Node.jsでやるまで
- webhook について
- 投稿日:2020-06-01T13:11:58+09:00
クリップボードに貼り付けた画像を取得する javascript
初投稿です。とりあえずメモ変わりに。
間違ってる所、改善点などあればどんどん突っ込んで下さい。目的
画像ファイルの読み込み方法に、クリップボードから取得できれば便利だと思ったので調べてみた。
コード
div contentditable要素にコピーした画像を貼り付けてimg要素に表示する処理。
html<div id="box" contenteditable="true">画像ペーストエリア、</div> <img src="" alt="" id="image">jsvar blob2=null; $("#box").on("paste",function(e) { var items = (event.clipboardData || event.originalEvent.clipboardData).items; for (var i = 0; i < items.length; i++) {//クリップボードに画像があるか探してる if (items[i].type.indexOf("image") === 0) { blob2 = items[i].getAsFile(); } }//for if (blob2 !== null) {//方法1 //blob2に画像がある場合の処理画像url作成処理 var blobURL = URL.createObjectURL(blob2); $("#image").attr("src",blobURL); }else{//方法2 //boxへ貼り付けられた画像をdomで取得する方法 setTimeout(fn, 1000);//1秒待ってから実行する。 } }); var fn = function() { var p = document.querySelector("#box"); var pa = p.querySelector("img"); if (pa) { test(pa.src); }else{ //この方法でも取得できなければ失敗 alert("画像を取得できませんでした。"); } } function test(pasrc) {//urlの形式判別、base64形式とhttp形式に別れる if (pasrc.match(/^data:image/)) { $("#image").attr("src",pasrc); } else if (pasrc.match(/^http:\/\/|^https:\/\//)) { $("#image").attr("src",pasrc); } }説明
方法1はクリップボードから取り出すのでblobとして存在している。画像形式は全てPNGとなっている。(mac)の場合、他は未検証
方法2は、div contenteditable="true"を使いdiv要素に貼り付けられた画像を取得している。元のurlの状態で取得できる、「http」か「base64画像」に分別されると思う。
ほとんどの場合方法1で取得できた。ウェブ上でコピーできる画像ならなんでも対応できた。
検証したブラウザは safari chrome firefox 環境はmacです。
参考サイト
https://qiita.com/tatesuke/items/00de1c6be89bad2a6a72
https://www.vrtmrz.net/javascript/contenteditable-paste-imageデモ
一応デモ、ごちゃごちゃしてるかも
取り敢えず一応動くというのを確認する用
https://jsbin.com/cadajireli/1/edit?html,js,output右上の auto run jsにチェック入れて動かして下さい。
- 投稿日:2020-06-01T13:05:16+09:00
非同期通信の手順書
今回の前提条件
コメント機能を非同期通信にするという前提で書きます
ファイルを用意しましょう
JS内にcomment.jsを用意しましょう。
ここにajaxの記述などを書いていくのでした。
ajaxって何?comment.jsに記述していこう
まずは以下のようにします。
$(function(){ $('クラス名').on('submit', function(e){ e.preventDefault(); var formData = new FormData(this); }) })今回はコメント機能なので、form要素のクラス名を記入します。そしてonメソッドでイベントセッティングをしましょう。
そしてpreventDefaultで元々のイベントを止めることができます。
今回、ここで新しくFormDataが出てきました。FormData
簡単にいうとformタグ内の要素のデータをJSのオブジェクトにすることです。
あ、JSにするために FormDataがあるんだ!って思っても大丈夫です。
今回引数にthisを入れてるのはinput要素の内容を取得するためです。ajaxを書いていこう
var url = $(this).attr('action') $.ajax({ url: url, type: "POST", data: formData, dataType: 'json', processData: false, contentType: falseattrメソッド
要素が持つ指定属性の値を返します。
contentType
簡単にいうと、 FormDataを使用している時は必ずfalseにします。
この認識で問題ないです。次はcreateアクションをいじっていきます。
- 投稿日:2020-06-01T12:29:28+09:00
Javascript 初めてのGSAPアニメーションの使い方 その1
フロントの技術強化のためのGSAPアニメーションを基礎から紹介します。
GSAPとは
GSAP(GreenSock Animation Platform)
GSAP(GreenSock Animation Platform)は、高速・軽量のHTML5アニメーションライブラリで、アニメーションライブラリのデファクトスタンダードといえる程多くのサイトで利用されています。
主なモジュールとして、トゥイーンを実現するTweenMaxと、タイムラインを制御すTimelineMaxの2種があります。また、TweenMaxと、TimelineMaxのそれぞれの簡易版の2種があります。
準備
今回は以下のディレクトリ構成で進めます。
GSAPの読み込みはCDNを使用します。
記事作成時点のバージョンは3.2.6です。・index.html
・/styles/style.css
・/scripts/main.js各シートを読み込んで準備しましょう。
今回の制作物
Tweenmax.to
Tweenmax.toは指定したプロパティーに向かって要素がアニメーションします。
赤い丸→青に変わりながら1秒かけて右と下に150px移動
緑の四角→丸のアニメーション終了後(1秒後)左と上に150px移動しながら2倍のサイズになりバウンスする<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="styles/style.css" /> <title>Document</title> </head> <body> <div class="circle"></div> <div class="square"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script>//GSAPの読み込み <script src="scripts/main.js"></script> //JSシートの読み込み </body> </html>body{ //要素を画面中心に配置 display: flex; justify-content: center; align-items: center; height: 100vh; } .circle{ //赤い丸 background-color: orangered; height: 150px; width: 150px; border-radius: 50%; } .square{ //緑の四角 width: 150px; height: 150px; background-color: green; }ここからjsを記述します。
TweenMax.to('.circle', 1, { x:150, y:150, backgroundColor: 'blue' }); //赤丸(class=".circle")をx軸,y軸へ150px移動と色を青に指定 TweenMax.to('.square', 3, { x: -150, y:-150, scale: 2, delay: 1, ease: Back.easeOut }); //四角(class=".square")をx軸y軸へ150px移動と1秒後に2倍に拡大してeaseOutでバウンスする基本の記述は
tweenmax.to( '取得したい要素' , { アニメーションのプロパティ });
です。プロパティは改行しても動きますのでお好みで選んでください。
個人的には1ライナーで書いたほうが見やすいかなと思います。
次回はTween.fromとJqueryとの連携です。
- 投稿日:2020-06-01T12:08:28+09:00
Laravel MixでVue.js+Bootstrapの簡単なログインフォームを作る。
FIRST PLAN株式会社のフロントエンドエンジニアtakeです。
sampleText
さて、今回はテストでログインフォームを作ったので良ければご覧ください。
こんな事をやりました
環境設定
まずはVue.js。
今回はLaravel内で使っているので、LaravelでVue.jsを使えるようにするには以下のコマンドを実行します。$ composer require laravel/ui $ php artisan ui vue --authすぐにvueを始めたい場合は公式ドキュメントにある通りCDNでも使えますし、初めからwebpackの環境構築がされている「Vue CLI」もあるので活用してみてください。
ただ今回は環境設定の記事ではないのでそこら辺は割愛します。そして次にBootstrapですが、BootstrapVueを使用します。
機能は同じでBootstrapの記法もそのまま使えるのですが、jQuery依存だったコンポーネントがVueのディレクティブが使えるように拡張されています。$ npm install bootstrap-vue bootstrapでインストールし、
以下のコードをメインのjsファイルに追加します。import BootstrapVue from 'bootstrap-vue'; Vue.use(BootstrapVue);後は好みの問題なのですが、FontAwesomeとvue-sweetalert2をインストールします。
vue-sweetalert2もBootstrapVueと同じで機能は元のsweetalert2と変わりませんが、Vueでそちらを使うとアイコン部分で表示崩れが起きる事があるのでそれを修正したものになります。
Bootstrapと同様にこちらもインストールします。FontAwesome
$ npm install --save @fortawesome/fontawesome-freeimport '@fortawesome/fontawesome-free/css/all.css';vue-sweetalert2
$ import VueSweetalert2 from 'vue-sweetalert2';import 'sweetalert2/dist/sweetalert2.min.css'; Vue.use(VueSweetalert2);コード
HTML
今回は以下のような感じで、
card
とinput-group
を使って実装しました。HTML<template> <section class="login-form d-flex flex-solumn justify-content-center align-items-center w-100 text-center"> <div class="login-form__field card rounded-0 pt-5 pb-3"> <i class="login-form__budge fas fa-user-circle text-primary bg-white rounded-circle"></i> <div class="card-body"> <h1 class="login-form__heading card-title text-primary font-weight-bold mb-0">Sign in</h1> <hr class="bg-primary mt-0 mb-4"> <p class="card-text text-muted mb-4">ユーザー名とパスワードを入力してください。</p> <div class="input-group mx-auto mb-4"> <div class="input-group-prepend"> <span class="input-group-text bg-primary text-white" id="basic-addon1"> <i class="fas fa-user"></i> </span> </div> <input class="form-control" type="text" placeholder="Username" v-model="login.user_name"> </div> <div class="input-group mx-auto mb-4"> <div class="input-group-prepend"> <span class="input-group-text bg-primary text-white" id="basic-addon1"> <i class="fas fa-key"></i> </span> </div> <input class="form-control rouded-0" type="password" placeholder="Password" v-model="login.password"> </div> <button class="login-form__button btn btn-primary w-75 mb-4" type="button" :disabled="checkInput" @click="signIn">Login </button> <div class="login-form__attention d-flex justify-content-between my-o mx-auto"> <div class="custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" id="remember-me" v-model="login.remember"> <label class="custom-control-label" for="remember-me">情報を記憶する</label> </div> <a class="card-link" href="#"> <u>パスワードを忘れた方はこちら</u> </a> </div> </div> </div> </section> </template>CSS
飽くまでBootstrapベースなので、scssでBEMを採用して影のエフェクト以外は軽く整える程度にしました。
scss.login-form { &__field { position: relative; max-width: 30rem; //影エフェクト &:after { content: ''; position: absolute; bottom: 20px; right: 55px; z-index: -1; width: 50%; height: 100px; box-shadow: 100px 0 10px 15px rgba(0, 0, 0, .3); transform: skew(-40deg); } } //__field &__budge { position: absolute; top: -2rem; left: 50%; font-size: 6rem; transform: translateX(-50%); } //__budge &__attention { font-size: 0.6rem; } //__attention &__button { border-radius: 0!important; } //__button } // .login-formJavaScript
以下がデフォルトのVueの記述方法になります。
ざっくり説明すると
data
は変数等を格納する場所・computed
は変更があるとリアルタイムで値を再計算してくれる場所・methods
は文字通りメソッドを格納する場所です。
他にもwatch
やライフサイクルフック等開発に便利な機能が色々とあるので、詳しくは公式ドキュメントをご参照ください。さて、まず
data
内のlogin
は「ユーザーネーム・パスワード・ユーザー情報を記憶するか否か」を保持しています。
html内にv-model="login.user_name"
といった記述があると思いますが、このv-model
によって双方向データバインディングを実現しています。
どちらかが変更された段階でもう片方にも自動で変更が同期されるため、自分で逐一document.querySelector(element)~
をする必要がなくなります。次は
computed
ですが、今回はlogin.user_name
とlogin.password
が条件に組み込まれていますが、どちらかの値が変更された段階で自動で値を更新します。
メソッド内にif (!this.checkInput)
と指定されている関数がありますが、このように使用する事で他の場所で毎回入力を確認する必要がなくなります。
ちなみにbutton
にある:disabled="checkInput"
にも使用されていますが、まずこの:
マークはv-bind
といってhtml内の属性(class
やhref
等)に付ける事で内部でJavaScriptのコードを直接使用可能にするものです。
今回はこれらを利用してdisabled
の値を入力の有無によって動的に変更しています。最後に
methods
は一番簡単で、関数を格納する場所になります。
とりあえず確認用にv-model
で同期した値をsweetalert2で表示しています。
イベントハンドリングはhtml側で@event="hoge"
のように行います。(今回の場合は@click="signIn"
)
ちなみにこの@click
以下はコンパイル時に削除されるため、JavaScriptのコードでhtmlを汚染したくない方にも安心です。JavaScriptexport default { data() { return { //ログイン情報 login: { user_name: '', password: '', remember: false } } }, computed: { //ユーザーネームとパスワードの両者が入力されている事を判定する値、自動で変更を検知し反映してくれる checkInput() { return (!this.login.user_name || !this.login.password) ? true : false; } }, methods: { signIn() { //ユーザーネームとパスワードの両者が入力されていた時のみ発火する。 if (!this.checkInput) { //v-modelで同期した値を出力する this.$swal({ icon: 'info', title: 'Information', text: ` ID: ${this.login.user_name}, Password: ${this.login.password}, Remember: ${this.login.remember} ` }) } return; } //signIn } }最後に
$ npm run dev
でコンパイルして確認すると...
キチンと動いています。
よろしければ参考にしてみて下さい。
- 投稿日:2020-06-01T12:08:28+09:00
Lravel MixでVue.js+Bootstrapの簡単なログインフォームを作る。
FIRST PLAN株式会社のフロントエンドエンジニアtakeです。
sampleText
さて、今回はテストでログインフォームを作ったので良ければご覧ください。
こんな事をやりました
環境設定
まずはVue.js。
今回はLaravel内で使っているので、LaravelでVue.jsを使えるようにするには以下のコマンドを実行します。$ composer require laravel/ui $ php artisan ui vue --authすぐにvueを始めたい場合は公式ドキュメントにある通りCDNでも使えますし、初めからwebpackの環境構築がされている「Vue CLI」もあるので活用してみてください。
ただ今回は環境設定の記事ではないのでそこら辺は割愛します。そして次にBootstrapですが、BootstrapVueを使用します。
機能は同じでBootstrapの記法もそのまま使えるのですが、jQuery依存だったコンポーネントがVueのディレクティブが使えるように拡張されています。$ npm install bootstrap-vue bootstrapでインストールし、
以下のコードをメインのjsファイルに追加します。import BootstrapVue from 'bootstrap-vue'; Vue.use(BootstrapVue);後は好みの問題なのですが、FontAwesomeとvue-sweetalert2をインストールします。
vue-sweetalert2もBootstrapVueと同じで機能は元のsweetalert2と変わりませんが、Vueでそちらを使うとアイコン部分で表示崩れが起きる事があるのでそれを修正したものになります。
Bootstrapと同様にこちらもインストールします。FontAwesome
$ npm install --save @fortawesome/fontawesome-freeimport '@fortawesome/fontawesome-free/css/all.css';vue-sweetalert2
$ import VueSweetalert2 from 'vue-sweetalert2';import 'sweetalert2/dist/sweetalert2.min.css'; Vue.use(VueSweetalert2);コード
html
今回は以下のような感じで、
card
とinput-group
を使って実装しました。HTML<template> <section class="login-form d-flex flex-solumn justify-content-center align-items-center w-100 text-center"> <div class="login-form__field card rounded-0 pt-5 pb-3"> <i class="login-form__budge fas fa-user-circle text-primary bg-white rounded-circle"></i> <div class="card-body"> <h1 class="login-form__heading card-title text-primary font-weight-bold mb-0">Sign in</h1> <hr class="bg-primary mt-0 mb-4"> <p class="card-text text-muted mb-4">ユーザー名とパスワードを入力してください。</p> <div class="input-group mx-auto mb-4"> <div class="input-group-prepend"> <span class="input-group-text bg-primary text-white" id="basic-addon1"> <i class="fas fa-user"></i> </span> </div> <input class="form-control" type="text" placeholder="Username" v-model="login.user_name"> </div> <div class="input-group mx-auto mb-4"> <div class="input-group-prepend"> <span class="input-group-text bg-primary text-white" id="basic-addon1"> <i class="fas fa-key"></i> </span> </div> <input class="form-control rouded-0" type="password" placeholder="Password" v-model="login.password"> </div> <button class="login-form__button btn btn-primary w-75 mb-4" type="button" :disabled="checkInput" @click="signIn">Login </button> <div class="login-form__attention d-flex justify-content-between my-o mx-auto"> <div class="custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" id="remember-me" v-model="login.remember"> <label class="custom-control-label" for="remember-me">情報を記憶する</label> </div> <a class="card-link" href="#"> <u>パスワードを忘れた方はこちら</u> </a> </div> </div> </div> </section> </template>Scss
今回はBootstrapベースなので、BEMを採用して影のエフェクト以外は軽く整える程度にしました。
scss.login-form { &__field { position: relative; max-width: 30rem; //影エフェクト &:after { content: ''; position: absolute; bottom: 20px; right: 55px; z-index: -1; width: 50%; height: 100px; box-shadow: 100px 0 10px 15px rgba(0, 0, 0, .3); transform: skew(-40deg); } } //__field &__budge { position: absolute; top: -2rem; left: 50%; font-size: 6rem; transform: translateX(-50%); } //__budge &__attention { font-size: 0.6rem; } //__attention &__button { border-radius: 0!important; } //__button } // .login-formJavaScript
これがデフォルトのVueの記述方法になります。
ざっくり説明するとdata
は変数等を格納する場所・computed
は変更があるとリアルタイムで値を再計算してくれる場所・methods
は文字通りメソッドを格納する場所です。
他にもwatch
やライフサイクルフック等開発に便利な機能が色々とあるので、詳しくは公式ドキュメントをご参照ください。まず
data
内のlogin
は「ユーザーネーム・パスワード・ユーザー情報を記憶するか否か」を保持しています。
html内にv-model="login.user_name"
といった記述があると思いますが、このv-model
によって双方向データバインディングを実現しています。
これによりデータと表示の同期・DOM操作を自力でやる必要がなくなります。次は
computed
ですが、今回はlogin.user_name
とlogin.password
が条件に組み込まれていますが、どちらかの値が変更された段階で自動で値を更新します。
メソッド内にif (!this.checkInput)
と指定されている関数がありますが、このように使用する事で、他の場所で毎回入力を確認する必要がなくなります。最後に
methods
は一番簡単で、関数を格納する場所になります。
とりあえず確認用にv-model
で同期した値をsweetalert2で表示しています。JavaScriptexport default { data() { return { //ログイン情報 login: { user_name: '', password: '', remember: false } } }, computed: { //ユザーネームとパスワードの両者が入力されている事を判定する値、自動で変更を検知し反映してくれる checkInput() { return (!this.login.user_name || !this.login.password) ? true : false; } }, methods: { signIn() { //ユーザーネームとパスワードの両者が入力されていた時のみ発火する。 if (!this.checkInput) { //v-modelで同期した値を出力する this.$swal({ icon: 'info', title: 'Information', text: ` ID: ${this.login.user_name}, Password: ${this.login.password}, Remember: ${this.login.remember} ` }) } return; } //signIn } }最後に
$ npm run dev
でコンパイルして確認すると...
キチンと動いています。
よろしければ参考にしてみて下さい。
- 投稿日:2020-06-01T11:57:53+09:00
Amazon Chime SDK新機能を使ってホワイトボードを実装した件
この記事はこちらでも紹介しています。
https://cloud.flect.co.jp/entry/2020/06/01/115652前回は、Amazon Chime SDKのVirtual背景の作り方についてご紹介しました。
https://qiita.com/wok/items/962929e63bc98e4033b9
今回も引き続きAmazon Chime SDKのお話をしたいと思います。
さて、先日Amazon がAmazon Chime SDKの新機能追加を発表したのをご存知でしょうか。
本機能は、Amazon Chimeで使われているデータ通信路を間借りすることで、会議の参加者間でデータメッセージのやり取りを可能にする機能です。発表にも書かれているとおり、これにより例えば会議室参加者間でホワイトボードを共有したり、絵文字のやり取りを簡単に行えたりします。また、活用の仕方によっては、参加者のミュートを強制するなど会議室の状態制御を行うことも可能になります。
ということで今回は、早速この機能を使ってホワイトボードを作ってみましたのでご紹介したいと思います。
今回作ったホワイトボードの挙動はこんな感じです。
Amazon Chimeとシグナリング
今回のAmazon Chime SDKの追加機能は、もともとAmazon Chimeで使われているシグナリング通信を間借りして実現されているようです。Amazon Chimeのビデオ会議はWebRTCという技術を用いて実現されていますが、この制御を行う際に用いられている通信がシグナリング通信です。具体的には、WebRTCではブラウザ間でP2Pの通信を行うのですが、この通信を開始するために相手の宛先を特定したり、暗号通信の鍵交換をしたりするときに用いられます。また、P2P通信と言ってもFirewallを越えた通信を行う場合にはTURNという中継サーバを経由する必要がありますが、こういった経路に関する情報交換もシグナリング通信で行われます。
WebRTCの全体像とシグナリングの関係はこちらのページが詳しいので、詳しく知りたい方は参照ください。
Amazon Chimeは、様々なネットワーク環境下においてもビデオ会議を簡単に開始できるように、マネージドな中継サーバやシグナリング通信の通信路を提供しています。今回追加された機能はこのシグナリングを行うマネージドな通信路を活用して、任意のデータメッセージをやり取りできるようにしています。なので、開発者はメッセージング用サーバを用意する必要がなく、簡単にビデオ会議システムに共有ホワイトボードなどを追加することができます。
API概観
新たに提供されたメソッドは下記の3つです。
本機能では、Topicというタグをつけてデータメッセージに送受信します。
まず、各クライアントではTopic毎に処理を定義したコールバック関数を登録しておきます。そして、送信側がTopicと共にデータメッセージを送信すると、そのデータメッセージを受け取ったクライアントはTopicに応じたコールバック関数を呼び出し処理を行います。内部処理、特にデータの流れの詳細は不明ですが、おそらく一般的なpublish/subscribeモデルで動いていると思われます。
今回使用してみて、とても使いやすい機能だと感じました。
なお、本機能では、データメッセージのPublisherが、そのデータメッセージのTopicをsubscribeしていても、そのデータメッセージを受信できないので、注意が必要かもしれません。
私は、PublisherとSubscriberがお互いの関係性を全く無視できるようにするのが、publish/subscribeモデルの利点だと思っているので、PublsherとSubscriberが同じソフトウェア(セッション)だとデータを受信できないというのは、ちょっとだけ違和感がありました。(個人的には、未だに自分のプログラムのバグかも?とも思ってます。)今回のホワイトボードのようなリアルタイムにPublisherのUIに情報を反映する必要があるものについては、受信時のフィルタリングをしなくてよく、ありがたいとも言えますので、個人の感じ方次第だとは思いますが。新たに提供されたメソッド
データ送信
https://aws.github.io/amazon-chime-sdk-js/interfaces/audiovideofacade.html#realtimesenddatamessage共有ホワイトボードの処理の概要
今回作成した共有ホワイトボードの大まかな処理の流れは次のとおりです。
- Publisherのブラウザのキャンバス(HTMLCanvasElemnt)上でマウスイベント/タッチイベントを検出し、座標を特定
- Publisherのキャンバス上に描画
- 座標をデータメッセージとしてBroker(Chime)に送信 (realtimeSendDataMessage)
- Brokerから各Subscribersに座標を送信
- 各Subscribersのキャンバスに描画
前述の通りPublisherは、自分の送信するデータメッセージを受信することができないので、自分のキャンバスに描画してからデータメッセージを送信する必要があります。
ホワイトボードのようにユーザ操作を遅延なくUIに反映させたいアプリケーションを作る場合はデータメッセージ送信前に自身のUIに反映させる方がユーザ体験上良いので、Publisherがデータメッセージを受信できる、できないにかかわらず同じような作りにはなるとは思いますが。
むしろ、Publisherは、自分の送信するデータメッセージを受信することができないので、Publisherで受信データを破棄しなくて良いので実装が楽かもしれません。実装
サブスクライブ設定
realtimeSubscribeToReceiveDataMessageでsubscribeするtopicと対応するコールバック関数を登録するラッパ関数の例です。ここでは、データメッセージの受領時に、app.app.receivedDataMessageを呼び出すコールバック関数を定義して引数として使っています。なお、app.app.receivedDataMessage自体は別の場所で任意の処理を定義しておいてください。
export const setRealtimeSubscribeToReceiveDataMessage = (app:App, audioVideo:AudioVideoFacade, topic:string) =>{ const receiveDataMessageHandler = (dataMessage: DataMessage): void => { app.receivedDataMessage(dataMessage) } audioVideo.realtimeSubscribeToReceiveDataMessage(topic, receiveDataMessageHandler) }データメッセージ送信
realtimeSendDataMessageを用いてデータメッセージを送る処理例です。
ホワイトボードに描画を行うために、始点と終点の座標、ストローク情報、線の太さなどをjosn化して送信しています。sendDrawsingBySignal = (targetId: string, mode:string, startXR:number, startYR:number, endXR:number, endYR:number, stroke:string, lineWidth:number)=>{ const gs = this.props as GlobalState const message={ action: 'sendmessage', data: JSON.stringify({ cmd : MessageType.Drawing, targetId : targetId, startTime : Date.now(), mode : mode, startXR : startXR, startYR : startYR, endXR : endXR, endYR : endYR, stroke : stroke, lineWidth : lineWidth }) } gs.meetingSession?.audioVideo.realtimeSendDataMessage(MessageType.Drawing.toString(), JSON.stringify(message)) }動作デモ
ホワイトボード
作成したホワイトボード機能の動きはこのような感じになります。このデモは授業のホワイトボードを模擬したものとなります。右側で描画した内容が左側に反映されているのが分かるでしょうか?
画面共有上での描画
また、このホワイトボードをオーバレイの形で作ってあげると、Amazon Chime SDKの画面共有機能と併用しながらプレゼンをすることができます。
FLECT Amazon Chime Meeting
今回説明した機能は、現在FLECT研究開発室で、ビデオ会議を使った新機能のテストベッドに組み込まれています。
下記のリポジトリに公開していますので、ご興味を持たれましたらアクセスしてみてください。https://github.com/FLECT-DEV-TEAM/FLECT_Amazon_Chime_Meeting
最後に
今回は、Amazon Chime SDKの最新機能を用いてホワイトボードを作成してみました。
日本では先日、緊急事態宣言の解除が発表されました。しかし、まだまだ教室に多くの人を集めて授業をするのは難しそうです。
また、同様に接客を行うにも、なかなかFace-to-Faceで接客というのもリスクがあり、難しいかもしれません。
ビデオ会議と共有ホワイトボードで、こういった課題に対応するというのも選択肢の一つではないかと思います。次回は、またAmazon Chimeで遊ぶか、以前紹介したマルチバーコードリーダの技術的な内容をご紹介するかをしようと思ってます。
では。
- 投稿日:2020-06-01T11:43:35+09:00
フレームワークなしでSPAを作るために、WebComponentsを用いてFluxを実装する
SPAを作るために必要となる設計思想Flux。今回はWebComponentsを用いてVanillaJSでFluxを実装してみたいと思います。
なお今回のコードを書くにあたって10分で実装するFluxを参考にさせていただいています。
まず、Fluxのデータの流れをおさらいしましょう。かなり簡単に書くと以下のような感じだと思います。
View -> Action -> Dispatcher -> Store -> View -> ...
まずユーザーがWebサイトにアクセスすると何らかのViewが表示されます。そしてユーザーが何らかの行動を起こしActionが発火します。Actionはアクション名と必要なデータをDispatcherを通してStoreに伝えます。StoreはActionに応じて状態(state)を更新します。そして、更新したことをViewに伝え、Viewは新しい状態に基づいて再描画します。ActionはDispatcherを使ってStoreへ、StoreはDispatcherを使って任意のActionを監視します。そのため、DispatcherはEventEmitterで実装できそうです。なお、ブラウザにEventEmitterは標準で実装されていないので、今回は代わりにEventTargetを使います。
Storeも状態を更新したことをViewに伝え、Viewは任意のStoreを監視します。つまり、StoreもEventTargetを使って実装します。
Viewは今回は、WebComponentsのCustomElementsを用いて実装してみたいと思います。なお今回実装するのは、ボタンを押したらカウントが増えるアプリです。
まず、Dispatcherを見てましょう。DispatcherはEventTargetそのものです。
dispatcher.mjsexport default new EventTarget();では次にActionを見てましょう。
actions.mjsimport dispatcher from 'dispatcher.mjs'; export default { countUp() { dispatcher.dispatchEvent(new CustomEvent('countUp')); } }Dispatcherを利用してActionをStoreに伝えるにはこのように
new CustomEvent()
を使います(new Event()
でもよい)。CustomEventを使うとデータも渡せます。データは
eventTarget.dispatchEvent(new CustomEvent('eventName', { detail: data }));
と書くことによって、受け取り側は、
eventTarget.addEventListener((e) => { const data = e.detail });
と書くことによってデータを受け取れます。
では次にStoreを見てみましょう。store.mjsimport dispatcher from 'dispatcher.mjs'; const initialState = { count: 0 }; class Store extends EventTarget { constructor() { super(); Object.assign(this, initialState); dispatcher.addEventListener('countUp', () => { this.count++; this.dispatchEvent(new Event('CHANGE')); //stateを変更したことをcomponentに伝える }); } } export default new Store();
'countUp'
アクションをリッスンして、内部のthis.count
というstateを更新していますね。そして、'CHANGE'
イベントを発火させています。こうすることによって、Viewにstateが更新したことを伝えることができます。
では次に、View(Component)を見てみましょう。Component.mjsimport actions from 'actions.mjs'; import store from 'store.mjs'; const html = ` <p>Count: <span>${store.count}</span></p> <button type="button">Count Up</button> `; export default class Component extends HTMLElement { constructor() { super(); const shadowRoot = this.attachShadow({mode: 'open'}); shadowRoot.innerHTML = html; this.span = shadowRoot.querySelector('span'); shadowRoot.querySelector('button').onclick = () => { actions.countUp(); } this.handleStoreChange = this.handleStoreChange.bind(this); } connectedCallback() { //storeのstateの変更を監視する; store.addEventListener('CHANGE', this.handleStoreChange); //最初の描画; this.handleStoreChange(); } disconnectedCallback() { store.removeEventListener('CHANGE', this.handleStoreChange); } handleStoreChange() { //storeのstateが変更されたらcomponentのstateへ渡す; this.count = store.count; } set count(value) { //componentのstateが新しい値に置き換わった時のみ描画する if(value === this._count) return; this._count = value; this.span.innerHTML = value; } }少し長いですね、順を追って見ていきましょう。
まず、CustomElementsを作るときはHTMLElementを継承したクラスを作ります。そして、constructor内でshadowRootをCustomElementsに追加し、そのshadowRootのinnerHTMLにベースとなるhtmlを代入します。これはお決まりのパターンでshadowRootを追加しないでconstructor内でhtmlを直接CustomElementsに代入すると、
document.createElement()
時にエラーになるので気を付けましょう。また、constructor内でbutton要素のonclickを登録しています。ボタンがクリックされたらActionが呼ばれるようにしています。
constructorの下にconnectedCallbackとdisconnectedCallbackというメソッドがあります。これはCustomElementsのライフサイクルの一つで、このCustomElementsがdocumentに追加された時
connectedCallback()
が、切断された時はdisconnectedCallback()
が呼ばれます。ここでは、connectedCallbackでStoreの監視を始めています。監視しているStore内で'CHANGE'イベントが発火されると、handleStoreChangeメソッドが呼ばれます。なお、Viewがdocumentに追加されたタイミングで一回handleStoreChangeメソッドを呼び、Storeからstateを取得しています。
そして、disconnectedCallbackではStoreの監視を止めています。メモリリーク防止のためです。では、handleStoreChangeメソッドでは何を行っているのでしょう。Componentのsetterであるcountにstore.countを代入しています。実はComponentは内部にstateを持つようにしています。stateを持つことによって、stateが変更された部分のみが差分で描画されるようになっています。
set count()
では、Storeから渡されたstateが新しい値に置き換わっているか調べていて、新しい値が渡された時のみ、DOMを更新します。
このように、ViewにCustomElementsを使った場合、setter内部で描画を更新する処理を書きます。以上Viewを見てきました。では、このComponentをdocumentに追加しましょう。以下のように書きます。
index.mjsimport Component from 'Component.mjs'; customElements.define('x-component', Component); document.body.innerHTML = '<x-component></x-component>';まず作ったCustomElementsを定義します。
customElements.define(要素名, Component)
で定義できます。要素名には必ず一つの-(ハイフン)が必要です。これを実行した結果が以下です。以下のコードをブラウザのコンソールに直接入力してもカウントアップアプリができます。(SafariはEventTargetのpolyfillが必要です。)
See the Pen
CustomElements-Flux-sample by shigure (@webkatu)
on CodePen.
以上、FluxをVanillaJSで実装してみました。以下のブログ記事にこのFlux実装を基にSPAを作る方法を簡単なサンプルを交えながら説明しています。
フレームワークなしで作るSPA
- 投稿日:2020-06-01T09:56:36+09:00
A-Frame: aframe-multisrc-componentの調査
困った
A-Frameでサイコロを作りたいのですが、標準のマテリアルコンポーネントだと立方体の6面とも同じ画像が表示されてしまいます。
demo
これでは困る。いい感じになった
立方体の6面とも異なる画像を表示できるコンポーネントを調べてみると、良さそうなのがありました。
aframe-multisrc-component
demo
いい感じになりました。
個々の面に対して色を指定したり、メタリックにしたりもできるようです。バグっぽい
使ってみて気づいたのですが、
- multisrcコンポーネントをエンティティから削除する
- multisrcコンポーネントを持つエンティティを削除する
のどちらかを実行すると、multisrcコンポーネントのremoveイベントが発火しますが、その中の処理にバグがあるっぽいです。
以下のようなエラーが発生します。
Cannot read property 'addEventListener' of undefinedループの中からthisが参照できなくなっているようです。
remove: function(){ var defaultMaterial = this.el.components.material.material this.el.getObject3D('mesh').material = defaultMaterial this.el.removeEventListener('componentchanged', this.compChange); var animationEventsArray = ['animationbegin', 'animationstart', 'animationcomplete', 'animationend'] animationEventsArray.forEach(function(event) { this.el.addEventListener(event, this.materialChangedByAnimation) // <- ここでthisがundefinedになる }); this.reduceMaterialChangedThrottle(200); }このコードを追記してremove関数を上書きしてあげると、とりあえずうごきました。
AFRAME.components.multisrc.Component.prototype.remove = function () { var defaultMaterial = this.el.components.material.material; this.el.getObject3D('mesh').material = defaultMaterial; this.el.removeEventListener('componentchanged', this.compChange); var animationEventsArray = ['animationbegin', 'animationstart', 'animationcomplete', 'animationend']; var self = this; // <- thisをselfで保持してループの中からも参照できるようにした animationEventsArray.forEach(function (event) { self.el.addEventListener(event, self.materialChangedByAnimation); <- thisをselfに置き換えた }); this.reduceMaterialChangedThrottle(200); }
- 投稿日:2020-06-01T09:31:59+09:00
【初心者向け】【Javascrript】FizzBuzz問題
今回はFizzBuzz問題をHTMLのscriptタグ内に書きました。
FizzBuzz問題とは?
1から30まで順に数えて出力して行き、3で割り切れる数は「Fizz」5で割り切れる数は「Buzz」両方で割り切れる数は「FizzBuzz」と出力する、ようにプログラムを書く問題です。
これが書けるか書けないかでプログラマー志願者を仕分けられるようになったようです。実際今でも使われていますから押さえておいて損はないと思います。FizzBuzz詳細
私が書いたコード
sample.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset=UTF-8> <title>loop</title> </head> <body> <script> var i = 0; for (i = 1; i <=100; i++) { if (i % 15 === 0 ) { document.write("<p>" + "FizzBuzz" + "</p>"); } else if(i % 3 === 0) { document.write("<p>" + "Fizz" + "</p>"); } else if(i % 5 === 0 ) { document.write("<p>" + "Buzz" + "</p>"); } else document.write("<p>" + i + "</p>"); } </script> </body> </html>書く順番
ifは条件が厳しいものから順番に書いていった方が良いです。今回の問題だと"Fizz"や"Buzz"を表示させるifを先に書いてしまうと3と5の倍数の数字がif文を通過するとき、3の倍数をFizzに置き換えるところで条件を満たしてしまうのでその先の処理は行わないからです。
- 投稿日:2020-06-01T08:31:09+09:00
ノーバンドルな開発ツール「Vite」を試してみる【no bundle dev enviroment for Vue 3.0】
最近猛烈にスターを集めている開発ツール、Viteを触ってみたので簡単に紹介します。
この記事はVite v0.19.1
時点での情報です。Viteとは?
ViteはVue.jsの作者のEvan You氏が開発中のノーバンドルな開発ツールです。
ネイティブのESモジュールのインポートを利用しバンドル不要で高速に動作するdevサーバーと、Rollup.jsをベースとしたプロダクションビルド機能を提供します。
設定不要で.vue
のSFCをコンパイルできて、さらにデフォルトで今開発中のVue3.0が使えます。
しかも、vue-cliのようにVue.js限定ではなく、React、Preactにも対応しています。注意
Still experimental, but we intend to make it suitable for production.
とある通り、まだ絶賛開発中です。プロダクションで使うのは控えた方が良さそうです。
何が嬉しいの?
VueのSFC(Single File Components)での開発ならVue CLIで良いのでは?と思うかもしれません。
Viteを使う利点は以下の通りです。
- 開発時はバンドル不要で動作するので、大規模プロジェクトでも初回起動が非常に早い
- HMR(画面の再描画無しにファイル変更をブラウザに適用してくれる機能)が、モジュールの総数と切り離されているため一貫して高速に動作する
ざっくり言うと総じて動作が早いです。開発体験が良い。
他詳細な説明はREADMEにも記載されているのでそちらもどうぞ。https://github.com/vitejs/vite#how-and-why
インストールとプロジェクト作成
VueとReactのプロジェクト作成を簡単に説明します。
Vue
以下コマンドでプロジェクトを作成します。
$ npm init vite-app example-vue
これだけでOKです。example-vueにVueのプロジェクトが作成されます。
あとは依存モジュールをinstallして起動するだけです。$ cd example-vue $ npm i $ npm run devReact
--template react
でReactのテンプレートを指定したうえでプロジェクトを作成します。$ npm init vite-app --template react example-reactexample-reactにReactのプロジェクトが作成されます。
あとはvueと同じく起動するだけです。$ cd example-react $ npm i $ npm run devビルド
ViteではRollup.jsを内部的に使ってリソースをバンドルしプロダクションビルドが可能です。
buildコマンドでビルドが実行されます。$ npm run build
dist
配下に成果物が生成されるので、あとはそれを公開するだけです。細かいビルドオプションを設定したい場合は、コマンド引数で指定するか、設定ファイルを作り記述できます。
以下成果物のディレクトリをout
ディレクトリに変更する例です。プロジェクトルートに
vite.config.js
を追加して以下を記述します。vite.config.jsmodule.exports = { outDir: "out" }あとはそのままビルドすれば
out
ディレクトリに成果物が生成されます。他設定可能なオプションはソースコードをみるのが良さそうです。
https://github.com/vitejs/vite/blob/master/src/node/config.tsTypeScriptへの対応
TypeScriptへの対応も設定なしで可能です。Vueの場合は
.vue
で<script lang="ts">
を設定するだけ。
Reactの場合は、.jsx
を.tsx
に変更するだけです。
.ts
ファイルもそのままimportできます。App.vue<template> <img alt="Vue logo" src="./assets/logo.png" /> <HelloWorld msg="Hello Vue + Vite" /> </template> <script lang="ts"> import HelloWorld from './components/HelloWorld.vue' import { defineComponent } from 'vue' // Composition APIなので defineComponent()を利用 export default defineComponent({ name: 'App', components: { HelloWorld }, setup() { return {} } }) </script>注意点
2020/05/31現在、TypeScriptはJSへのトランスパイルのみ対応していて、型チェックは行えません。
基本的にエディタ上での型エラーの確認と、tsconfig.jsonを追加しビルド前にtsc --noEmit
を利用して型チェックを行ってください。PostCSS、Scssへの対応
Viteは、
.vue
ファイルとインポートされた.css
ファイルの全てにPostCSSを自動的に適用します。
必要なPostCSSのプラグインをnpmでインストールして、あとはpostcss.config.js
をプロジェクトルートに追加するだけです。Autoprefixerの利用例。
$ npm i autoprefixer --save-devpostcss.config.jsmodule.exports = { plugins: [ require('autoprefixer')() ] }CSS Pre-Processorsのscssも、
sass
をnpmインストールして、.vue
ファイルで<style lang="scss">
を指定するだけで利用できます。$ npm i sass --save-dev<style lang="scss"> /* scss */ </style>終わりに
とても高速に動作するのでViteかなり良いですね。あと、Vue 3.0のsandbox環境としても最高です。
今回紹介した以外にも機能盛り沢山なので、是非READMEを読んで欲しいです。
https://github.com/vitejs/vite今後もさらに機能が拡張されるようなので引き続き動向を見ていきたいです。
最後に。Evan You氏のVite作成時の投稿がカッコ良すぎる。
こんなこといつか言ってみたいです。As I was going to bed, I had an idea about a no-bundler dev setup (using native browser ES imports), but with support for Vue SFCs with hot reload. Now it's almost 6AM and I have PoC working. The hot reload is so fast it's near instant.
— Evan You (@youyuxi) April 20, 2020参考
- 投稿日:2020-06-01T08:31:09+09:00
ノーバンドルな開発ツール「Vite」を触ってみる 【no bundle dev enviroment for Vue 3.0】
最近猛烈にスターを集めている開発ツール、Viteを触ってみたので簡単に紹介します。
この記事はVite v0.19.1
時点での情報です。Viteとは?
ViteはVue.jsの作者のEvan You氏が開発中のノーバンドルな開発ツールです。
ネイティブのESモジュールのインポートを利用しバンドル不要で高速に動作するdevサーバーと、Rollup.jsをベースとしたプロダクションビルド機能を提供します。
設定不要で.vue
のSFCをコンパイルできて、さらにデフォルトで今開発中のVue3.0が使えます。
しかも、vue-cliのようにVue.js限定ではなく、React、Preactにも対応しています。注意
Still experimental, but we intend to make it suitable for production.
とある通り、まだ絶賛開発中です。プロダクションで使うのは控えた方が良さそうです。
何が嬉しいの?
VueのSFC(Single File Components)での開発ならVue CLIで良いのでは?と思うかもしれません。
Viteを使う利点は以下の通りです。
- 開発時はバンドル不要で動作するので、大規模プロジェクトでも初回起動が非常に早い
- HMR(画面の再描画無しにファイル変更をブラウザに適用してくれる機能)が、モジュールの総数と切り離されているため一貫して高速に動作する
ざっくり言うと総じて動作が早いです。開発体験が良い。
他詳細な説明はREADMEにも記載されているのでそちらもどうぞ。https://github.com/vitejs/vite#how-and-why
インストールとプロジェクト作成
VueとReactのプロジェクト作成を簡単に説明します。
Vue
以下コマンドでプロジェクトを作成します。
$ npm init vite-app example-vue
これだけでOKです。example-vueにVueのプロジェクトが作成されます。
あとは依存モジュールをinstallして起動するだけです。$ cd example-vue $ npm i $ npm run devReact
--template react
でReactのテンプレートを指定したうえでプロジェクトを作成します。$ npm init vite-app --template react example-reactexample-reactにReactのプロジェクトが作成されます。
あとはvueと同じく起動するだけです。$ cd example-react $ npm i $ npm run devビルド
ViteではRollup.jsを内部的に使ってリソースをバンドルしプロダクションビルドが可能です。
buildコマンドでビルドが実行されます。$ npm run build
dist
配下に成果物が生成されるので、あとはそれを公開するだけです。細かいビルドオプションを設定したい場合は、コマンド引数で指定するか、設定ファイルを作り記述できます。
以下成果物のディレクトリをout
ディレクトリに変更する例です。プロジェクトルートに
vite.config.js
を追加して以下を記述します。vite.config.jsmodule.exports = { outDir: "out" }あとはそのままビルドすれば
out
ディレクトリに成果物が生成されます。他設定可能なオプションはソースコードをみるのが良さそうです。
https://github.com/vitejs/vite/blob/master/src/node/config.tsTypeScriptへの対応
TypeScriptへの対応も設定なしで可能です。Vueの場合は
.vue
で<script lang="ts">
を設定するだけ。
Reactの場合は、.jsx
を.tsx
に変更するだけです。
.ts
ファイルもそのままimportできます。App.vue<template> <img alt="Vue logo" src="./assets/logo.png" /> <HelloWorld msg="Hello Vue + Vite" /> </template> <script lang="ts"> import HelloWorld from './components/HelloWorld.vue' import { defineComponent } from 'vue' // Composition APIなので defineComponent()を利用 export default defineComponent({ name: 'App', components: { HelloWorld }, setup() { return {} } }) </script>注意点
2020/05/31現在、TypeScriptはJSへのトランスパイルのみ対応していて、型チェックは行えません。
基本的にエディタ上での型エラーの確認と、tsconfig.jsonを追加しビルド前にtsc --noEmit
を利用して型チェックを行ってください。PostCSS、Scssへの対応
Viteは、
.vue
ファイルとインポートされた.css
ファイルの全てにPostCSSを自動的に適用します。
必要なPostCSSのプラグインをnpmでインストールして、あとはpostcss.config.js
をプロジェクトルートに追加するだけです。Autoprefixerの利用例。
$ npm i autoprefixer --save-devpostcss.config.jsmodule.exports = { plugins: [ require('autoprefixer')() ] }CSS Pre-Processorsのscssも、
sass
をnpmインストールして、.vue
ファイルで<style lang="scss">
を指定するだけで利用できます。$ npm i sass --save-dev<style lang="scss"> /* scss */ </style>終わりに
とても高速に動作するのでViteかなり良いですね。あと、Vue 3.0のsandbox環境としても最高です。
今回紹介した以外にも機能盛り沢山なので、是非READMEを読んで欲しいです。
https://github.com/vitejs/vite今後もさらに機能が拡張されるようなので引き続き動向を見ていきたいです。
最後に。Evan You氏のVite作成時の投稿がカッコ良すぎる。
こんなこといつか言ってみたいです。As I was going to bed, I had an idea about a no-bundler dev setup (using native browser ES imports), but with support for Vue SFCs with hot reload. Now it's almost 6AM and I have PoC working. The hot reload is so fast it's near instant.
— Evan You (@youyuxi) April 20, 2020参考
- 投稿日:2020-06-01T02:42:40+09:00
TypeScriptで学ぶデザインパターン〜Interpreter編〜
対象読者
- デザインパターンを学習あるいは復習したい方
- TypeScriptが既に読めるあるいは気合いで読める方
- いずれかのオブジェクト指向言語を知っている方は気合いで読めると思います
- UMLが既に読めるあるいは気合いで読める方
環境
- OS: macOS Mojave
- Node.js: v12.7.0
- npm: 6.14.3
- TypeScript: Version 3.8.3
本シリーズ記事一覧
- TypeScriptで学ぶデザインパターン〜Iterator編〜
- TypeScriptで学ぶデザインパターン〜Adapter編〜
- TypeScriptで学ぶデザインパターン〜Template Method編〜
- TypeScriptで学ぶデザインパターン〜Factory Method編〜
- TypeScriptで学ぶデザインパターン〜Singleton編〜
- TypeScriptで学ぶデザインパターン〜Prototype編〜
- TypeScriptで学ぶデザインパターン〜Builder編〜
- TypeScriptで学ぶデザインパターン〜Abstract Factory編〜
- TypeScriptで学ぶデザインパターン〜Bridge編〜
- TypeScriptで学ぶデザインパターン〜Strategy編〜
- TypeScriptで学ぶデザインパターン〜Composite編〜
- TypeScriptで学ぶデザインパターン〜Decorator編〜
- TypeScriptで学ぶデザインパターン〜Visitor編〜
- TypeScriptで学ぶデザインパターン〜Chain of Responsibility編〜
- TypeScriptで学ぶデザインパターン〜Facade編〜
- TypeScriptで学ぶデザインパターン〜Mediator編〜
- TypeScriptで学ぶデザインパターン〜Observer編〜
- TypeScriptで学ぶデザインパターン〜Memento編〜
- TypeScriptで学ぶデザインパターン〜State編〜
- TypeScriptで学ぶデザインパターン〜Flyweight編〜
- TypeScriptで学ぶデザインパターン〜Proxy編〜
- TypeScriptで学ぶデザインパターン〜Command編〜
- TypeScriptで学ぶデザインパターン〜Interpreter編〜
Interpreterパターンとは
解決したい問題を簡単なミニ言語で表現するためのパターンです。
サンプルコード
Interpreterパターンで作られたクラス群がどんなものになるのか確認していきましょう。
今回は、題材として"足し算をする機能"を想定します。GitHubにも公開しています。
具体的には
2 1 3 + +
という文字列を渡すと2 + 1 + 3
の結果(6)を出力するプログラムを考えます。modules/Expression.ts
構文木のノードを示すインターフェースです。
Expression.tsexport default interface Expression { interpret(numbers: number[]): void; }
interpret
で構文解析を行います。modules/ExpressionNumber.ts
数値ノードを示すクラスです。
ExpressionNumber.tsimport Expression from "./Expression"; export default class ExpressionNumber implements Expression { private number: number; constructor(number: number) { this.number = number; } interpret(numbers: number[]): void { numbers.push(this.number); } }
interpret
では数値をnumbers
に追加していきます。modules/ExpressionPlus.ts
足し算ノードを示すクラスです。
ExpressionPlus.tsimport Expression from "./Expression"; export default class ExpressionPlus implements Expression { interpret(numbers: number[]): void { numbers.push(numbers.pop() + numbers.pop()); } }
interpret
では数値をnumbers
から2つ取り出してそれを足したものをnumbers
に格納しています。modules/Parser.ts
構文解析を行うためのクラスです。
Parser.tsimport Expression from "./Expression"; import ExpressionPlus from "./ExpressionPlus"; import ExpressionNumber from "./ExpressionNumber"; export default class Parser { private parseTree: Expression[] = []; constructor(string: string) { for (const part of string.split(' ')) { if (part === '+') { this.parseTree.push(new ExpressionPlus); } else { this.parseTree.push(new ExpressionNumber(parseInt(part))); } } } evaluate(): number { const context: number[] = []; for (const node of this.parseTree) { node.interpret(context); } return context.pop(); } }
evaluate
では設定したノードを一つずつ取り出して構文解析しています。Main.ts
本パターンで作成されたクラス群を実際に使用する処理が書かれています。
Main.tsimport Parser from "./modules/Parser"; const expression: string = '5 2 19 + +'; const parser: Parser = new Parser(expression); console.log(parser.evaluate());クラス図
ここまでInterpreterパターンで作られたクラス群を1つずつ確認してきました。次にクラス図を示します。Interpreterパターンの全体像を整理するのにお役立てください。
- AbstractExpression: サンプルコードでは
Expression
インターフェースが対応- TerminalExpression: サンプルコードでは終端となる表現がないため該当なし(
start 2 2 + end
のような構文解析をさせる場合end
に該当する表現が終端になる)- NonTerminalExpression: サンプルコードでは
ExpressionNumber
クラスExpressionPlus
クラスが対応- Context: サンプルコードでは
Parser
クラスが対応- Client: サンプルコードでは
Main
が対応※LucidChartを使用して作成
解説
最後に、このデザインパターンの存在意義を考えます。
普通はなんらかの処理をさせる場合はTypeScriptのプログラミング言語しか登場しないと思います(当然ですが)。ところが今回はミニ言語とミニ言語を解析するTypeScriptのプログラムといういわば2階層になっています。ということはミニ言語を修正したい場合は当然TypeScriptのプログラムを修正する必要はありません。このように、修正をミニ言語に止めることができるのです。
補足
サンプルコードの実行方法はこちらと同様です。
参考
あとがたり
終端をクラスで表現するようサンプルコードをいつか書き換えたい。BNFは基本情報で少し触れてから全く関わりがないため個人的に難しい。
- 投稿日:2020-06-01T00:15:15+09:00
React hooksを基礎から理解する (useEffect編)
React hooksとは
React 16.8 で追加された新機能です。
クラスを書かなくても、state
などのReactの機能を、関数コンポーネントでシンプルに扱えるようになりました。
- React hooksを基礎から理解する (useState編)
- React hooksを基礎から理解する (useEffect編) 今ここ
- React hooksを基礎から理解する (useContext編)
- React hooksを基礎から理解する (useReducer編)
- React hooksを基礎から理解する (useCallback編)
- React hooksを基礎から理解する (useMemo編)
- React hooksを基礎から理解する (useRef編)
useEffectとは
useEffect
を使うと、useEffect
に渡された関数はレンダーの結果が画面に反映された後に動作します。
つまりuseEffect
とは、「関数の実行タイミングをReactのレンダリング後まで遅らせるhook」になります。副作用の処理(DOMの書き換え、変数代入、API通信など)を関数コンポーネントで扱えます。
クラスコンポーネントでのライフサイクルメソッドに当たります。
- componentDidMount
- componentDidUpdate
- componentWillUnmount
create-react-appでReactのコードを書く
create-react-appをひさしぶりに
npm install
しようとしたらテンプレートが出来ない?
困っていて見つけた記事。解決出来ました。ありがとうございます。参考:ひさしぶりにcreate-react-appしたらテンプレートができなかった時の対処法
Material-UIをinstall
Material-UI
をinstallしたら、使いたいコンポーネントをすぐ見つけられるし、勝手にスタイリングしてくれるのでテンションあがります?$ npm install @material-ui/core参考:MATERIAL-UI
クリックしたらタイトルも同時に変更されるコンポーネントを作る
クラスコンポーネント
react.jsimport React, { Component } from 'react' import ButtonGroup from '@material-ui/core/ButtonGroup'; import Button from '@material-ui/core/Button'; class EffectClass extends Component { constructor(props){ super(props); this.state = { count: 0, } } componentDidMount(){ document.title =`${this.state.count}回クリックされました` } componentDidUpdate(){ document.title =`${this.state.count}回クリックされました` } render() { return ( <> <p>{`${this.state.count}回クリックされました`}</p> <ButtonGroup color="primary" aria-label="outlined primary button group"> <Button onClick={()=> this.setState({count: this.state.count + 1})} > ボタン </Button> <Button onClick={()=> this.setState({count: 0})}> リセット </Button> </ButtonGroup> </> ) } } export default EffectClass関数コンポーネント
react.jsimport React, {useState, useEffect} from 'react' import ButtonGroup from '@material-ui/core/ButtonGroup' import Button from '@material-ui/core/Button' const EffectFunc = () => { const [count, setCount] = useState(0) useEffect(() => { document.title =`${count}回クリックされました` }) return ( <> <p>{`${count}回クリックされました`}</p> <ButtonGroup color="primary" aria-label="outlined primary button group"> <Button onClick={()=>setCount((prev) => prev + 1)}> ボタン </Button> <Button onClick={()=>setCount(0)}> リセット </Button> </ButtonGroup> </> ) } export default EffectFuncクラスコンポーネントの場合
副作用は ReactがDOMを更新したあとに起こすようにしたいので、
componentDidMount
とcomponentDidUpdate
に記載します。するとReact
がDOM
に変更を加えた後に、document.title
を更新しています。react.jscomponentDidMount(){ document.title =`${this.state.count}回クリックされました` } componentDidUpdate(){ document.title =`${this.state.count}回クリックされました` }関数コンポーネントで
useEffect
を使った場合デフォルトでは、
useEffect
は毎回のレンダー後に呼ばれます。react.jsconst [count, setCount] = useState(0); useEffect(() => { document.title =`${count}回クリックされました` })React公式サイトのstate とライフサイクルをもう一度読むと理解が進みました。
参考:React公式サイト state とライフサイクルuseEffectの第2引数に配列を渡して特定の値が変わったときだけ再レンダーさせる
useEffect()
の第2引数に[count]を渡すと、count
に変化があったときだけ再レンダーします。react.jsimport React, {useState, useEffect} from 'react' import { makeStyles } from '@material-ui/core/styles'; import ButtonGroup from '@material-ui/core/ButtonGroup' import Button from '@material-ui/core/Button' import Input from '@material-ui/core/Input'; const useStyles = makeStyles((theme) => ({ root: { '& > *': { margin: theme.spacing(1), }, }, })); const EffectFunc = () => { const classes = useStyles(); const [count, setCount] = useState(0) const [name, setName] = useState({ lastName: '', firstName: '' }) useEffect(() => { document.title =`${count}回クリックされました` },[count]) return ( <> <p>{`${count}回クリックされました`}</p> <ButtonGroup color="primary" aria-label="outlined primary button group"> <Button onClick={()=>setCount((prev) => prev + 1)}> ボタン </Button> <Button onClick={()=>setCount(0)}> リセット </Button> </ButtonGroup> <p>{`私の名前は${name.lastName} ${name.firstName}です`}</p> <form className={classes.root} noValidate autoComplete="off"> <Input placeholder="姓" value={name.lastName} onChange={(e)=>{setName({...name,lastName: e.target.value})}}/> <Input placeholder="名" value={name.firstName} onChange={(e)=>{setName({...name,firstName: e.target.value})}}/> </form> </> ) } export default EffectFunc
useEffect
の第2引数に[count]
を取るときreact.jsuseEffect(() => { document.title =`${count}回クリックされました` console.log(`再レンダーされました`) },[count])
name
が更新されても再レンダーされず、count
が更新された場合だけ、document.title
が再レンダーされていることがわかります。第2引数に
[count]
を取らないとき
useEffect
はデフォルトで、副作用関数は初回のレンダー時および毎回の更新時に呼び出されます。react.jsuseEffect(() => { document.title =`${count}回クリックされました` console.log(`再レンダーされました`) })
count
が更新された場合だけだけでなく、name
が更新された場合にも、document.title
が再レンダーされていることがわかります。第2引数にからの配列
[]
を取ったときレンダリングは初回のみで再レンダーされないので、
document.title
は更新されません。react.jsuseEffect(() => { document.title =`${count}回クリックされました` console.log(`再レンダーされました`) },[])クリーンアップについて
クリーンアップとはイベントリスナの削除、タイマーのキャンセルなどのことです。
クリーンアップ関数
をreturnすると、2度目以降のレンダリング時に前回の副作用を消してしまうことができます。クラスコンポーネントの場合
componentWillUnmountは、クリーンアップ(addEventLitenerの削除、タイマーのキャンセルなど)に使用されます。componentDidMountに副作用を追加し、componentWillUnmountで副作用を削除します。
react.jscomponentDidMount() { elm.addEventListener('click', () => {}) } componentWillUnmount() { elm.removeEventListener('click', () => {}) }関数コンポーネントの場合
上記に相当するhookは以下。「クリーンアップ関数」をreturnすることで、2度目以降のレンダリング時に前回の副作用を消してしまうことができます。
react.jsuseEffect(() => { elm.addEventListener('click', () => {}) // returned function will be called on component unmount return () => { elm.addEventListener('click', () => {}) } }, [])最後に
次回は
useContext
について書きたいと思います。参考にさせていただいたサイト