- 投稿日:2020-10-27T23:41:49+09:00
Vue.jsで、いろんなやり方の画像プレビュー方法
ref属性から画像を取得して、表示する
// refからアップロード input(type="file", ref="file", @change="leftSetImage") v-img(:src="image.source")// refから一時的なURLに変換して取得 leftSetImage() { const files = this.$refs.file; const fileImg = files.files[0]; if (fileImg.type.startsWith("image/")) { this.image.source = window.URL.createObjectURL(fileImg); } },イベントオブジェクトから画像を取得して、表示する
一時的なURLでプレビュー表示
// イベントオブジェクトからアップロード input( type="file", @change="setImageLeft($event)" ) v-img(:src="office.left_image.image", type="submit")// イベントオブジェクトから、一時的URLに変換して取得 setImageLeft(e) { const file = (e.target.files || e.dataTransfer)[0]; this.LeftUploadedImage = file; if (file.type.startsWith("image/")) { this.office.left_image.image = window.URL.createObjectURL(file); } },base64に変換して、プレビュー表示
今までのやり方だと、ブラウザを放置して更新したり、そのデータを画像を保存したりした時の再表示の動作ができなくなるということが起きる。
base64に変換する場合は、そのような事象は起きない。
// イベントオブジェクトからアップロード input( type="file", @change="setImageRight($event)" ) v-img(:src="office.right_image.image", type="submit")// イベントオブジェクトから、base64に変換して取得 setImageRight(e) { const file = e.target.files[0] || e.dataTransfer.files; const reader = new FileReader(); reader.onload = (e) => { this.office.right_image.image = e.target.result; }; reader.readAsDataURL(file); },
- 投稿日:2020-10-27T23:41:49+09:00
Vue.jsならではの、いろんなやり方の画像プレビュー方法
ref属性から画像を取得して、表示する
// refからアップロード input(type="file", ref="file", @change="leftSetImage") v-img(:src="image.source")// refから一時的なURLに変換して取得 leftSetImage() { const files = this.$refs.file; const fileImg = files.files[0]; if (fileImg.type.startsWith("image/")) { this.image.source = window.URL.createObjectURL(fileImg); } },イベントオブジェクトから画像を取得して、表示する
一時的なURLでプレビュー表示
// イベントオブジェクトからアップロード input( type="file", @change="setImageLeft($event)" ) v-img(:src="office.left_image.image", type="submit")// イベントオブジェクトから、一時的URLに変換して取得 setImageLeft(e) { const file = (e.target.files || e.dataTransfer)[0]; this.LeftUploadedImage = file; if (file.type.startsWith("image/")) { this.office.left_image.image = window.URL.createObjectURL(file); } },base64に変換して、プレビュー表示
今までのやり方だと、ブラウザを放置して更新したり、そのデータを画像を保存したりした時の再表示の動作ができなくなるということが起きる。
base64に変換する場合は、そのような事象は起きない。
// イベントオブジェクトからアップロード input( type="file", @change="setImageRight($event)" ) v-img(:src="office.right_image.image", type="submit")// イベントオブジェクトから、base64に変換して取得 setImageRight(e) { const file = e.target.files[0] || e.dataTransfer.files; const reader = new FileReader(); reader.onload = (e) => { this.office.right_image.image = e.target.result; }; reader.readAsDataURL(file); },
- 投稿日:2020-10-27T22:00:23+09:00
Firefox/Chrome拡張機能のポップアップ内ではOnclickは働かないので他の方法を使おう
概要
拡張機能アイコン(ツールバーにあるやつ)をクリックして出てくるポップアップは、
browser_action
内のdefault_popup
にHTMLファイルを指定することによって作る。manifest.json"browser_action":{ "default_icon": { "48" : "48.png" }, "default_popup": "popup.html" }だがこのHTMLファイル内においては、
Onclick
は使えない。
ポップアップ内において、インラインのJavaScriptは許可されていないのだ。対処法
DOMContentLoaded
を使う。
まずHTMLファイル内のOnclick
を動作させたかったタグにIDを割り振る。
以下に例を示す。<!--置換前--> <a Onclick="hoge()">hoge</a> <!--置換後--> <a id="hoge">hoge</a>JavaScriptのコードは外部ファイル(ここでは
hoge.js
とする)に以下のように書く。HTMLファイル内に<script src="hoge.js"></script>
を書くのを忘れないよう注意。hoge.jsdocument.addEventListener("DOMContentLoaded", function(){ var hoge = document.getElementById("hoge"); hoge.addEventListener("click", function(){ // ここに処理を書く }); });ちなみにこのJSファイルは
manifest.json
内のcontent_scripts
に書かなくても動く。参考
https://stackoverflow.com/questions/13591983/onclick-or-inline-script-isnt-working-in-extension
https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Content_Security_Policy
- 投稿日:2020-10-27T21:56:10+09:00
JavaScriptの魅力をなるべく簡単にまとめてみた!
この記事は、初心者である私が「jsの魅力、伝えたいなぁ」と思って書いた記事です。
一応初心者にも理解できるようにはしていますが、多分以下の知識を持っている人じゃないと完全に理解できないと思います。
- HTMLをある程度記述できる
- ある程度ITについての知識がついている(P検3級程度)
これがわからなくても、できる限りわかるようにはしているので、わからない部分はネットで調べてより詳しい情報をみて理解していってください()
さて、先ほども言ったように、私は初心者な訳です。
初めてプログラム言語を触ったのは、今から1年半ぐらい前になりますかね。まぁ、プログラム言語を学ぶにあたって、自分が使うプログラム言語を決める必要があるのですが、ネット上では
「JavaScript(以下、js)がおすすめだよ!」
という声が多いと感じましたし、私も一番最初はjsをいじりました。
まぁしばらくはいろんなプログラム言語を触った訳なんですが、やっぱりjsに戻ってきましてね。せっかくだし、jsの参考書でも購入するかということで、買ってみたんですが、その参考書が結構当たりだったんです。今まで当たり前だと思ってたものが違ってたり、その逆もあったり。また、コードの行数を減らすためのテクニックとか、色々なことが書いてあったんです。
そこで今回は、その参考書からも多少抜粋して、jsの代表的な特徴について紹介してきます。
1,書いてすぐに動かせる
よく、「jsは初心者向けプログラム言語だよ」と言われるのですが、その根拠のひとつがこれだと感じます。
例として、C言語はコンパイラ言語ですので、一度書いたコードはコンパイルして、その後、コンパイルし終わったファイルを実行する訳ですが、ここまでの作業がめんどくさいという人もいるかも知れません。
もっと極端な話、C言語はコンパイラとかデバッガとか、IDE(統合開発環境)が必要になってくるんですが、jsはこれといったものは必要ありません(厳密には違いますが。後で説明が入ります)。まぁ、エディタぐらいは必要でしょうけど。
じゃあjsはどうやってコードを動かしてるんだ、って話になると思うんですよね。
答えは、ブラウザの中のエンジンで動かしているんです。
詳しく言っちゃうと、例えば皆さんが使っているであろうsafariやgoogle Chrome、fire foxなど、俗にいう「Webブラウザ」がありますよね?実は、そのブラウザの中にはjsを実行するためのエンジンがあるのです。それはブラウザによって変わってくるので、例えばsafariでできることが、google Chromeになるとできないなど、ちょっとした差はあります。
まぁ要するに、ブラウザの中でjsが動くから、コンパイラとかも必要ないよということです。
さらにいっちゃえば、jsにとってのIDEはブラウザということになります。
2,動的型付け言語
初心者からしてみたら、「何その単語」となりますよね。
簡単にいうと、動的型付け言語とは、
「プログラムを動かす時にデータ型決めておくよ」
という言語を指します。
つまり、データ型をつける必要がないということです。これが、多分「初心者におすすめのプログラム言語」と言われる所以なのではないでしょうか。
ここからは自論なのですが、正直この機能については初心者向けとは思えません。というのも、データ型を指定しないということは、その変数はどんなデータ型にもなれるということです。それが論理エラーを招く可能性もありますし、プログラマーからしてみても、「この変数はこのデータ型を持っているのか」というのが視覚的に判断できないので、混乱が生じる可能性もあります。
初心者だからこそ、データ型について詳しく触れる必要があるのではないのか、と思ったり思わなかったり
3,HTMLの操作が非常に楽
先ほども言ったように、jsはWebブラウザで動作するプログラム言語です。
HTML1とはWebブラウザにおいて、文書を生成するためのマークアップ言語なのですが、同じWeb上で動作するので、HTMLに対する操作が非常に楽です。
例えば、
「ボタン"userButton"が押されたら、id="Text"のpタグの文章を変える」
という処理は、以下の通りで実現できます。test1.jsdocument.getElementById("userButton").onclick = function{ //ボタンを押したら...? //id"Text"のpタグに文字列を代入 document.getElementById("Text").innerHTML = "Hello JavaScript!"; }これで完了です。至って簡単ですね。
「は?簡単じゃないし?」と思う方もいるとは思いますが、しばらくjsに触れればこれも理解できます。
4,イベント処理
そもそもイベントとは、ユーザがサイトに対して何かをしたアクションのことを指します。
例えば、
「"A"のキーが押されたらメッセージを出力したい」
という処理があるとすると、そのコードは以下の通り。test2.jsdocument.addEventListener("keydown",event =>{ let key = event.key; //キーの値を取得 if(key == "A") console.log("Aが押されました!!"); //キーがAならば });こんな感じで、簡単にイベント処理を行うことができます。ついでに、test1.jsの、「ボタンが押されたら....」という処理も、イベント処理の一種になります。
まとめ
jsは様々な機能がありますが、基本はWeb環境で動くことのできる、Web特化のプログラム言語です。ただ、Web以外にも、様々な場所で活躍している、プログラム言語の代表的な言語になります。また、ここで紹介できなかったjsの魅力もあります。詳しくは下にある参考文献を購入して見てみてください。
昔は、「toy language(おもちゃの言語)」と言われていたらしいですが、今はそんなことないので、ガシガシ勉強しましょう!
HTMLとは、「Hyper Text Markup Language」の略で、Webの文書の書式を定義する為の、一種のマークアップ言語です。簡単にいうと、Web上の文書を作成する為の言語です。jsをいじるならばこの言語も知っておいた方がいいですね ↩
- 投稿日:2020-10-27T21:52:51+09:00
Javascript データ型の基本
はじめに
Javascript初心者です。
データ型を学んでいて、重要だと思ったことをアウトプットしていきます。
なお、筆者はJava,c#,rubyをちょっとだけ学んでいるので、そういった言語との
違いなどについても述べていければと思います。※ 注意
記事の内容は誤っている箇所がある可能性があります。
お気づきになられた方はご指摘頂けると大変ありがたいです。
何卒よろしくお願いします。Javascriptのデータ型の種類
※ECMAScript6~の型
- string
- number
- boolean
- null
- undifined
- symbol
- object
string~symbolはプリミティブ型、基本型と呼ばれています。
objectはオブジェクト型、参照型と呼ばれています。
プリミティブ、オブジェクトの違いについてはほとんどのプログラミング言語で
定義が似ていると思うのでググってみてください。
それぞれ解説していきます。string
文字列を扱う文字列型です。
ダブルクォーテーション(""),シングルクォーテーション('')で囲んで表現されます。number
数値を扱う数値型です。
ちなみに、'数値'と'数字'は多くの場合、別物とみなされます。
boolean
正か負か、又は0か1かを表す真偽値型です。(論理型とも呼ばれます)
true,falseという名前で表現されます。null
「何も存在していない」ことを表現します。
※多言語では、”存在しないオブジェクトへの参照”などと表される場合もありますが
Javascriptにおいてnullはそうではありません。undifined★
「定義されていない」を表現します。
Javascript固有の型となっています。"宣言したけど値は未定義"
のような変数に対してundifinedが割り当てられます。symbol★
ユニークで不変な「識別子」を表現します。
Object型の中のデータ構造に使用されたりします。
Rubyでいうsymbolとは異なります。
※ECMAScript6より登場しました。object
データおよびそのデータを操作する命令が入ったデータ構造です。
その名の通り、現実の「モノ」に例えられます。
上記のプリミティブ型以外でnew
キーワードで変数に格納されるものがこれにあたります。ちょっとしたTips
string ⇄ numberのデータ型変換
paizaとかでも意外と変換忘れがちなので注意したいところです...// ■ string → number let str = "1000"; let num1 = +str; // 方法① let num2 = Number(str); // 方法② console.log(typeof(num1),typeof(num2)); //=> "number" "number" // ■ number → string let num = 1000; let str1 = num + ""; let str2 = String(num); console.log(typeof(str1),typeof(str2)); //=> "strng" "string"参考
- 投稿日:2020-10-27T21:47:36+09:00
カウントダウン機能の作成
カウントダウン機能の作成
初期値120秒から1秒ずつカウントダウンする機能の作成。
<body onload="init()"> <p>COUNT DOWN: <span id="time"></span></p> </body>ページを読み込んだときにperformance.nowで変数startTimeにその時点の時間をミリ秒で代入します。
setIntervalメソッドで1秒ごとに関数tickを呼び出します。<script> let startTime; function init() { startTime = performance.now(); setInterval(tick, 1000); }tick関数内は、まず変数nowTimeに関数tickが呼び出されたときの時間をperformance.nowで代入します。
body要素内のspan要素を取得してページを読み込んだときから経過した時間を120秒からカウントダウンする形式で表示します。
performance.nowはミリ秒で取得するので、1秒ごとにカウントダウンするように差分の時間を1000で割ります。function tick() { const nowTime = performance.now(); const time = document.getElementById("time"); time.textContent = 120 + Math.floor((startTime - nowTime) / 1000); } </script>下記のコードをコピーしてファイルに貼り付ければ試せます。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script> let startTime; function init() { startTime = performance.now(); setInterval(tick, 1000); } function tick() { const nowTime = performance.now(); const time = document.getElementById("time"); time.textContent = 120 + Math.floor((startTime - nowTime) / 1000); } </script> </head> <body onload="init()"> <p>COUNT DOWN: <span id="time"></span></p> </body> </html>
- 投稿日:2020-10-27T21:23:25+09:00
Mapbox で点をスムースに移動させる
Mapbox で移動する点を描く では、連続した点を単純に描画してみました。
が、パカパカ移動するので何だかつまらないしありきたりです。
なので、Uber アプリのようにもっとスムースにシンボルを移動させます。完成形は↓です。
はじめに
で例が示されているので、これを改造します。
また、この機能を実現させるのに、turf.js という地理空間図形や座標の計算を行ってくれるライブラリを使用します。
index.js
処理の要点は2つです。
1.
requestAnimationFrame
を使ってフレーム毎に描画を行う
2. ルートの「開始点からnメートル進んだ点」を turf.js で計算する移動する速度(km/h)はここでは固定とします。
requestAnimationFrame
で描画のハンドラ関数を再帰で繰り返し呼び出し、前回の描画から今回までにかかった時間(ミリ秒)で何m進むのかを計算します。
進んだ距離を加算しておき、turf.js を使って「開始点から Nm 進んだ座標」を取得して、DataSource を更新→画面を更新します。const waypoints = [ [136.53890132904053,34.844644908488974], [136.54332160949704,34.840242190008105], // 中略 // [136.5387511253357,34.84480340196252] ]; // Symbol Layer const symbolData = { 'type': 'FeatureCollection', 'features': [] }; map.on('load', function() { map.loadImage( 'https://img.icons8.com/material/32/0000FF/marker--v1.png', (error, image) => { // 中略 // time = new Date().getTime(); animate(); } ); }); const line = turf.lineString(waypoints); const lineLength = turf.length(line, { units: 'meters' }); const kph = 600; // 600km/h let time = null; let distance = 0; function animate() { // 1フレームで移動する距離を算出 const now = new Date().getTime(); const offset = now - time; time = now; const movingInMeters = ((kph / 60 / 60 / 1000) * offset) * 1000; // 移動距離加算 distance = distance + movingInMeters; // 移動した点を計算 const options = {units: 'meters'}; const movedPoint = turf.along(line, distance, options); // 更新 symbolData.features.splice(0); symbolData.features.push({ 'type': 'Feature', 'geometry': { 'type': 'Point', 'coordinates': movedPoint.geometry.coordinates } }); const dataSource = map.getSource('point'); dataSource.setData(symbolData); // repeat if (distance >= lineLength) { distance = 0; } requestAnimationFrame(animate); }DEMO
時速600km の設定なので F1 よりも相当速いです。
See Also
- 投稿日:2020-10-27T21:22:27+09:00
Mapbox で移動する点を描く
よくある「スマートフォンなどの位置情報を受信して、その位置を地図上に示す」を Mapbox で行います。
Mapbox でのピン表示について考える で書いたとおり、「頻繁なアニメーションが伴う場合は Symbol Layer」がふさわしいと考えるので、今回はそちらを使用します。
とは言っても、ここではダミーデータとして緯度経度のリストを走査してシンボルを移動させているだけなので、ただのシンボルの再描画の例になります。
index.js
事前準備が長いですが、重要なのは最下部
setInterval
の中で行っているdataSource.setData()
で、これを行うと地図(上のレイヤ)が更新されます。ところどころ省略しているので、完全版は DEMO を見てください。
const map = new mapboxgl.Map({ container: 'map', center: [136.53890132904053,34.844644908488974], zoom: 14, style: { // 中略 // }, }); const waypoints = [ [136.53890132904053,34.844644908488974], [136.54332160949704,34.840242190008105], // 中略 // [136.5387511253357,34.84480340196252] ]; // Symbol Layer const symbolData = { 'type': 'FeatureCollection', 'features': [] }; map.on('load', function() { map.loadImage( 'https://img.icons8.com/material/32/0000FF/marker--v1.png', (error, image) => { if (error) { throw error; } map.addImage('iconA', image); map.addSource('point', { 'type': 'geojson', 'data': symbolData }); map.addLayer({ 'id': 'points', 'type': 'symbol', 'source': 'point', 'layout': { 'icon-image': 'iconA', 'icon-size': 1, 'icon-allow-overlap': true, 'icon-ignore-placement': true, } }); let counter = 0; setInterval(() => { symbolData.features.splice(0); symbolData.features.push({ 'type': 'Feature', 'geometry': { 'type': 'Point', 'coordinates': waypoints[counter % waypoints.length] } }); const dataSource = map.getSource('point'); dataSource.setData(symbolData); counter = counter + 1; }, 1000); } ); });DEMO
- 投稿日:2020-10-27T21:21:50+09:00
Mapbox でのピン表示について考える
Mapbox で地図上にいわゆる「ピン」を表示する、またピンをクリックすると「吹き出し」を表示させる、Google Maps SDK ではマーカー と呼ばれる機能を実現する方法がいくつか(2つ)あります。それぞれの特徴と使い分けについて考えます。
1. マーカー(Marker)
一つ目は Mapbox でも Marker と呼ばれる機能です。
以下は、Mapbox のドキュメントへのリンクです。
- 既定のマーカー(Add a default marker)
- ドラッグ可能なマーカー(Create a draggable Marker)
- アニメーションするマーカー(Animate a marker)
- カスタムアイコンを持ったマーカー(Add custom icons with Markers)
- マーカーにポップアップを追加(Attach a popup to a marker instance)
Mapbox の「マーカー」は、Google Maps などのそれと同じく、色や形状が変更でき、吹き出し(ポップアップ)を関連付けることができ、ドラッグ可能にすることができます。
2. シンボルレイヤー(Symbol Layer)
二つ目の選択肢は Symbol Layer です。
が、実際には Symbol Layer という機能名があるわけではありません。まず Mapbox には Layer という PhotoShop などのペイントソフトと似たような概念があります。複数の Layer が積み重なって、「地図」が描画されます。
背景地図も「ひとつの Layer」であり、Mapbox で OpenStreetMaps を表示するだけ の
index.js
を見るとstyles: []
であることが分かります。Layer には "fill", "line", "symbol", "circle", "heatmap", "fill-extrusion", "raster", "hillshade", "background" の種類(
type
)があり、上記の背景地図は "raster" です。Symbol Layer とは、「type が
symbol
である Layer」のことです。Layer に表示するデータを示すものが「ソース(Source)」です。ソースにも様々な種類がありますが、最もよく使われるが GeoJSON です。
Symbol Layer を表示するのに必要なデータは、GeoJSON.Point の地物群ということになります。
以下は、Mapbox のドキュメントへのリンクです。
- シンボルを追加(Add an icon to the map)
- 属性値に応じてスタイルを変化させる(Generate and add a missing icon to the map)
- 移動するシンボル(Animate a point)
- ルート上を移動するシンボル(Animate a point along a route)
- エフェクト付きのシンボル(Add an animated icon to the map)
- シンボルをクリックしたらポップアップを表示させる(Display a popup on click)
- ドラッグ可能なシンボル(Create a draggable point)
- シンボルの画像を動的に生成(Add a generated icon to the map)
一見すると、Marker と同じことが行えるように見えます。
「同じことが行えるかどうか?」ならば Yes ですが、その実装には多くのコード量を伴うものもあります。例えば、「シンボルをクリックしたらポップアップを表示させる」を行いたい場合、Marker では
.setPopup()
の1行で済みますが、Symbol Layer の場合 は、「Symbol がクリックされたというイベントハンドラで、Popup を表示する」というコードをわざわざ記述しなくてはなりません。「クリック可能を示すマウスカーソルの変更」も同じくです。ドラッグ可能なシンボル(Create a draggable point) も、そのコードを見れば相当に面倒なことを行っていることがわかるでしょう。
以上から、Symbol Layer は、Marker よりは自由度が少なく、より「地図データ」に近いものと言えそうです。
Marker と Symbol Layer の使い分け
選択状態を示すなら Marker
最も一般的なユースケースだと思います。
「地図上のスポットをクリックしたら吹き出しが表示される」という事を簡単に実現したいならば、Marker がよいです。データ量が多い場合は Symbol Layer
地図データに近い存在である「Symbol Layer」は、Marker よりも大量のレンダリングを高速に行うことができます。
これは Marker と Symbol Layer のレンダリングのされ方の違いに起因します。
Marker は、HTML要素がそこに配置されます。つまり Marker の追加=DOM Tree への任意のHTMLタグの追加、となります。
一方で Symbol Layer では、HTML要素は生成されません。Symbol は canvas に直接描画されています。
DOM Tree を変更するよりも Canvas に WebGL で描画する方が一般的には高速になりますから、Symbol Layer の方が大量のデータを高速に描画できるということになります。
次の DEMO は、大量の Marker または Symbol を追加して描画する例です。
2000個くらいになると、Marker の方がモタつきを感じるようになりますが Symbol はあまり変化を感じません。
10000個以上では、Marker はほぼフリーズ状態ですが、Symbol の方はまだ耐えられる印象です。頻繁なアニメーションが伴う場合は Symbol Layer
複数の移動体をリアルタイムトラッキングするなど、画面の書き換えなアニメーションが頻繁に発生するユースケースでも、描画コストの低い Symbol Layer が適しています。
複雑かつ動的なピンを表示したいなら Marker
前項で示した通り、Marker は HTML要素 であり、Symbol Layer は canvas への直接描画です。
Symbol Layer へ描画するピンの形状や色は、事前にmap.addImage()
で登録しておく必要があります。また、登録できるのは基本的には画像のみです。Marker は HTML要素ですから表現の自由度は高いです。例えば地図上に直接グラフのようなものを表示したい場合は、Marker の方が適しているでしょう。ただし、前述のように描画コストには注意する必要があります。
次の DEMO は、Marker の見た目に HTML の Table を使用した例です。
データを種類毎に管理したいなら Symbol Layer
「コンビニ群」「レストラン群」「ガソリンスタンド群」など、ピンデータを何らかのカテゴリ毎に管理したい場合は、Symbol Layer が向いていると思います。
それぞれを Layer/Source とすれば、Layer 単位での表示・非表示切り替え、描画順序(手前/奥)の切り替えが Style により容易にできます。
ここまで来るとデータ量は多くなりそうですから、必然的に Symbol Layer にせざるを得ないとも言えます。One more thing...
さらに大量の地点情報を表示したい場合、「タイル画像を生成してしまう」という選択肢もあります。この場合、静的なデータを生成することになるので頻繁に変化するコンテンツには向きませんが、一定間隔でデータを更新するバッチ処理などで済む場合は有用と思われます。
MapBox では、Mapbox Tiling Service(MTS)」というサービスを提供しており(現在は public beta)、タイル画像、しかもベクトルタイルデータを作成するツールキットが提供されるようです。筆者もまだ使ったことがないので、使う機会があったら記事を書きたいと思います。
まとめ
「データとしての地点情報」ならば、Symbol Layer にしておくのが良いかと思います。
その中で、特定の機能(選択状態や強調状態)を示すものだけを Maker とするのが良いと思います。
Google Maps SDK には Symbol Layer(というか Layer) という概念がないので、知らないと大量の Marker を登録してしまう事になりそうですが、それは避けた方が良いと思います。
- 投稿日:2020-10-27T21:20:44+09:00
Mapbox で初期表示位置(中心とズームレベルまたは範囲)を指定
初期位置を指定したい、しかもクエリパラメータなどで動的に、「中心とズームレベル」または「指定した範囲」を初期位置としたい場合は、
new mapboxgl.Map({...})
で直接指定するのではなく、変数にしておいてcenter
,zoom
のペアまたはbounds
を設定するとよいです。
center
はLngLatLike
であり [経度, 緯度] という degree の配列、bounds
はLngLatBoundsLike
であり、LngLatLike
の配列([[西南], [東北]])です。index.js
const opt = { container: 'map', style: { // 省略 // }, }; // ランダム(時刻)で、富士山(中心とズームレベル) か 琵琶湖(範囲) を切り替え if (new Date().getTime() % 2 == 0) { opt.center = [138.73072, 35.36286]; opt.zoom = 13; } else { opt.bounds = [[135.856934, 34.981346], [136.282654, 35.531947]]; // [[west, south], [east, north]] // opt.bounds = [135.856934, 34.981346, 136.282654, 35.531947]; // [west, south, east, north] でも ok } const map = new mapboxgl.Map(opt);DEMO
See Also
- 投稿日:2020-10-27T21:19:58+09:00
Mapbox で OpenStreetMaps を表示するだけ
Mapbox のアクセストークンを取得して埋め込むのも面倒なアナタに。
import する js/css
https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.js https://api.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.cssindex.html
<div id="map"></div>index.js
const map = new mapboxgl.Map({ container: 'map', center: [134, 35], zoom: 10, style: { version: 8, sources: { OSM: { type: "raster", tiles: [ "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", ], tileSize: 256, attribution: "OpenStreetMap", }, }, layers: [{ id: "BASEMAP", type: "raster", source: "OSM", minzoom: 0, maxzoom: 18, }], }, });DEMO
- 投稿日:2020-10-27T21:14:37+09:00
Rubyの条件分岐(case,while,無限ループ,break)
Rubyの条件分岐
case文
条件分岐を表現するための文法。複数の条件を指定する時に、if文のelsifを重ねるよりもシンプルにコードを書くことができる
samplecase 対象のオブジェクトや式 when 値1 # 値1に一致する場合に実行する処理 when 値2 # 値2に一致する場合に実行する処理 when 値3 # 値3に一致する場合に実行する処理 else # どれにも一致しない場合に実行する処理 endwhile文
繰り返し処理を行うためのRubyの構文。指定した条件が真である間、処理を繰り返す
samplenumber = 0 while number <= 10 puts number number += 1 end # ターミナル出力結果 # 0 # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 # 10無限ループ
処理が永遠に繰り返されること
samplenumber = 0 while true puts number number += 1 end # ターミナル出力結果 # 0 # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 # 10 # . # . # .上記のコードは条件式の部分にはじめからtrueと書くことによって意図的に無限ループを発生させている
break
eachメソッドやwhile文などのループから脱出するために使われる
samplenumber = 0 while number <= 10 if number == 5 break end puts number number += 1 end # ターミナル出力結果 # 0 # 1 # 2 # 3 # 4このようにif文などの条件分岐とbreakを使うと、特定の条件のときにループを脱出することができる
現場からは以上です!
- 投稿日:2020-10-27T20:50:47+09:00
vue.jsを初めて学ぶ ② [テンプレート構文]
前回までの記事
↓
① hello world目次
1.テンプレート構文とは?
・newVueテンプレート
3. thisを理解する
4. v-から始まるディレクティブを理解する
5. v-bindとは?
6. v-bindのオブジェクト化
7. v-onとは?
8. イベント修飾子とは?
9. キー修飾子とは?
10. v-onの省略記法
11. v-modelによる双方向データバインディング
12. computedプロパティで動的なデータを扱う
13. methodとcomputedプロパティの違い
14. watchプロパティで、非同期処理を行う
15. ()の使い方
16. class要素をデータにバインディングする(オブジェクト)
17. class要素をデータにバインディングする(配列)テンプレート構文とは
3行のテンプレート構文。vueがこれを読み込み、htmlを出力。ブラウザがhtmlを見る。new Vue({ el: '#app', data: { message: 'Hello World' }各プロパティの説明
- el: マウント先の要素を指定するブロック
- data: 変数を保持するブロック
- methods: 関数を保持するブロック
テンプレート構文の種類
newVueテンプレートとは
index.jsnew Vue({ el: '#app', data: { <!-- [1]文字列を出力 --> message: 'Hello World' <!-- [2]和を出力 --> number: 3 <!-- [3]三項演算子 --> ok: true } <!-- [4]メソッドを出力 --> methods: { sayHi: function() { return: 'Hi'; } } })index.html<div id = "app"> <!-- [1]文字列を出力 --> <p>{{message}}</p> <!-- [2]和を出力 --> <p>{{number + 3}}</p> <!-- [3]三項演算子 --> <p>{{ok ? 'yes' : 'no'}}</p> <!-- [4]メソッドを出力 --> <p>{{ sayHi() }}</p> </div>三項演算子とは?
唯一の、3 つのオペランドをとる演算子です。条件に続いて疑問符 (?)、そして条件が真値であった場合に実行する式、コロン (:) が続き、条件がfalsyであった場合に実行する式が最後に来ます
- ただし素のjavascriptが出力されるのは、単一の式だけに注意。
thisを理解する。
- newVueテンプレート内の変数message
- sayHiメソッド内で、呼び出してみる。
index.jsnew Vue({ el: '#app', data: { message: 'Hello,World!!' }, methods: { sayHi: function() { <!--これはエラーになる--> return message; <!--正しくは、thisが必要--> return this.message; } } })index.html<div id = "app"> <p>{{ sayHi() }}</p> </div>thisとは、Vue.jsのインスタンスにアクセスするために必要なプロキシ
v- から始まるディレクティブを理解する。
- v-とはVue.jsの特別な属性
属性とは、htmlではhref,class,idといった要素の事。
HTMLにおける、属性とはv-text
v-once
v-html(xss脆弱性に注意)
vue.jsにおけるxss脆弱性
v-htmlに関して、信頼のあるコンテンツしか置かないようにする。v-bindを使用して、Googleへのリンクを作成する
- v-bindを理解する。
index.html<div id = "app"> <a href="url">Google</a> </div>index.jsnew Vue ({ data: { url: 'https://google.com', } })これでは、404エラーとなってしまう。
- v-bind使用例
text:index.html <div id = "app"> <a v-bind:href="url">Google</a> </div>
index.jsnew Vue ({ data: { url: 'https://google.com', } })
- v-bindを省略可能
index.html<div id = "app"> <a :href="url">Google</a> </div>index.jsnew Vue ({ data: { url: 'https://google.com', } })v-bindをオブジェクト化して使用する。
- 要素が[href][class][id]といった複数付与したい場合。オブジェクト化が最適
index.html<div id = "app"> <a v-bind="objectGoogle">Google</a> </div>index.jsnew Vue ({ data: { objectGoogle: { href: 'https://google.com', class: 'sample', id: 31 } } })v-onでイベント発生時に、処理を実装。
- DOMで提供されいているイベントに限定される。 DOMイベント一覧
ボタンクリックで数字がカウントされる処理を実装してみる。
v-on:に指定するのは、clickイベント
index.html<div id = "app"> <p>現在{{number}}回クリックされている</p> <button v-on:click="countUp">カウントアップ</button> </div>index.jsnew Vue ({ el: '#app', data: { number: 0 }, methods: { countUp: function() { this.number += 1 } } })v-on:DOMイベント="関数名"とhtml側の記載はシンプルにしておくのが良い。
マウスをかざすた所の、XとY軸を表示する
v-on:に指定するのは、mousemoveイベント
index.html<div id = "app"> <p v-on:mousemove="changeMousePosition">マウスを載せてください</p> <p>X:{{x}},Y{{y}}</p> </div>index.jsnew Vue ({ el: '#app', data: { x:0, y:0, }, methods: { changeMousePosition: function(event) { this.x = event.clientX; this.y = event.clientY; } } })関数changeMousePositionの引数には、event(testなど他のテキストでも動作するが、)を指定するのがルール。
イベント修飾子
- stopPropagationだったが、stop
- preventDefaultだったが、prevent で良い。
v-onイベントで発火する関数処理をstopさせる。
- 関数changeMousePositionをstopさせる。
index.html<div id = "app"> <p v-on:mousemove="changeMousePosition">マウスをかざしてください <span v-on:mousemove="noEvent">マウスをかざしても反応させない。</span> </p> <p>X:{{x}},Y{{y}}</p> </div>index.jsnew Vue ({ el: '#app', data: { x:0, y:0, }, methods: { changeMousePosition: function(event) { this.x = event.clientX; this.y = event.clientY; }, noEvent: function(event) { event.stopPropagation() } })
- spanタグにnoEvent関数を追加
- noEvent関数に、stopPropagationという処理をstopさせる命令を追加
vue.jsのすごい所
- vue.jsでは、処理を止めるstopPropagationを簡易的に設定できる。
- html側のv-on:mousemove以降の関数指定で、stopを命令するだけ!
index.html<div id = "app"> <p v-on:mousemove="changeMousePosition">マウスをかざしてください <span v-on:mousemove.stop>マウスをかざしても反応させない。</span> </p> <p>X{{x}},Y{{y}}</p> </div>index.jsnew Vue ({ el: '#app', data: { x:0, y:0, }, methods: { changeMousePosition: function(event) { this.x = event.clientX; this.y = event.clientY; } })stop便利!
v-onイベントで発火するデフォルト処理をpreventさせる。
- googleへのリンクタグをクリックしても飛ばないように設定。
index.html<div id = "app"> <a v-on:click="noEvent" href="https://google.com">google.com</a> </div>index.jsnew Vue ({ el: '#app', methods: { noEvent: function(event) { event.preventDefault(); } })vue.jsのすごい所
- vue.jsでは、処理を止めるpreventDefaulを簡易的に設定できる。
- html側のv-on:mousemove以降の関数指定で、preventを命令するだけ!
index.html<div id = "app"> <a v-on:click.prevent href="https://google.com">google.com</a> </div>index.jsnew Vue ({ el: '#app', })つなげる事も可能
v-on:mousemove.stop.prevent
キー修飾子
キーボードに対する修飾子
1. エンターキーを入力時点で動作させる。.enter
2. スペースキー入力時点で動作させる。.space
index.html<div id = "app"> <input type="text" v-on:keyup.enter="myAlert"> </div>index.jsnew Vue ({ el: '#app', methods: { myAlert() { alert('アラート'); } } })つなげる事も可能
v-on:keyup.enter.space
v-onディレクティブ省略記法
- カウントアップ関数v-onを省略する。
index.html<p>現在{{number}}回クリックされています</p> <button @click="countUp">countUp</button>省略が可能な記法は、プロジェクト内で統一すること。
v-model
- 双方向データバインディングに用いる。
index.html<div id = "app"> <input type="text" v-model="message"> <h1>{{message}}</h1> </div>index.jsnew Vue ({ el: '#app', data: { message: 'こんにちは', } })双方向データバインディング
- inputにテキストを入力すると、変数messageの内容が置き換わる。
- data内のmessageはもちろん、h1タグのmessageも変更される。動的なデータを扱う
- vue.jsのdataプロパティでは、動的なデータは扱えない。(プレーンなテキストのみ)
- computedプロパティを使用する
index.html<div id = "app"> <p>{{ counter }}</p> <button v-on:click="countUp">+1</button> <p>{{ lessThanThree }}</p> </div>index.jsnew Vue ({ el: '#app', data: { counter: 0 }, methods: { countUp: function() { this.counter += 1 } }, computed: { lessThanThree: function() { return this.counter > 3 ? '3より上' : '3以下' } } })computedプロパティの関数lessThanThreeには
- 引数が必要ではない
- returnで値を返す必要があるdataプロパティでは
- データの初期化をする関数lessThanThreeは、methodsプロパティでは実現できないのか?
結論:できるけど、最適化するならComputedメソッド一択!
- computedプロパティは依存関係に基づきキャッシュされる特徴がある。
- methodsプロパティの関数は、{{二重カッコ内のメソッド}}は再表示される度に実行される。
- 他の第3のメソッドによる更新にもmethodプロパティのメソッドは実行されてしまう。
- そのため、computedプロパティを使用すると最適化できる。
index.html<div id = "app"> <p>{{ counter }}</p> <button v-on:click="countUp">+1</button> <p>{{ lessThanThreeMethod() }}</p> <p>{{ lessThanThreeComputed }}</p> </div>index.jsnew Vue ({ el: '#app', data: { counter: 0 }, methods: { countUp: function() { this.counter += 1 }, lessThanThreeMethod: function() { return this.counter > 3 ? '3より上' : '3以下' } }, computed: { lessThanThreeComputed: function() { return this.counter > 3 ? '3より上' : '3以下' } } })watchプロパティによる、非同期処理
- データが変わったときに非同期をする
- 非同期処理の中では、thisが使用不可
- computedプロパティでは、returnが必要(同期処理)になるので、非同期処理したい場合はwatchプロパティ!
3秒でカウントの値(counter)が0にリセットされる非同期処理
text:index.html
<div id = "app">
<p>{{ counter }}</p>
<button v-on:click="countUp">+1</button>
<p>{{ lessThanThreeComputed }}</p>
</div>
index.jsnew Vue ({ el: '#app', data: { counter: 0 }, methods: { countUp: function() { this.counter += 1 } }, computed: { lessThanThreeComputed: function() { return this.counter > 3 ? '3より上' : '3以下' } }, watch: { counter: function() { var vm = this; setTimeout(function(){ vm.counter = 0 }, 3000) } } })()の使い方
computedプロパティでは、メソッド名のみを{{二重カッコ内}}に記載
computedプロパティに()を足すと、エラーとなり表示されなくなる。methodプロパティでは、メソッド名+()を{{二重カッコ内}}に記載
methodプロパティから()を除くと、function () { [native code] }
と表示される。v-ディレクティブの引数としてメソッド名を指定する場合
()は付与してもしなくてもどちらでもOK。
引数には、javascriptコードを書いてもOK。classをデータにバインディングする方法(オブジェクトver)
- {オブジェクト内}に、bg-colorなど、-(ハイフン)がある場合それを'bg-color'をクォーテーションで括る必要あり。
index.html<div id = "app"> <h1 :class="{red: true, 'bg-blue': false}">はろう</h1> </div>index.jsnew Vue ({ el: '#app' })sample.css.red { color: red; } .bg-blue { background-color: blue; }
- ボタンを設置して、クラスの付与を分岐する。
index.html<div id = "app"> <h1 :class="classObject">はろう</h1> <button @click="isActive = !isActive">切り替えボタン</button> </div>index.jsnew Vue ({ el: '#app', data: { isActive: true }, computed: { classObject: function() { return { red: this.isActive, 'bg-blue': !this.isActive } } } })sample.css.red { color: red; } .bg-blue { background-color: blue; }classをデータにバインディングする方法(配列ver)
index.html<div id = "app"> <h1 :class="[color, bg]">はろう</h1> </div>index.jsnew Vue ({ el: '#app', data: { color: 'red', bg: 'bg-blue', } })text.sample.css.red { color: red; } .bg-blue { background-color: blue; }
- 投稿日:2020-10-27T20:12:01+09:00
クリックイベントでMouseかEnterキーかを判定する
動機
button
要素はクリックした時にfocusがあたった状態になるので、
デザインの再現の際に、不都合があることがあります。button:focus { outline: none; }上記のようにfocus時のスタイルを消してしまえば、いったんは良いのですが、
キーボードでアクセスした時に、そのbutton
がfocusされているのか視覚的に分からないため、アクセシビリティ的には良くありません。ですので、マウスでクリックした時はfocus時のスタイルを消し、キーボードアクセスでfocusされた時は、スタイルを残せるようにJavaScriptで判定したいと思います。
実装例
const button = document.querySelector('button'); const onClickButton = (event) => { if (event.detail !== 0) { event.currentTarget.style.outline = 'none'; } } const onFocusButton = (event) => { event.currentTarget.style.outline = ''; } button.addEventListener('click', onClickButton, false); button.addEventListener('focus', onFocusButton, false);解説
onClickButton
の箇所clickイベントの時、引数eventにはMouseEventオブジェクトが入ります。
MouseEventには(自分が確認した限り)、keydown時のevent.key
のように押下されたキーを判定できるプロパティが無かったため、event.detail !== 0
で、マウスのクリックなのか、Enterキーなのかを判定しました。
event.detail
が何かと言うと、クリック数を読み取り専用で返してくれるプロパティのようです。UIEvent.detail - Web API | MDN
ですので、今回はクリック数がゼロかどうかで、MouseかEnterかを判定しています。
つまり、
event.detail !== 0
は、クリック数が0以外の時なので、
Mouseでクリックされた時の挙動を記述します。Mouseでクリックされた時は
event.currentTarget.style.outline = 'none';
でfocus時のスタイルを消します。もっと詳細にスタイルを指定したい場合は、classList.add('className')
でクラス制御でスタイルを変更しても良いかもしれません。
onFocusButton
の箇所
onFocusButton
では、focusが当たった時に、onClickButton
で消したスタイルを元に戻す記述をしています。
event.currentTarget.style.outline = '';
で空文字を代入しているのは、既存のスタイル(ブラウザのデフォルトスタイルや、CSSで指定したスタイル)が再度適用できるようにです。まとめ
以上、clickイベント時にMouseかEnterキーかを判定して、
focus時のスタイルをコントロールする方法を紹介しました。
より良い方法がありましたら、是非コメントを頂けたら幸いです。
- 投稿日:2020-10-27T19:31:28+09:00
JavaScriptでのオブジェクト指向について学んでみた(DOM)についても少し
3つの重要すぎる用語
オブジェクト
データと機能が合わさったもの
データとは? 例で言うと人間、野球
機能とは? 例で言うと食べる、寝る、打つ、走るプロパティ
オブジェクトの中のデータの部分
メソッド
オブジェクトの中の機能の部分
考え方としてはオブジェクトの中にメソッドとプロパティがあるという感じでいいと思います!
私がファンのイチロー選手とローラさんで例で例えると
オブジェクト プロパティ メソッド イチロー 男性、愛知県出身、野球選手 打つ、走る、勇気を与える ローラ 女性、モデル、20代 元気を与える 筋トレを教える オブジェクト自体をさらにまとめてプロパティの中に入れるという表現ができます!
オブジェクトの作り方
hoge.jslet model = {}今回はモデルという大きなオブジェクトの中にローラさんがいる感じにします。ここにプロパティを追加すると
hoge.jslet model = { name: 'rola', }さらにメゾットも
hoge.jslet human = { name: 'rola', teachWorkout: function(){}, }こんな感じです。
ここにプロパティを追加と変更をしてみます!hoge.jsmodel.age = 25 model.name = 'magi-'この記述を先ほどの記述の下に入れてあげるだけです!
超簡単なやり方としてはこんな感じです。メゾットのfunctionの後に呼び出したい記述を入れてあげるとコンソールで呼び出せるので試してみてください!書き方は以前の記事でも紹介した${}を使うと楽です!ブラウザで用意された最上位オブジェクトWindowについて
windowオブジェクトとはブラウザの情報を持っているオブジェクトです。ブラウザの情報をもつので、ブラウザオブジェクトと呼ばれます。
JavaScriptで予め定義されているメソッドやオブジェクトは全てwindowオブジェクトのプロパティであると言えます!
例えばこのように書くとブラウザにアラートを表示できます。hoge.jswindow.alert("エンジニアになるぞ!")ちなみにwindowは省略できます!
hoge.jsalert("エンジニアになるぞ!")これでも結果は一緒です!
console.logや次に紹介するdocumentもwindowというのがくっついていますがたいてい省略されています!最も頻繁に使用するdocumentオブジェクトについて
このオブジェクトはブラウザ上で表示された情報(HTML)を操作する事ができるオブジェクトです。
このdocumentオブジェクトは非常に多くのプロパティやメソッドを持っています。したがって、documentオブジェクトはこれからHTMLに対して何か処理をする際は頻繁に使用するオブジェクトとなるので覚えておきましょう!
下記ではHTMLに処理する上での考え方と重要な"DOM"についてアウトプットしています!DOM(HTMLの要素をJavaScript上で取得し操作)
DOMとはDocument Object Model(ドキュメントオブジェクトモデル)の略です。HTMLを解析し、データを作成する仕組みです。
HTMLやCSSがWebページとして閲覧されるまでの流れ
⬇️
1, HTMLで書かれた単なる文字情報を解析してDOMに変換
2, そのままだととても見にくいのでCSSやJavaScriptによる装飾を行う
3, ユーザー(私たち)がみる画面に映す。HTMLは階層構造になっていて、DOMによって解析されたHTMLは、階層構造のあるデータとなります。これを、DOMツリーやドキュメントツリーと呼びます!
JavaScriptのメソッドを使うと、DOMツリーを操作することができます!
HTMLの要素名や、「id、class」といった属性の情報を元にDOMツリーの一部を取得し、CSSを変更したり、要素を増やしたり、消したりできます。するとそれがブラウザに反映され、描画も変わります!これがJavaScriptの面白いところかなと思っています!まとめ
オブジェクトについての考え方はRubyに比べてわかりやすいかなと感じました!
これからもっとJavaScriptの奥深くに入り込んでいきます!
- 投稿日:2020-10-27T18:39:05+09:00
Vuejsでフラッシュカードを作りましょう(音声でもコントロール可能)
やりたいこと
今回、Vuejsを勉強のため、簡単なフラッシュカードのウェッブアプリを作成します。
フラッシュカードは質問と答えの面が2つあって、その二つ面を交換すために、「フリップ」があります。実装
データを準備する
まず、HTMLとVuejsの要素を作って、連携しましょう。
<div id="flashCardApp">const flashCardApp = new Vue({ el: '#flashCardApp', data: {}, methods: {}dataのほうはcardsとshowedCardがあります。cardsは全部アプリのカードです。showedCardは表示されるカードになります。で、アプリ起動する時、表示されるカードは最初のカードになります。
cards = [{カード1の情報}, {カード1の情報}, {カード3の情報},...]; data: { cards: cards, showedCard: cards[0] }カード情報は:
id: カードのID(例: 0, 1, 2) frontText: 質問の言葉(例: BANK, CHURCH) backText: 言葉の意味(例:銀行, 教会) colorClass: カードの色(責任) flipped: どんな面が表示されるか(例: true, false)です。
前と次のカードのボータン
表示されるカードは一つだけなので、全部のカードを見えるように、カード間に移動するボータンは必要になります。また、最後の次のカードは最初のカードです。逆に最初のカードの前は最後のカードになります。
JS側
methods: { previosCard: function() { let showedId = this.showedCard.id - 1; if(showedId < 0){ showedId = cards.length - 1; } this.showedCard = cards[showedId]; }, nextCard: function() { let showedId = this.showedCard.id + 1; if(showedId >= cards.length){ showedId = 0; } this.showedCard = cards[showedId] } }HTML側
<button v-on:click="previosCard" class="button-right">前</button></div> <button v-on:click="nextCard" class="button-left">次</button>カードのフリップのボータン
ユーザはカードのフロント面とバック面を交換するボータンを押すと、現在表示されるカードのflippedの状態を変更して、Vuejsはその状態の変更に対して自動的にカードのフロントとバック面を交換してもらいます。
js側methods: { flipCard: function() { this.showedCard.flipped = !this.showedCard.flipped; } }html側
<transition name="flip"> <div v-show="!showedCard.flipped" @click="flipCard" class="card-text card-front-text"> <div class="pretext"> 単語 </div> <h2> {{showedCard.frontText}} </h2> <button class="button-white button-clear button-large"> フリップ <i class="fas fa-sync-alt"></i> </button> </div> </transition> <transition name="flip"> <div v-show="showedCard.flipped" @click="flipCard" class="card-text card-back-text"> <div class="pretext"> 意味 </div> <h2> {{showedCard.backText}} </h2> <button class="button-white button-clear button-large"> フリップ <i class="fas fa-sync-alt"></i> </button> </div> </transition>音声でフラッシュカードをコントロールしましょう。
annyangは、ユーザが音声コマンドでサイトを制御できるようにする小さなjavascriptライブラリです。
annyangの使い方は本当に簡単です。if (annyang) { // コマンドを定義する const commands = { '次': () => { flashCardApp.nextCard(); }, 'フリップ': () => { flashCardApp.flipCard(); }, '前': () => { flashCardApp.previosCard(); }, }; //入力の言語を日本語に変更する annyang.setLanguage("ja"); // ↑の定義したコマンドを追加しする annyang.addCommands(commands); // マイクロからの声が聞こえ始めます annyang.start(); }それで、「次」と「前」と「フリップ」のコマンドを言ってもらうと、フラッシュカードをコントロールできます。ただ、音声認識はクラウドで処理するので、少しだけディレイ感じがあります。
以上です。
結果と参考
デモ: https://codepen.io/tardigrades2/full/oNLZovL
コード: https://codepen.io/tardigrades2/pen/oNLZovL)
参考したコード: https://codepen.io/mgnmrt/pen/pKZVYg
- 投稿日:2020-10-27T18:02:29+09:00
【kintone】お絵描きして添付ファイルに保存する(改)
先日、こんな記事をUPしました。
【kintone】お絵描きして添付ファイルに保存するそれのお絵かき改良版です。
drawingboard.jsを使う
こちらのお絵かきライブラリを使ってみました。
drawingboard.jsdistフォルダー内の
- drawingboard.min.js
- drawingboard.min.css
を、kintoneの「JavaScript / CSSでカスタマイズ」にアップしておきましょう。
また、JavaScriptも書き換えます。JavaScriptを書き換える
↓のようにJavaScriptを書き換えて、「JavaScript / CSSでカスタマイズ」にアップしておきましょう。
(function () { 'use strict'; function draw_start(){ let canvas = new DrawingBoard.Board('canvas'); } //canvasの画像を保存 function saveCanvas(canvas_id) { let canvas_ary = document.getElementsByClassName("drawing-board-canvas"); let canvas = canvas_ary[0]; let title = document.getElementById("title").value; // Brobを取得 canvas.toBlob( function ( blob ) { // FormDataにファイルを格納 let formData = new FormData(); formData.append('__REQUEST_TOKEN__', kintone.getRequestToken()); formData.append('file', blob, title+'.png'); //ファイルをアップロード let xmlHttp = new XMLHttpRequest(); xmlHttp.open('POST','https://サブドメイン.cybozu.com/k/v1/file.json'); xmlHttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xmlHttp.send(formData); //200が帰ってきたらレコード登録 xmlHttp.onload= function(){ if(xmlHttp.status===200){ let key = JSON.parse(xmlHttp.responseText).fileKey; let body={ app:101, record:{ "タイトル":{value:title}, "添付ファイル":{value:[ {fileKey: key} ]} } } //fileKeyを設定 kintone.api("/k/v1/record","POST",body).then(function(resp){ console.log(resp); location.reload(); },(err)=>{ console.log(err,record.$id.value); alert(err); }); } } }); } kintone.events.on(['app.record.index.show'], event => { if(event.viewId == 一覧のID){ draw_start(); let btn_save = document.getElementById("btn_save"); btn_save.onclick =()=>{saveCanvas('canvas');}; } return event; }); })();CSSを書こう
こんな感じでキャンバスのサイズを設定するCSSです。サイズはご自由に!
「JavaScript / CSSでカスタマイズ」にアップしておきましょう。
#canvas{ width: 1000px; height: 600px; }絵がかける!
クリアボタンは動かなくなるけど、カスタマイズビューから消しておくといいと思います。
まとめ
ApplePencilでも絵を描くことができるのでぜひためしてみてね^0^
メモするのにいいかもしれないです^^現在のところ、一度保存した画像の編集には対応していないので、
今後はもうちょっと研究して、編集にも対応させてみたいと思います^0^
- 投稿日:2020-10-27T16:57:52+09:00
node-sassがDeprecatedになるとのことでNuxt.jsのsassをアップデートした
node-sass(LibSass)って長らく使ってたイメージありますね。
こちらに記事にあるようにLibSassっていうC++で書かれたライブラリを使った非推奨になるとのことで、これを使ってるnode-sassも非推奨になり、Dart製のSassを使ってねという感じになる模様です。
- https://www.npmjs.com/package/node-sass ← 今まではよく使われていたけど今後非推奨
- https://www.npmjs.com/package/sass (Dart Sass) ← 今後はこっちが推奨
今回Node.jsのv15系にアップデートしたのでnode-sassのバージョンも上げようかと思って調べてたら発見しました。
環境
- Node.js v15.0.1
- Nuxt.js v2.12.2
元々v14系で動かしてたけどアップデートさせてみました。
node-sassからsassに移行
削除して(nuxtのバージョンは上げなくても良かったけどついでに)
yarn remove nuxt node-sass sass-loaderインストール
yarn add nuxt sass sass-loaderアプリケーションを起動させてみる
yarn dev無事に起動しました。
package.jsonはこんな感じ
- 元々
package.json"dependencies": { 省略 "nuxt": "^2.12.2", "node-sass": "^4.14.0", "sass-loader": "^8.0.0" },
- アップデート後
package.json"dependencies": { 省略 "nuxt": "^2.14.7", "sass": "^1.27.0", "sass-loader": "^10.0.4" },所感
少し触ってみてるけど特に挙動は変わらない模様です。もともとのsassってすごくビルドが遅くてnode-sassが流行ってきたとかだった気がしたけど(嘘かもしれない)知らぬ間にDartで作り替えられてたんですね。
僕はそのまま移行できた感じですが、そのままスッと移行してプロジェクトに影響でないかは各自の判断でお願いします。
おまけ
これ完全にタイミング悪かった問題だと思うんですけど、node-sassのGitHubリポジトリだとNode.js15系に対応した5.0.0がリリースされていて......
npm側ではまだ対応されてないという感じで
これによってnode-sassを入れ直してもバージョン指定して入れてもうまくいかず、
Node.js v15系でnode-sassのインストールエラーがずっと変わらなかったです苦笑結果的に今回はこれを回避する話ではなかったけど出てたエラーもメモ
・ ・ ・ [3/4] ? Linking dependencies... warning "@nuxtjs/markdownit > raw-loader@4.0.2" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0". warning "nuxt > @nuxt/components@1.1.1" has unmet peer dependency "webpack@^4.0.0". warning " > sass-loader@10.0.4" has unmet peer dependency "webpack@^4.36.0 || ^5.0.0". [4/4] ? Building fresh packages... error /Users/n0bisuke/Documents/hogehoge/node_modules/node-sass: Command failed. Exit code: 1 Command: node scripts/build.js Arguments:
- 投稿日:2020-10-27T16:04:37+09:00
react-transition-group
react-transition-group
react-transition-groupはReactでCSSアニメーションを扱う為のライブラリです。
見栄えのするモーション自体を提供してくれるわけではなく、CSSを適用するタイミングを提供してくれるので、自分でアニメーションのCSSを書いてアニメーションさせます。下記の4つのコンポーネントが提供されます。
- Transition
- CSSTransition
- SwitchTransition
- TransitionGroup
今回はcreate-react-appを使い、CSS(SCSS)はCSSModulesを使っていきます。
※レンダリング回数削減などパフォーマンス面については扱いません。インストール
terminal//create-react-appのインストール npx create-react-app プロジェクト名 //プロジェクト直下に移動 cd プロジェクト名 //node-sassのインストール(scssではなくcssを使う場合は不要です。) npm i -D node-sass //react-transition-groupのインストール npm i -S react-transition-group //起動 npm startsrc/App.jsの不要な部分を削除します。
src/App/jsimport React from "react"; import "./App.css"; function App() { return ( <div className="App"> {/* ここにこれから作るコンポーネントを配置 */} </div> ); } export default App;Transition
シンプルなトランジション
どのタイミングでinと状態が変化しているか視覚化しています。
src\components\singleTransition\SingleTransition.jsimport React, { useState } from "react"; import { Transition } from "react-transition-group"; import Style from "./singleTransition.module.scss"; //トランジションのスタイル4種類を定義(使わないものは省略可能) const transitionStyle = { entering: { transition: "all 1s ease", transform: "translateY(220px) ", backgroundColor:"red" }, entered: { transition: "all 1s ease", transform: "translateY(220px) ", backgroundColor:"green" }, exiting: { transition: "all 1s ease", transform: "translateY(0)", backgroundColor: "blue", }, exited: { transition: "all 1s ease", transform: "translateY(0)", backgroundColor: "gray", }, }; //SingleTransitionコンポーネント const SingleTransition = () => { //マウントの状態を管理 const [mount, setMount] = useState(false); //マウントのオンオフを切り替える const changer = () => { setMount(!mount); }; return ( <div className={Style.wrapper}> <button onClick={changer}>inの切り替え</button> <div className={Style.circleGroup}> <div className={Style.circleMember}> <Transition in={mount} timeout={1000} > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {mount ? "in=true" : "in=false"}</p> <p className={Style.circleText}> {state}</p> </div> </div>} </Transition> </div> </div> </div> ); } export default SingleTransition;src\components\singleTransition\singleTransition.module.scss.circleGroup { display: flex; justify-content: space-between; margin: 100px auto; width: 1000px; } .circleMember { text-align: center; width: 100px; } .circleShape { align-items: center; background-color: red; border-radius: 50px; display: flex; height: 100px; justify-content: center; width: 100px; } .circleText { color: white; } button { margin: 0 auto; display: block; }src\App.jsimport React from "react"; import SingleTransition from "./components/singleTransition/SingleTransition"; function App() { return ( <div className="App"> <ChainTransition /> </div> ); } export default App;classNameでstateを含むクラス名を指定することで、transitionの状態によって独自のクラスを適用する事もできます
必須のProps
in
inの状態 結果 inがtrueになる マウント開始 inがfalseになる アンマウント開始 timeout
entering、exitingのトランジションを使う場合で、addEndListenerを設定しない場合は必須です。
timeoutの指定による状態の変化
状態 初期 timeoutで指定した時間経過後 マウント時 entering entered アンマウント時 exiting exited 各transitionに個別にタイムアウトを指定することもできます。
sampletimeout={{ appear: 500, enter: 300, exit: 500, }}addEndListener
entering、exitingのトランジションを使う場合、timeoutを設定しない場合は必須です。
カスタムのtransition終了トリガーを追加して、動的にtimeout時間を設定したい場合に使用します。sample<Transition in={mount} {...callBacks} timeout={1000} > //↓ <Transition in={mount} {...callBacks} addEndListener={(node, done) => node.addEventListener("transitionend", done, false)}>inがtrueになる度に追加されるので、毎回doneを呼ぶタイミングでイベントの解除が必要です。
timeoutとaddEndListener
timeoutoとaddEndListenerをどちらも指定しないとコンポーネントのトランジションはenteredとexitedだけを繰り返します。
両方指定した場合はaddEndListenerはフォールバックとして使用されます。4つの状態
inとtimeoutの組み合わせで、コンポーネントに4つの状態が提供されます。
使用しないものについては、省略可能です。
- entering
- enterd
- exiting
- exited
enteringとexitingのtransitionの時間の長さについては、通常コンポーネントのtimeoutの値とそろえますが、あえて違う値にすることもできます。
inとtimeout以外のProps
src\components\otherProps\OtherProps.jsimport React, { useState } from "react"; import { Transition } from "react-transition-group"; import Style from "./otherProps.module.scss"; //アニメーションのスタイル4種類を定義(使わないものは省略可能) const transitionStyle = { entering: { transition: "all 1s ease", transform: "translateY(220px) ", backgroundColor:"red" }, entered: { transition: "all 1s ease", transform: "translateY(220px) ", backgroundColor:"green" }, exiting: { transition: "all 1s ease", transform: "translateY(0)", backgroundColor: "blue", }, exited: { transition: "all 1s ease", transform: "translateY(0)", backgroundColor: "gray", }, }; const OtherProps = () => { //マウントの状態を管理 const [mount, setMount] = useState(false); //マウントのオンオフを切り替える const changer = () => { setMount(!mount); }; return ( <div className={Style.wrapper}> <button onClick={changer}>inの切り替え</button> <div className={Style.circleGroup}> <div className={Style.circleMember}> <p>Normal</p> <Transition in={mount} timeout={1000} > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {mount ? "in=true" : "in=false"}</p> <p className={Style.circleText}> {state}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>mountOnEnter</p> <Transition in={mount} timeout={1000} mountOnEnter > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {mount ? "in=true" : "in=false"}</p> <p className={Style.circleText}> {state}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>unmountOnExit</p> <Transition in={mount} timeout={1000} unmountOnExit > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {mount ? "in=true" : "in=false"}</p> <p className={Style.circleText}> {state}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>enter=false</p> <Transition in={mount} timeout={1000} enter={false} > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {mount ? "in=true" : "in=false"}</p> <p className={Style.circleText}> {state}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>exit=false</p> <Transition in={mount} timeout={1000} exit={false} > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {mount ? "in=true" : "in=false"}</p> <p className={Style.circleText}> {state}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>nodeRef</p> <Transition in={mount} timeout={1000} nodeRef > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {mount ? "in=true" : "in=false"}</p> <p className={Style.circleText}> {state}</p> </div> </div>} </Transition> </div> </div> </div> ); } export default OtherProps;src\components\otherProps\otherProps.module.scss.circleGroup { display: flex; justify-content: space-between; margin: 100px auto; width: 1000px; } .circleMember { text-align: center; width: 100px; } .circleShape { align-items: center; background-color: red; border-radius: 50px; display: flex; height: 100px; justify-content: center; width: 100px; } .circleText { color: white; } button { margin: 0 auto; display: block; }src\App.jsimport React from "react"; import OtherProps from "./components/otherProps/OtherProps"; function App() { return ( <div className="App"> <ChainTransition /> </div> ); } export default App;in、timeout以外のProps
Props 未指定時(暗黙的に指定されている) 未指定時から変更する場合 変更時内容 enter enter、enter={true} enter={false} enteringにならない exit exit、exit={true} exit={false} exitingにならない mountOnEnter mountOnEnter={false} mountOnEnter、mountOnEnter={true} 遅延マウント(初回のみ) unmountOnExit unmountOnExit={false} unmountOnExit、unmountOnExit={true} exitedでアンマウント nodRef nodeRef={false} nodeRef、nodeRef={true} entering、exitingにならない appear appear={false} appear={true} appearの動作(in=trueと一緒に指定) appear
各コンポーネントのinの初期値をtrue、appear=trueにします。
変更箇所のみ
src\components\otherProps\OtherProps.js//mountの初期値をtrueにして、in=trueにする const [mount, setMount] = useState(true); //各コンポーネントにappear={true}を追加 <Transition in={mount} timeout={1000} {...callBacksNormal} appear={true} >コールバック
状態の変化時に処理を行う事ができます。
sample//状態変化時のコールバック const callBacks = { onEnter: () => console.log("enterです"), onEntered: () => console.("enteredです"), onExit: () => console.log("exitです"), onExited: () => console.log("exitedです"), }; //Transitionに{...callBacks}を追加 <Transition in={mount} {...callBacks} timeout={1000} >Transitionを連鎖させる
src\components\chainTransition\ChainTransition.jsimport React, { useState } from "react"; import { Transition } from "react-transition-group"; import Style from "./chaintransition.module.scss"; //アニメーションのスタイル4種類を定義(使わないものは省略可能) const transitionStyle = { entering: { transition: "all 1s ease", transform: "translateY(220px) ", backgroundColor:"red" }, entered: { transition: "all 1s ease", transform: "translateY(220px) ", backgroundColor:"green" }, exiting: { transition: "all 1s ease", transform: "translateY(0)", backgroundColor: "blue", }, exited: { transition: "all 1s ease", transform: "translateY(0)", backgroundColor: "gray", }, }; //ChainTransitionコンポーネント const ChainTransition = () => { //マウントの状態を管理 const [firstCircle, setFirstCircle] = useState(false); const [secondCircle, setSecondCircle] = useState(false); const [thirdCircle, setThirdCircle] = useState(false); const [fourthCircle, setFourthCircle] = useState(false); const [fifthCircle, setFifthCircle] = useState(true); const [sixthCircle, setSixthCircle] = useState(true); const [seventhCircle, setSeventhCircle] = useState(true); //マウントのオンオフを切り替える const changer = () => { setFirstCircle(!firstCircle); }; const callBacks = { onEnter: () => { setSecondCircle(true); }, onEntering: () =>{ setThirdCircle(true); }, onEntered: () => { setFourthCircle(true); }, onExit: () => { setFifthCircle(false); }, onExiting: () => { setSixthCircle(false); }, onExited: () => { setSeventhCircle(false); }, }; return ( <div className={Style.wrapper}> <button onClick={changer}>inの切り替え</button> <div className={Style.circleGroup}> <div className={Style.circleMember}> <p>(trigger)</p> <Transition in={firstCircle} timeout={1000} {...callBacks}> {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {firstCircle ? "in=true" : "in=false"}</p> <p className={Style.circleText}> {state}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>onEnter</p> <Transition in={secondCircle} timeout={1000} > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {secondCircle ? "in=true" : "in=false"}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>onEntering</p> <Transition in={thirdCircle} timeout={1000}> {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {thirdCircle ? "in=true" : "in=false"}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>onEntered</p> <Transition in={fourthCircle} timeout={1000} > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {fourthCircle ? "in=true" : "in=false"}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>onExit</p> <Transition in={fifthCircle} timeout={1000} > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {fifthCircle ? "in=true" : "in=false"}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>onExiting</p> <Transition in={fifthCircle} timeout={1000} > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {sixthCircle ? "in=true" : "in=false"}</p> </div> </div>} </Transition> </div> <div className={Style.circleMember}> <p>onExited</p> <Transition in={seventhCircle} timeout={1000} > {(state) => <div className={Style.circleShape} style={transitionStyle[state]} > <div> <p className={Style.circleText}> {seventhCircle ? "in=true" : "in=false"}</p> </div> </div>} </Transition> </div> </div> </div> ); } export default ChainTransition;src\components\chainTransition\chaintransition.module.scss.circleGroup { display: flex; justify-content: space-between; margin: 100px auto; width: 1000px; } .circleMember { text-align: center; width: 100px; } .circleShape { align-items: center; background-color: red; border-radius: 50px; color: red; display: flex; height: 100px; justify-content: center; width: 100px; } .circleText { color: white; } button { margin: 0 auto; display: block; }src\App.jsimport React from "react"; import ChainTransition from "./components/chainTransition/ChainTransition"; function App() { return ( <div className="App"> <ChainTransition /> </div> ); } export default App;コールバックのタイミング
nodeRef Propが渡されると、コールバックを使ってもnodeは渡されません。
コールバックの種類 適用タイミング onEnter entering適用前 onEntering entering適用時 onEntered entered適用時 onExit exiting適用前 onExiting exiting適用時 onExited exited適用時 CSSTransition
Transitionとの大きな違いは、親要素の名前がになっていることと、CSSTransition用のPropsとしてclassNameが使える事です。
src\components\singleCSSTransition\SingleCSSTransition.jsimport React, { useState } from "react"; import { CSSTransition } from "react-transition-group"; import Style from "./singleCSSTransition.module.scss"; //SingleCSSTransitionコンポーネント const SingleCSSTransition = () => { //マウントの状態を管理 const [mount, setMount] = useState(false); //マウントのオンオフを切り替える const changer = () => { setMount(!mount); }; return ( <div className={Style.wrapper}> <button onClick={changer}>inの切り替え</button> <div className={Style.circleGroup} > <div className={Style.circleMember} > <CSSTransition in={mount} timeout={1000} classNames={{ appear:Style.testAppear, appearActive:Style.testAppearActive, appearDone:Style.testAppearDone, enter:Style.testEnter, enterActive: Style.testEnterActive, enterDone:Style.testEnterDone, exit:Style.testExit, exitActive: Style.testExitActive, exitDone: Style.testExitDone, }} > {(state) => <div className={Style.circleShape} > <div> <p className={Style.circleText} > {mount ? "in=true" : "in=false"}</p> <p className={Style.circleText} > {state}</p> </div> </div>} </CSSTransition> </div> </div> </div> ); } export default SingleCSSTransition;src\components\singleCSSTransition\singleCSSTransition.module.scss.circleGroup { display: flex; justify-content: space-between; margin: 100px auto; width: 1000px; } .circleMember { text-align: center; width: 100px; } .circleShape { align-items: center; background-color: red; border-radius: 50px; display: flex; height: 100px; justify-content: center; width: 100px; } .circleText { color: white; } button { margin: 0 auto; display: block; } //apperaの最初のフレーム瞬間の状態 .testAppear { transition: all 1s ease; border-radius: 10px; transform: rotateZ(30deg) scale(2); opacity: 0; } //.testAppearの直後の状態 .testAppearActive { } //appearの最終フレームの状態 .testAppearDone { transition: all 1s ease; border-radius: 50px; opacity: 1; } //enter中の最初のフレーム瞬間の状態 .testEnter { transition: all 1s ease; transform: translateY(0); background-color: red; } //enter中の最終フレームの状態 .testEnterActive { transition: all 1s ease; transform: translateY(220px); background-color: red; } //enter完了の状態 .testEnterDone { transition: all 1s ease; transform: translateY(220px); background-color: green; } //exitの初期状態 .testExit { transition: all 1s ease; transform: translateY(220px); background-color: green; } //exit中の最終フレームの状態 .testExitActive { transition: all 1s ease; transform: translateY(0); background-color: blue; } //exit完了 .testExitDone { transition: all 1s ease; transform: translateY(0); background-color: gray; }src\App.jsimport React from "react"; import SingleCSSTransition from "./components/singleCSSTransition/SingleCSSTransition"; function App() { return ( <div className="App"> <SingleCSSTransition /> </div> ); } export default App;className
CSSTransitionのclassName Propsでは、各状態にクラス名を付けられます。
sample<CSSTransition classNames={{ enter:Style.testEnter, enterActive: Style.testEnterActive, enterDone:Style.testEnterDone, exit:Style.testExit, exitActive: Style.testExitActive, exitDone: Style.testExitDone, }}>今回はCSSModulesを採用しているので、使用する各状態用用のクラスに個別に名前をつける必要がありますが、他の方法でCSSを指定している場合接頭辞を指定すれば、自動的にクラス名が生成されます。
※上記のように任意のクラス名に変更することも可能です。table:接頭辞をtestにした場合
- active done test-appear test-appear-active test-appear-done test-enter test-enter-active test-enter-done test-enter test-exit-active test-exit-done ※appearクラスの追加タイミングを追ってみると、enterクラスと同時に適用されてしまうので使い方には注意が必要です。
appear
コンポーネントのinの初期値をtrue、appear=trueにします。
変更箇所のみ記載
src\components\singleCSSTransition\SingleCSSTransition.js//mountの初期値をtrueにしてin=trueにする const [mount, setMount] = useState(true); //CSSTransitonにappear={true}を追加 <CSSTransition in={mount} {...callBacks} timeout={1000} classNames={{ appear:Style.testAppear, appearActive:Style.testAppearActive, appearDone:Style.testAppearDone, enter:Style.testEnter, enterActive: Style.testEnterActive, enterDone:Style.testEnterDone, exit:Style.testExit, exitActive: Style.testExitActive, exitDone: Style.testExitDone, }} appear={true} >SwitchTransition
SwitchTransitionでTransition、CSSTransitionを囲みます。
SwitchTransition独自のPropsとしてmodeがあります。src\components\transitionSwitching\TransitionSwiching.jsimport React, { useState } from "react"; import { SwitchTransition, Transition } from "react-transition-group"; import Style from "./transitionSwitching.module.scss"; //トランジションのスタイル4種類を定義(使わないものは省略可能) const transitionStyle = { entering: { transition: "all 0.5s ease", transform: "translateX(-150px) ", backgroundColor:"red", opacity:"0", }, entered: { transition: "all 0.5s ease", transform: "translateX(150px)", backgroundColor:"green", opacity:"1", }, exiting: { transition: "all 0.5s ease", transform: "translateX(-150px)", backgroundColor: "blue", opacity:"1", }, exited: { transition: "all 0.5s ease", transform: "translateX(150px)", backgroundColor: "gray", opacity:"0", }, }; const TransitionSwitch=()=> { const [name, setName] = useState(false); return ( <div> <button onClick={() => setName(!name)}>Switching</button> <div className={Style.squareWrapper}> <SwitchTransition mode="in-out"> <Transition key={name ? "aaa" : "bbb"} timeout={500} unmountOnExit mountOnEnter > {state => <div state={state} style={transitionStyle[state]} className={Style.square}> {name ? <p>AAA</p> : <p>BBB</p>} </div>} </Transition> </SwitchTransition> </div> </div> ); } export default TransitionSwitch;src\components\transitionSwitching\transitionSwitching.module.scss/* 簡易リセット */ * { box-sizing: border-box; margin: 0; padding: 0; } /* スクロールバー常時表示 */ html { overflow-y: scroll; } .squareWrapper { margin-top: 100px; position: relative; } .square { background-color: blue; display: block; width: 200px; padding: 10px 20px; margin: 0 auto; color: #fff; text-align: center; position: absolute; left: calc(50% - 100px); } button { margin: 0 auto; display: block; }src\App.jsimport React from "react"; import TransitionSwitching from "./components/transitionSwitching/TransitionSwiching"; function App() { return ( <div className="App"> <TransitionSwitching /> </div> ); } export default App;mode
mode 内容 out-in 先に現在の要素がアウトし、完了後に新しい要素がインする in-out 先に新しい要素がインし、完了後に現在の要素がアウトする TransitionGroupとSwitchTransitionの使い分け
古い子要素のoutと新しい子要素のinを同時に行う場合は、TransitionGroupを使用します。
TransitionGroup
TransitionGroup は、Transition または CSSTransition のリストを管理する為のコンポーネントです。
TransitionGroupでTransitionやCSSTransitionをラップします。
各TransitionやCSSTransitionにはinは不要で、代わりにユニークなKeyを設定します。src\components\transitionList\TransitionList.jsimport React, { useState,useRef } from "react"; import {TransitionGroup,Transition} from 'react-transition-group'; import Style from "./transitionList.module.scss"; //トランジションのスタイル4種類を定義(使わないものは省略可能) const transitionStyle = { entering: { transition: "all 0.2s ease", opacity:"0" }, entered: { transition: "all 0.2s ease", opacity:"1" }, exiting: { transition: "all 0.2s ease", opacity:"0" }, exited: { transition: "all 0.2s ease", opacity:"0" }, }; //TransitionGroupコンポーネント const TransitionList = () => { //inputに入力中の文字 const [inputting,setInputting]=useState(""); //input関連付け用のref const inputRef =useRef(); //最後のID番号管理用 const [lastId,setLastId]=useState(3); //初期アイテムリスト const initialList=[ {id:0,word:"React"}, {id:1,word:"Hooks"}, {id:2,word:"Transition"}, ]; //アイテムリストに初期アイテムをセット const [items,setItems]=useState(initialList); //アイテムの追加処理 const adder = () => { if(inputting){ setItems( items=>[ ...items, {id:lastId, word:inputting} ] )}; //IDのインクリメント setLastId(prevId=>prevId+1); //inputのクリア setInputting(""); inputRef.current.value=""; }; //リセット const reseter=()=>{ setInputting(""); inputRef.current.value=""; setItems(initialList); } //form送信防止 const stopSubmit=(e)=>{ e.preventDefault(); } return ( <div className={Style.wrapper}> <form action="" onSubmit={stopSubmit} className={Style.controller} > <div className={Style.controllerAddGroup}> <input className={Style.controllerInput} ref={inputRef} type="text" onChange={ (e)=>setInputting(e.target.value) } /> <button className={Style.button} onClick={adder} disabled={!inputting ? true:false} >追加</button> </div> <button className={Style.button} onClick={reseter}> 初期化 </button> </form> <div className={Style.cardGroup}> <TransitionGroup className={Style.cardInner}> {items.map(({id,word})=>( <Transition key={id} timeout={200} > {(state) => <div className={Style.cardShape} style={transitionStyle[state]} > <button className={Style.button} onClick={() => setItems(items => items.filter(item => item.id !== id) ) } > 削除 </button> <p className={Style.cardWord}> {word}</p> <p className={Style.cardTransition}> {state}</p> </div>} </Transition> ) ) } </TransitionGroup> </div> </div> ); } export default TransitionList;src\components\transitionList\transitionList.module.scss/* 簡易リセット */ * { box-sizing: border-box; margin: 0; padding: 0; } /* スクロールバー常時表示 */ html { overflow-y: scroll; } @media screen and (min-width: 651px) { .wrapper { margin: 0 auto; max-width: 600px; } .controller { background-color: #ffebee; border-radius: 4px; display: flex; filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.6)); justify-content: space-between; margin-top: 40px; padding: 20px; } .controllerAddGroup { display: flex; } .controllerInput { border: none; padding: 10px; background-color: #ffcdd2; transition: all 0.5s ease; &:focus { background-color: #ffffff; outline: none; } } .button { appearance: none; background-color: #ec407a; border: none; color: #ffffff; cursor: pointer; font-family: inherit; font-size: 1rem; margin: 0; outline: none; padding: 10px 20px; transition: all 0.5s ease; width: 100px; &:hover { background-color: #ad1457; } &:disabled { opacity: 0; cursor: default; } } .cardGroup { display: flex; justify-content: space-between; margin-top: 40px; max-width: 1000px; } .cardInner { width: 100%; } .cardShape { background-color: #e3f2fd; border-radius: 4px; display: flex; filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.6)); justify-content: space-between; padding: 20px; max-width: 600px; & + .cardShape { margin-top: 20px; } } .cardWord { color: #212121; display: inline; font-size: 2rem; font-weight: bold; line-height: 1.2; overflow: hidden; padding: 0 20px; text-align: left; text-overflow: ellipsis; white-space: nowrap; width: 400px; } .cardTransition { background-color: #1e88e5; color: #ffffff; border-radius: 4px; padding: 10px; } } @media screen and (max-width: 650px) { .wrapper { margin: 0 auto; max-width: 600px; padding: 0 10px; } .controller { background-color: #ffebee; border-radius: 4px; filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.6)); margin-top: 20px; padding: 15px; & > .button { margin-left: auto; margin-right: auto; display: block; margin-top: 10px; } } .controllerAddGroup { display: flex; width: 100%; justify-content: space-between; } .controllerInput { border: none; padding: 10px; background-color: #ffcdd2; transition: all 0.5s ease; width: 100%; &:focus { background-color: #ffffff; outline: none; } } .button { appearance: none; background-color: #ec407a; border: none; color: #ffffff; cursor: pointer; font-family: inherit; font-size: 0.5rem; margin: 0; outline: none; padding: 10px; transition: all 0.5s ease; width: 100px; &:hover { background-color: #ad1457; } &:disabled { opacity: 0; cursor: default; } } .cardGroup { display: flex; justify-content: space-between; margin-top: 20px; max-width: 1000px; } .cardInner { width: 100%; } .cardShape { background-color: #e3f2fd; border-radius: 4px; display: flex; filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.6)); justify-content: space-between; padding: 15px; max-width: 600px; & + .cardShape { margin-top: 10px; } } .cardWord { color: #212121; display: inline; font-size: 1rem; font-weight: bold; line-height: 1.2; overflow: hidden; padding: 0 20px; text-align: left; text-overflow: ellipsis; white-space: nowrap; width: 400px; } .cardTransition { background-color: #1e88e5; color: #ffffff; border-radius: 4px; padding: 5px; } }src\App.jsimport React from "react"; import TransitionList from "./components/transitionList/TransitionList"; function App() { return ( <div className="App"> <TransitionList /> </div> ); } export default App;リポジトリ
- 投稿日:2020-10-27T14:50:15+09:00
javascript 正規表現で置換 str replace
javascriptで正規表現をやりたい
text.replace("/<p>/","");
と
マッチング部分を " で囲むとバグるので注意var text = 'youryour'; //文字列置換 (各最短マッチ) var tmp = text.replace(/<p>|<h3>|<blockquote.*?<\/blockquote>|<img.*?>|<iframe.*?<\/iframe>|<code.*?<\/code>|<pre.*?<\/pre>/gsim, ''); //</p> もしくは </h3> を改行に変換 var tmp = tmp.replace(/<\/p>|<\/h3>/gsim,"\n");修飾子の意味
m : 複数行検索
s : を改行文字と一致するようにします。
i : 大文字、小文字の区別をしない
g : 最初にマッチした時点でマッチする文字列を探すのを終了せず全てのマッチする文字列を探します。複数マッチングするかを調べることができます。
- 投稿日:2020-10-27T10:59:47+09:00
REACTチュートリアルメモ
REACTのチュートリアルについてのメモです
1. 開発環境を作る
公式チュートリアルに沿って進めていきます
https://ja.reactjs.org/tutorial/tutorial.htmlブラウザ上で開発するかローカルで開発するか
ブラウザ上で開発出来るスターターコードを使えば開発環境の整備は不要ですが、今回僕はローカルでやりました。
ローカル環境を作る
以下の手順でローカルのプロジェクトを作成してサーバーを起動します
1. Nodejsをインストールする
公式サイトからダウンロードしてインストールします
https://nodejs.org/ja/2. Create React Appでプロジェクトフォルダを作る
適当なディレクトリで以下を実行します
terminalnpx create-react-app my-app
3. サーバーを起動する
上記を実行するとmy-appというディレクトリが作成されて、デフォルトで必要なファイルが生成されていますので、my-appに移動してnpm startすればサーバーが待機します。
terminalcd my-app npm start
http://localhost:3000/ を開くと以下のように表示される筈です
チュートリアルでは作成されたファイルを使用しないので/src/の中のファイルを全部消して作り直します
LinuxまたはMacの場合cd my-app cd src rm -f * cd ..
Windowsの場合cd my-app cd src del * cd ..
2. Gameクラスを作成して表示する
チュートリアルでは以下のような3目並べのゲームボードを作成します
https://codepen.io/gaearon/pen/gWWZgR?editors=0010
これを実現するため、1マスに該当するSquareクラスを9個並べてBoardクラスにして、これをGameクラスのプロパティとしてもたせた上で、ReactDOM.render()に表示してもらいます。/src/index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; class Square extends React.Component { render() { return ( <button className="square"> {/* TODO */} </button> ); } } class Board extends React.Component { renderSquare(i) { return <Square />; } render() { const status = 'Next player: X'; return ( <div> <div className="status">{status}</div> <div className="board-row"> {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)} </div> <div className="board-row"> {this.renderSquare(3)} {this.renderSquare(4)} {this.renderSquare(5)} </div> <div className="board-row"> {this.renderSquare(6)} {this.renderSquare(7)} {this.renderSquare(8)} </div> </div> ); } } class Game extends React.Component { render() { return ( <div className="game"> <div className="game-board"> <Board /> </div> <div className="game-info"> <div>{/* status */}</div> <ol>{/* TODO */}</ol> </div> </div> ); } } // ======================================== ReactDOM.render( <Game />, document.getElementById('root') );/src/index.cssbody { font: 14px "Century Gothic", Futura, sans-serif; margin: 20px; } ol, ul { padding-left: 30px; } .board-row:after { clear: both; content: ""; display: table; } .status { margin-bottom: 10px; } .square { background: #fff; border: 1px solid #999; float: left; font-size: 24px; font-weight: bold; line-height: 34px; height: 34px; margin-right: -1px; margin-top: -1px; padding: 0; text-align: center; width: 34px; } .square:focus { outline: none; } .kbd-navigation .square:focus { background: #ddd; } .game { display: flex; flex-direction: row; } .game-info { margin-left: 20px; }これで先程と同様にnpm startして http://localhost:3000/ にアクセスすると、以下のように表示される筈です。
3. クラスに値を持たせる/値を参照する
以下のようにしてやればSquareクラスに文字を表示できます
index.jsclass Square extends React.Component { render() { return ( <button className="square"> {"a"} </button> ); } }classに持たせた値を参照して表示する場合には以下のようにします。Square.render()内でthis.props.valueを参照しているので、Squareクラスのprops(プロパティ)のvalueを参照していることになります。
index.jsclass Square extends React.Component { render() { return ( <button className="square"> {this.props.value} </button> ); } }BoardクラスからSquareクラスを作る際に引数としてvalueプロパティを作るようにします。
index.jsclass Board extends React.Component { renderSquare(i) { return <Square value={i} />; } }Boardクラスの引数通りに0~8の番号を表示するようになりました
4. インタラクティブなコンポーネントを作る
buttonにonClick={}を追加することでSquareクラスのオブジェクトがクリックされたときにalertを出すようにしてみましょう。以下のように関数を渡してやることでクリックされたときにreactがalert()を出してくれます。
index.jsclass Square extends React.Component { render() { return ( <button className="square" onClick={function() { alert('click'); }}> {this.props.value} </button> ); } }なお、上記のようにfunctionが重なるとthisがどれを指しているのか分かりにくくなるので下のようにarrow関数で書く方が良いそうです。
index.jsclass Square extends React.Component { render() { return ( <button className="square" onClick={() => alert('click')}> {this.props.value} </button> ); } }5. クリックしたらXを表示するようにする
まず、コンストラクタ(クラスが最初に呼ばれた時に実行される関数)を追加して状態を記憶させるthis.stateを初期化するようにします。最初にsuper(props)としてるのは、ES2015(ES6)のJavaScriptクラスではsuper()クラスを呼ぶまでthisもsuperも使えなくなる仕様になっているからだそうで、コンストラクタを書くときは常に最初にsuper(props)するように推奨されています。
続いて、buttonに表示される値をthis.state.valueに変更し、onClickでthis.state.valueを'X'に変更するようにします。
class Square extends React.Component { constructor(props) { super(props); this.state = { value: null, }; } render() { return ( <button className="square" onClick={() => this.setState({value: 'X'})}> {this.state.value} </button> ); } }6. React DevTools
ChromeまたはFirefoxの拡張機能として提供されているReact DevToolsを使うとReactアプリケーションのpropsとstateを確認できるようになります。
インストール手順は以下を参照してください
https://ja.reactjs.org/docs/optimizing-performance.html拡張機能を有効にすると、ブラウザの開発者向けツールにreactのcomponentsタグとProfilerタグが追加されて、各クラスのプロパティが確認できるようになります。
7. 子コンポーネントの値を親コンポーネントに監理させる
ここまでのコードではSquareクラスが自分で表示するための値を保持していましたが、親クラスに監理させた方がコードが分かりやすく、より壊れにくく、リファクタリングしやすくなるそうですので、そのように書き換えていきます。
まず、親クラスにコンストラクタ関数を書いてBoard.state.squaresを初期化、この値を使ってSquareオブジェクトを表示してやります。また、先程までSquareクラスに書いてあったonClickで呼び出す関数もBoardクラスにhandleClick()として書いてやります。これにより値の監理をやりやすくなります。
また、handleClick()内でthis.state.squaresの値を一旦slice()してから書き換えているのはコードをイミュータブルにする為です。元の値を直接いじらないイミュータブルな書き方にする事で変更の有無を検出しやすくしたり、変更の履歴を保存したり、render()すべきタイミングを把握しやすくする効果が期待できるそうです。
index.jsclass Board extends React.Component { constructor(props){ super(props); this.state = { squares: Array(9).fill(null), }; } handleClick(i) { const squares = this.state.squares.slice(); squares[i] = 'X'; this.setState({squares: squares}); } renderSquare(i) { return ( <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} /> ); }Squareでやっていた値の監理がなくなったのでコンストラクタは消し、onClickでは親クラスから受け取ったSquare.props.onClick()関数(中身はBoard.handleClick())をonClickで実行するように書いてやります。
index.jsclass Square extends React.Component { render() { return ( <button className="square" onClick={() => this.props.onClick()} > {this.props.value} </button> ); } }クリックするとBoardクラスにstateの値が変わり、それに合わせてXが表示されていくようになりました。
これによりSquaredはクリックされたことを親クラスに伝えるだけのコンポーネントになって、ロジックが書きやすくなりました。
8. 関数コンポーネント
上記の変更によりSquareクラスに値を持たなくなったのでクラスでなく関数で書いたほうが簡潔になります。{() => this.props.onClick()}の代わりにprops.onClickと書き換えるので、以下のようにかなり短く書くことができます。
index.jsfunction Square(props) { return ( <button className="square" onClick={props.onClick}> {props.value} </button> ); }9. 手番の処理
Xの手番の次はOにならないといけないので、次の手番がXOのどちらなのか書いてやります。BoardクラスにxIsNextプロパティを設定して、trueだったら次はXの手番、falseだったら次はOの手番として処理します。
index.jsfunction Square(props) { return ( <button className="square" onClick={props.onClick}> {props.value} </button> ); } class Board extends React.Component { constructor(props){ super(props); this.state = { squares: Array(9).fill(null), xIsNext: true, }; } handleClick(i) { const squares = this.state.squares.slice(); squares[i] = this.state.xIsNext ? 'X' : 'O'; this.setState({ squares: squares, xIsNext: !this.state.xIsNext, }); } renderSquare(i) { return ( <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} /> ); } render() { const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');10. 勝者を判定する
勝者を判定する関数を作成します。squaresは初期値がnullなのでif文からするとfalseと扱われるのを利用してnullではない値が何れかのlineすべてに入っていたら勝者は入っていた値であるという関数ですね。
index.jsfunction calculateWinner(squares) { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6], ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { return squares[a]; } } return null; }Board.render()内でcalculateWinner()を使って勝者判定を行い、勝者を表示するようにします。
render() { const winner = calculateWinner(this.state.squares); let status; if (winner){ status = 'Winner: ' + winner; } else { status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); }また、勝者が決まった後に手番が進められるとおかしいので、Board.handleClick()内でcalculateWinner()がnullでないときは手番の更新を行わないようにします。
handleClick(i) { const squares = this.state.squares.slice(); if (calculateWinner(squares) || squares[i]) { return; } squares[i] = this.state.xIsNext ? 'X' : 'O'; this.setState({ squares: squares, xIsNext: !this.state.xIsNext, }); }11. 履歴の保存
履歴を残しておいて手番を戻ることができるようにします。具体的には、以下のようなフォーマットで履歴を保存しておいて、戻れるようにしてやります。
こんな風に保存したいhistory = [ // Before first move { squares: [ null, null, null, null, null, null, null, null, null, ] }, // After first move { squares: [ null, null, null, null, 'X', null, null, null, null, ] }, // After second move { squares: [ null, null, null, null, 'X', null, null, null, 'O', ] }, // ... ]上記のhistoryをGame.props.statesとして管理するように書き換えます。
handleClick()でhistoryに最新のsquaresを追加して上書きすることで1つずつ追加される挙動を作っています。
チュートリアルではこれに並行してBoardクラスで管理してた値をまとめてGameクラスで管理するように変更する作業もやってますので、以下のようにごっそり書き換えてしまってますが、自分でやるときはまず値の管理を置き換えて、正常に動いてから機能追加した方が良いと思います。
index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; function Square(props) { return ( <button className="square" onClick={props.onClick}> {props.value} </button> ); } class Board extends React.Component { renderSquare(i) { return ( <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)} /> ); } render() { return ( <div> <div className="board-row"> {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)} </div> <div className="board-row"> {this.renderSquare(3)} {this.renderSquare(4)} {this.renderSquare(5)} </div> <div className="board-row"> {this.renderSquare(6)} {this.renderSquare(7)} {this.renderSquare(8)} </div> </div> ); } } class Game extends React.Component { constructor(props) { super(props); this.state = { history: [{ squares: Array(9).fill(null), }], xIsNext: true, } } handleClick(i) { const history = this.state.history; const current = history[history.length - 1]; const squares = current.squares.slice(); if (calculateWinner(squares) || squares[i]) { return; } squares[i] = this.state.xIsNext ? 'X' : 'O'; this.setState({ history: history.concat([{ squares: squares }]), xIsNext: !this.state.xIsNext, }); } render() { const history = this.state.history; const current = history[history.length - 1]; const winner = calculateWinner(current.squares); let status; if (winner){ status = 'Winner: ' + winner; } else { status = 'Next player: ' + (this.props.xIsNext ? 'X' : 'O'); } return ( <div className="game"> <div className="game-board"> <Board squares={current.squares} onClick={(i) => this.handleClick(i)} /> </div> <div className="game-info"> <div>{status}</div> <ol>{/* TODO */}</ol> </div> </div> ); } } // ======================================== ReactDOM.render( <Game />, document.getElementById('root') ); function calculateWinner(squares) { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6], ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { return squares[a]; } } return null; }12. 履歴の表示
とりあえず表示
ここでJavaScriptの標準機能である配列のmapメソッドを使います
mapメソッドの動作例const numbers = [1, 2, 3]; const doubled = numbers.map(x => x * 2); // [2, 4, 6]Game.render()内でhistoryの値からbutton表示させるhtmlを生成し、olタグ内に貼り付けることで戻るためのbuttonを作ります。
index.jsrender() { const history = this.state.history; const current = history[history.length - 1]; const winner = calculateWinner(current.squares); const moves = history.map((step, move) => { const desc = move ? 'Go to move #' + move : 'Go to game start'; return ( <li> <button onClick={() => this.jumpTo(move)}>{desc}</button> </li> ) }) let status; if (winner){ status = 'Winner: ' + winner; } else { status = 'Next player: ' + (this.props.xIsNext ? 'X' : 'O'); } return ( <div className="game"> <div className="game-board"> <Board squares={current.squares} onClick={(i) => this.handleClick(i)} /> </div> <div className="game-info"> <div>{status}</div> <ol>{moves}</ol> </div> </div> ); }この時点ではbuttonタグのonClick()で呼ばれるjumpTo()を書いていないのでbuttonを押すとエラー停止になります。
historyにkeyを追加
この時点で実行するとconsoleに以下のような警告がでます
リスト項目に固有のkeyが置かれていないので良くないですよ、と警告されています。順番だけで管理してると項目が増えた時に混乱するのでチュートリアルにも「動的なリストを構築する場合は正しい key を割り当てることが強く推奨されます」とありますから、素直に従ってkeyを追加してやります。具体的には
{hoge(move)}を{hoge(move)}と書き換えます。reactがkey={move}を認識してそこに書かれたhoge(move)にGame.state.moveを渡せば良いんだと理解してくれます。render() { const history = this.state.history; const current = history[history.length - 1]; const winner = calculateWinner(current.squares); const moves = history.map((step, move) => { const desc = move ? 'Go to move #' + move : 'Go to game start'; return ( <li key={move}> <button onClick={() => this.jumpTo(move)}>{desc}</button> </li> ) })jumpTo()を書く
戻るための関数を書いていきます。まずconstructorに現在表示している手番が何番目かを示すstepNumberを設定し、jumpTo()するとstepNumberとxIsNextをその時点での値に戻し、その後render()する際に表示する盤面を最新のものhistory[history.length - 1]ではなく指定されたものhistory[stepNumber]に変更、handleClick()されたら手戻りしたところまでのhistoryに追加して書き加えていくようにします。
jumpTo()の時点でhistoryを書き換えてしまうと履歴をウロウロすることが出来なくなっちゃいますので新たに手番が指されたときに更新するようにしてるわけですね。
index.jsclass Game extends React.Component { constructor(props) { super(props); this.state = { history: [{ squares: Array(9).fill(null), }], stepNumber: 0, xIsNext: true, } } handleClick(i) { const history = this.state.history.slice(0, this.state.stepNumber + 1); const current = history[history.length - 1]; const squares = current.squares.slice(); if (calculateWinner(squares) || squares[i]) { return; } squares[i] = this.state.xIsNext ? 'X' : 'O'; this.setState({ history: history.concat([{ squares: squares }]), stepNumber: history.length, xIsNext: !this.state.xIsNext, }); } jumpTo(step) { this.setState({ stepNumber: step, xIsNext: (step % 2) === 0, }); } render() { const history = this.state.history; const current = history[this.state.stepNumber]; const winner = calculateWinner(current.squares); const moves = history.map((step, move) => { const desc = move ? 'Go to move #' + move : 'Go to game start'; return ( <li key={move}> <button onClick={() => this.jumpTo(move)}>{desc}</button> </li> ) })ということで完成したコードがこちらです
https://codepen.io/gaearon/pen/gWWZgR?editors=001013. buildする
アプリケーションが完成しましたのでデプロイ用にbuildしましょう。
まず、相対パスで書けるようにpackage.jsonに"homepage": "./"を追記します。
package.json{ "name": "my-app", "version": "0.1.0", "homepage": "./",あとはプロジェクトフォルダでnpm run buildするだけです
terminalcd my-app npm run build
プロジェクトフォルダ内にbuildフォルダが生成されています
このindex.htmlをブラウザで開くとアプリケーションが表示されます
14. 次に何をやれば良いか
この先、ドキュメントはreact.jsの主なコンセプトの解説へと続いていきます。JSXやstateのライフサイクル、イベント処理やReactの流儀など重要な項目にフォーカスして説明しているので続いて読んでいくのが良さそうです。
https://ja.reactjs.org/docs/hello-world.html
- Hello World
- JSX の導入
- 要素のレンダー
- コンポーネントと props
- state とライフサイクル
- イベント処理
- 条件付きレンダー
- リストと key
- フォーム
- state のリフトアップ
- コンポジション vs 継承
- React の流儀
感想
チュートリアルをやる前にREACTは学習コストが高くて云々という記事も多くみかけたんですが、classベースで書くやり方も情報を上位クラスで保持する書き方もそこそこ複雑なコードを書く時の作業性に効いてきそうですし、デバッグがやりやすいようにブラウザ拡張ツールでエラーコードが確認できるのもありがたいです。少なくとも、生のJavaScriptよりは大幅にコードが書きやすいのでreact.jsを食わず嫌いしてるウェブ開発ビギナーはチュートリアルだけでもやってみてほしいです。
- 投稿日:2020-10-27T09:49:31+09:00
初心者のプログラミング3
勉強の記録
勉強した内容
- ツイッターなどでよくある診断ツールの作成。
- 入力した結果を表示させる
- 表示させたものを新しく入力した場合に削除する
何をベースに勉強してるか
内容の詳細
わかったことについて
- functionは省略できる、アロー関数なるものがでてきた。
- ifに似たwhileなる物も登場、違いがまだよく分かってない!
- HTMLのbodyにjavascriptから挿入する方法。
今回書いたコード
'use strict'; const userNameInput = document.getElementById('user-name'); const assessmentButton = document.getElementById('assessment'); const resultDivided = document.getElementById('result-area'); const twwetDivided = document.getElementById('tweet-area'); /** * 指定した要素の子供をすべて削除する * @param {HTMLElement} element HTMLの要素 */ function removeAllChildren(element) { while (element.firstChild) { // result-area に、何かタグがある限りループ element.removeChild(element.firstChild); } } /** * 指定した要素に診断結果用のタグを指定する。 * @param {HTMLElement} element HTMLの要素 */ function createAssessmentResult(element, result){ // result-areaにh3タグで'診断結果'という文字を表示 const h3 = document.createElement('h3'); // h3タグを作る h3.innerText = '診断結果'; //h3タグに'診断結果'の文字列を設定 element.appendChild(h3); // result-area に h3 変数を設定 // result-areaにpタグで診断結果を表示 const p = document.createElement('p'); p.innerText = result; element.appendChild(p); }; assessmentButton.onclick = () => { let userName = userNameInput.value; if (userName.length === 0) { // 名前の入力がなかったので処理を中断 return; } removeAllChildren(resultDivided); const result = assessment(userName); createAssessmentResult(resultDivided, result); } const answers = [ '{userName}のいいところは声です。{userName}の特徴的な声は皆を惹きつけ、心に残ります。', '{userName}のいいところはまなざしです。{userName}に見つめられた人は、気になって仕方がないでしょう。', '{userName}のいいところは情熱です。{userName}の情熱に周りの人は感化されます。', '{userName}のいいところは厳しさです。{userName}の厳しさがものごとをいつも成功に導きます。', '{userName}のいいところは知識です。博識な{userName}を多くの人が頼りにしています。', '{userName}のいいところはユニークさです。{userName}だけのその特徴が皆を楽しくさせます。', '{userName}のいいところは用心深さです。{userName}の洞察に、多くの人が助けられます。', '{userName}のいいところは見た目です。内側から溢れ出る{userName}の良さに皆が気を惹かれます。', '{userName}のいいところは決断力です。{userName}がする決断にいつも助けられる人がいます。', '{userName}のいいところは思いやりです。{userName}に気をかけてもらった多くの人が感謝しています。', '{userName}のいいところは感受性です。{userName}が感じたことに皆が共感し、わかりあうことができます。', '{userName}のいいところは節度です。強引すぎない{userName}の考えに皆が感謝しています。', '{userName}のいいところは好奇心です。新しいことに向かっていく{userName}の心構えが多くの人に魅力的に映ります。', '{userName}のいいところは気配りです。{userName}の配慮が多くの人を救っています。', '{userName}のいいところはその全てです。ありのままの{userName}自身がいいところなのです。', '{userName}のいいところは自制心です。やばいと思ったときにしっかりと衝動を抑えられる{userName}が皆から評価されています。', ]; /** * 名前の文字列を渡すと診断結果を返す関数 * @param {string} userName ユーザーの名前 * @return {string} 診断結果 */ function assessment(userName) { //userName(文字列) を数値に変換 //全ての文字を足し算する var userNameNumber = 0; for (let i = 0; i < userName.length; i++) { userNameNumber += userName.charCodeAt(i); } //5桁の数値を回答結果の範囲(0~15)に返還 var answerNumber = userNameNumber % answers.length; //診断結果 var result = answers[answerNumber]; return result.replace(/\{userName\}/g, userName); }; console.assert( assessment('太郎') === '太郎のいいところは決断力です。太郎がする決断にいつも助けられる人がいます。', '診断結果の文言の特定の部分を名前に置き換える処理が正しくありません。' );むずかしかったよ
- 授業の途中までは理解し進めたつもりだけど、コードをまとめてスマートにしようってなったあたりから???が増えてきた。 課題としてはこの辺かなって思いました。
- あと未だに自分で考えて作ってみようってなると無理ですね。
次回やる予定のこと
- 次回はツイート機能の開発みたいです!
- 投稿日:2020-10-27T08:23:55+09:00
querySelectorの使い方
querySelector
JavaScriptには「getElementById()」とか「getElemetnsByClassName()」などHTML要素を取得できるメソッドはありますが、
「querySelector()」を使うとid属性値・class属性値などを意識せずにjQuery感覚でHTML要素をセレクタ指定することができます。
index.html<p>全ての色が混ざり合うと白くなるものは何か?</p> <button onclick="result1()">答え</button> <p id="ans1">ここに答えを表示</p>index.jslet list1 = document.querySelector('#ans1'); function result1() { list1.innerHTML = '<h4>加法混色</h4>'+'<img src="img/rgb.png">'; }
- 投稿日:2020-10-27T07:12:03+09:00
memo? useCallback? パフォーマンスが気になる JSXer には SolidJS がオススメ
パフォーマンスなんか気にしたくない
Give a man a bug and he'll work for a day.
Give a man a benchmark and he'll work for a lifetime.1
https://twitter.com/awesomekling/status/1318615899910307850パフォーマンスなんかに気をとられながら実装したくないんですよ。
React のmemo()
やuseCallback()
のような最適化のためだけの API を呼ぶ呼ばないで 1 ミリ秒も悩みたくないんです。そんな API は存在しないでほしい。でも気になっちゃうんです。というか、まったく最適化せずに React でアプリを構築していくと、カクつきを体感するぐらいには遅くなりますよね。気にせざるを得ません。
つい最近も React のパフォーマンスチューニング記事がバズってましたね。
- Reactのパフォーマンスチューニングの歴史をまとめてみた
https://blog.ojisan.io/react-re-render-history- React / Redux を実務で使うということは
https://zenn.dev/suzuesa/articles/35ace7a7cd127f9a1d08早すぎる最適化は害悪か
早すぎる最適化は諸悪の根源とまで言われています。とても強烈な言葉です。本当にそうでしょうか? React でもそうでしょうか?
memo()
をガチで導入しようと思ったら、 MobX のようなリアクティブなグローバルデータストアを各コンポーネントから直接参照するか、あるいは状態ツリーを不変にするか(そのために Immutable.js や Immer を導入しようか)、みたいな話になるかと思います2。 実装が進んでから最適化しようと思ったら、状態の持ち方から変えることになるかもしれません。これはさすがにつらい。パフォーマンス問題は得てして発見が遅れがちです。普段の開発用 PC ではサクサク動くのに、 2 年前に出たローエンドスマホでテストしてみたら全然動かない、みたいな形で姿を現します。手元では再現しません。ローエンドスマホでのデバッグで遅い原因を突き止めて最適化するのはなかなかの試練です。
そして 1 度でもパフォーマンスが課題になるとソースコードのあっちもこっちも気になって気になってムズムズしてきます。最適化はどれだけ時間があっても足りません。(ムズムズしても、極端に遅い箇所だけの最適化にとどめて他の新機能開発に時間を使うのがオトナというもの。最適化に時間を費やしすぎることこそが害悪なのです。...なかなかオトナにはなれないものですけどね...)
最初からなるべくパフォーマンスが問題にならないような仕組みを導入しておくのが無難なのです。
早すぎる最適化はリスク回避です。 少なくともこの文脈では。可能なら意識しなくても十分にパフォーマンスが出るようなライブラリを選びたいところですよね。
ということで、 React に似た API と JSX で、勝手に爆速になる UI ライブラリ Solid を紹介します。
Solid
- ryansolid/solid: A declarative, efficient, and flexible JavaScript library for building user interfaces.
https://github.com/ryansolid/solidSolid is a declarative JavaScript library for creating user interfaces. It does not use a Virtual DOM. Instead it opts to compile its templates down to real DOM nodes and wrap updates in fine grained reactions. This way when your state updates only the code that depends on it runs.
[DeepL 翻訳]
Solid は、ユーザー インターフェイスを作成するための宣言型 JavaScript ライブラリです。仮想 DOM は使用しません。その代わりに、テンプレートを実際の DOM ノードまでコンパイルして、更新をきめ細かいリアクションでラップします。これにより、状態が更新されたときに、それに依存するコードのみが実行されます。Solid は、
React のように JSX とフックライクな API で宣言的な関数コンポーネントを記述でき、
lit-html のように効率的にテンプレートから DOM を構築・更新し、
Svelte のようにコンパイル時に再レンダリングを最適化し、
SSR もサポートする、軽量で非常に高速な UI ライブラリです。公式コード例(TSX)
import { createState, onCleanup, Component } from "solid-js"; import { render } from "solid-js/dom"; const App: Component = () => { const [state, setState] = createState({ count: 0 }), timer = setInterval(() => setState("count", c => c + 1), 1000); onCleanup(() => clearInterval(timer)); return <div>{state.count}</div>; }; render(() => <App />, document.getElementById("app"));https://codesandbox.io/s/8no2n9k94l?file=/index.tsx
なんとなく読めるのではないでしょうか。
React と特に大きく異なるのは、関数コンポーネント内の処理が要素初期化時に 1 度だけ呼ばる点です。 React のようにレンダリングのたびに呼ばれるわけではありません。ここは挙動の違いを明確に意識する必要があると思います。
React の
memo()
やuseCallback()
のような最適化用の API はありません。それでいて React や Vue.js (3.0) はもちろん、 lit-html や Svelte よりも高速に動作します。ベンチマークを見ると vanillajs にかなり近いです。
- Official results for js web frameworks benchmark
https://krausest.github.io/js-framework-benchmark/index.htmlTSX で書けて型チェックできるし、 API もわりと扱いやすいし、この実行速度。
(私の観測範囲では) 控えめに言って最強です (2020 年 10 月現在)。
注意点
どんなライブラリにもクセはあります。私は React の
onChange
が"input"
イベントで発火するのが許せない人です。私は Mr. Complain です3。 Angular のこともいろいろ書きました。 まだぜんぜん使い込んでいませんが Solid に対しても不満はあります。state や props をデストラクチャリングしちゃダメ
createState()
で作成する状態やコンポーネントの引数 props はフィールドアクセスが変更監視のトリガーになっていて、リアクティブにしたい式の中でアクセスする必要があります。デストラクチャリングすると、プロパティの変更に対してリアクティブに動作しなくなってしまいます。
- Answering Common Questions about SolidJS - DEV # 4. Why does destructuring not work? I realized I can fix it by wrapping my whole component in a function.
https://dev.to/ryansolid/answering-common-questions-about-solidjs-23ea#4-why-does-destructuring-not-work-i-realized-i-can-fix-it-by-wrapping-my-whole-component-in-a-function変更監視系 API をコンポーネントの外で使うと警告
Solid では
createSignal()
API によってリアクティブでアトミックな状態、createState()
API によってリアクティブな状態ツリーを作ります。これらは React のuseState()
と異なり、グローバルスコープで普通に作ればコンポーネント間で状態を共有できます。(フックと違ってコンポーネント内で呼ぶ順番も関係ありません。)非常に便利です。
- Reactivity (solid/reactivity.md at master · ryansolid/solid)
https://github.com/ryansolid/solid/blob/master/documentation/reactivity.mdこれら状態を監視して変更に反応する、たとえば
createMemo()
(MobX でいうcomputed()
、 Vue.js でいうcomputed()
、 Recoil でいうselector()
)などの API もグローバルスコープで期待通りに動作します。が、警告が出ます。computations created outside a
createRoot
orrender
will never be disposedこの警告の意図は理解できます。が、現実のユースケースに照らして考えると、この警告は親切すぎるというか、大きなお世話だと感じます。
Solid の作者によれば、グローバルな状態には Context API がオススメとのことですが、めちゃくちゃ単純で直感的なcreateSignal()
+createMemo()
に比べると Context API は複雑で扱いづらいです。
グローバルな状態を作る方法として、変更監視 API の実際の呼び出しをルートコンポーネントの初期化まで遅延させる手は使えそうです。
https://codesandbox.io/s/lmrb9?file=/index.tsxためしに作ってみたもの
- Todo リスト
https://codesandbox.io/s/solidjs-todo-nsgru?file=/index.tsx- ライフゲーム
https://luncheon.github.io/conway-game-of-life--react-vs-preact-vs-solid/ライフゲームの例では React + Recoil、 Preact + preact-shared-state-hook、 Vue.js 3.0、 Solid でライフゲームロジックを共有して FPS を比較してみました。 Solid はバンドルサイズが最小になり、かつ、 FPS も他と比べると群を抜いて良い結果になりました(ベンチマークに適した例ではないかもしれませんが)。(参考に手元の PC で React 12 fps、 Preact 12 fps、 Vue.js 8 fps、 Solid 28 fps 程度です。)
おわりに
Solid は React に近い API と Vanilla JS に近い実行速度を持つ優れた UI ライブラリです。パフォーマンスのことばかりに気をとられる日々は終わりを迎えるかもしれません。
とはいえ、まだ歴史の浅いライブラリですのでエコシステムが未成熟です。コンポーネントフレームワーク(Material-UI とか Element UI みたいなの)は見当たらないし、ネット上で見つかる事例も少ないです。日本語の記事ぜんぜんないし。いま使うなら多少の苦労は覚悟しなきゃいけないかもしれません。
私はその苦労の価値があるんじゃないかと踏み、 Solid に期待を込めて、この記事を書いている次第です。
参考
- Why SolidJS: Do we need another JS UI Library? - DEV
https://dev.to/ryansolid/why-solidjs-do-we-need-another-js-ui-library-1mdc- Comparison with other Libraries (solid/comparison.md at master · ryansolid/solid)
https://github.com/ryansolid/solid/blob/master/documentation/comparison.md
- 投稿日:2020-10-27T04:58:34+09:00
Js/CSSのキャッシュをコントロール
js/cssをブラウザでキャッシュさせない方法
Cahche Bustingって言うらしい。。。
その方法は、以下の通り
対策前のコード↓
sample.html<!-- CSS --> <link href="style.css" rel="stylesheet" type="text/css"> <!-- JavaScript --> <script src="script.js" type="text/javascript" ></script> <!-- 画像 --> <img src="sample.jpg" alt="Sample">対策後コード↓
sample.html<!-- CSS --> <link href="style.css?ver=1.0.0" rel="stylesheet" type="text/css"> <!-- JavaScript --> <script src="script.js?ver=1.0.0" type="text/javascript" ></script> <!-- 画像 --> <img src="sample.jpg?ver=1.0.0" alt="Sample">「?」の後に任意の文字列を付与すると、ブラウザは対策前のURLとは違うものだと認識する。
(これをクエリ文字列という)クエリ文字列はほかにも
・更新日時 (例:src=”style.css?date=20201027053000″)
・バージョン (例:src=”style.css?ver=1.2.3″)
・ランダム数jsやcss側での加工はと言うと、必要ない。
これでjs/cssを更新しても、ブラウザでキャッシュされなくなる。
しかし、htmlがキャッシュされたら元も子もないので、htmlには以下のmetaを記述する。sample.html<head> <meta http-equiv="Pragma" content="no-cache"> <meta http-equiv="Cache-Control" content="no-cache"> <meta http-equiv="Expires" content="0">3つともキャッシュしちゃダメよ!ということらしい。
以上。
- 投稿日:2020-10-27T04:17:29+09:00
モダンな技術のReactを勉強しよう
最近現役エンジニアの方とお話しさせていただく機会があったのですが、やっぱり現場はReact一択!!とういう声が大きかったので、どんな技術なのか、どうやって導入するのか調べて実際に簡単なアプリを作成していこうと思うので、これからReactをアウトプットして行こうと思います。
Reactとは
まずは簡単にReactってどういうものなのかざっと調べた情報を記述します
- ReactはFacebookが作った
- JSのライプラリ
- 記述の仕方はJSX(ほぼHTML)
- {}内はJSの記述できる
- SPA(Single Page Application)を作ることに最適
- ちなみにSPAはページ推移せず、一つのWebページでコンテンツを切り替えられるやつのこと いちいちリロードが必要ないやつ
- TypeScriptと相性が良い
ほんとに簡単に調べた結果を羅列しただけですが、こんな感じ
モダンな技術を学ぶのはわくわくしますね! オラわくわくすっ(ry
まあよく意味わからないところとかあるんですけど、それはアプリ作りながらやっていけばわかる様になっていくのかなと思います
じゃけん、環境構築やっていきましょうか!(埼玉県出身者より)環境構築
create-react-app
という開発環境を簡単に構築できるツールを使っていくのですが、どうやらそれを使うためには
node
npm
が必要らしいですまたそれをインストールするためには
Homebrew
とnodebrew
をインストールしなければいけない様です、、ややこしい、、なんでまずはこのコマンド
ターミナル/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"その後、Macのパスワードと
Press RETURN to continue or any other key to abort
と出てくるのでreturn押してあげてください
これでHomebrewのインストール完了つぎはこのHomebrewを使ってnodebrewをインストールします
ターミナルbrew install nodebrewこれでnodebrewのインストール完了
そんで次にnodebrewを使ってnodeをインストールしますターミナルnodebrew install stableディレクトリがないとエラー出る場合はこれを先に
ターミナルmkdir -p ~/.nodebrew/srcインストールが完了したら
ターミナルnodebrew lsでバージョンを確認しましょう
多分今の安定バージョンというのはv14.4.0
になっていて、それをインストールした状態ですが
current: none
とターミナルに記載があると思います。
これをv14.4.0
にするためにターミナルnodebrew use v14.4.0これで再度バージョンを確認すると
current: v14.4.0
になっているはずそして次はこれ(最新macOSなら)
ターミナルecho 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.zprofileこれでやっとnodeがインストールできました!!
nodeがインストールされたらnpmもインストールされていますので、これでcreate-react-app
を使う環境が整ったとういう感じですね!そしてこれが最後のアプリケーションを作成するときにコマンド
ターミナルnpx create-react-app 任意のアプリケーション名これでアプリケーションが作成されました!!
Finderから確認できればアプリケーションは作成されています
長かった?ですねあっ、アプリケーションの作成の際はちゃんと自分が作りたいディレクトリに移動して作成してくださいね!
まとめ
とりあえず
create-react-app
の環境構築までをずらずら記述していったのですが、ネットワークの基礎的な部分が圧倒的に不足していますね。。。
なんでかわからないけどできたという状態から抜け出すためにはどのくらい勉強すればいいのかトホホ、、、参考
- 投稿日:2020-10-27T02:09:58+09:00
カレンダーから日付を選択するフォーム作成方法
- 投稿日:2020-10-27T00:52:18+09:00
GoToトラベルのクーポン種別を絞込む
はじめに
金持ちほど得をする素敵なキャンペーン、GoToトラベルですが、クーポン種別が紙と電子の二種類あることをご存知でしょうか?
紙クーポンは、その名の通り実体のある紙形のクーポン、電子クーポンはスマホから使用できるバーチャルなクーポンです。普通に考えれば、同じキャンペーンで発行されたクーポンなので同じように使えるはずですが、そこは流石日本というべきか、
電子クーポンは紙クーポンと比較して約半数の店鋪でしか使用できません。「まあ、最初に調べてから旅行すればいいじゃん」と思いますが、GoToトラベルのサイト上ではクーポン種別による絞り込みが出来ません。
これをUserScriptを使用して改善してみます。
UserScriptって?
UserScriptとは、Webサイト上でユーザー任意のJavascriptを割り込む仕組み、またはそのスクリプトのことです。
たいていのブラウザで拡張機能として動作します。Tampermonkeyあたりが有名でしょうか。
今回はこれを用いることによって、GoToトラベルの公式Webサイト上で絞り込み機能を有効化しています。スクリプト
// ==UserScript== // @name GoToRefine // @namespace https://satoh.dev/ // @version 0.1 // @description Refine your search results by coupon type for GoTo Travel. This is how it should be. // @author Soh Satoh // @match https://map.goto.jata-net.or.jp/?st=result&* // @require https://jpillora.com/xhook/dist/xhook.min.js // @grant none // ==/UserScript== (function () { // Add a button to choose between electronic or paper coupons // ref: https://www.lisz-works.com/entry/tampermonkey-add-selector var selectable_elements = ['デフォルト', '電子クーポン', '紙クーポン']; var selectbox = $('<select>') .attr({ name: 'sel', id: 'selector', style: 'color: white; font-size:' + $('.reset-btn').css('font-size'), }) .on('change', async function () { localStorage.setItem('mode', this.selectedIndex); location.reload(true); }) .appendTo($('.reset-btn')); $.each(selectable_elements, function (i, v) { selectbox.append($('<option>').val(v).text(v)); }); // Apply the saved setting var selector = $('#selector')[0]; selector.selectedIndex = localStorage.getItem('mode'); if(selector.selectedIndex == 0) return; // Hook XHR xhook.after(function (request, response) { if(request.url.indexOf('api/point') == -1) return; var modified = JSON.parse(response.data); var items = modified.items; var i = items.length; while (i--) { if (items[i][selector.value] != '1') { items.splice(i, 1); } } response.data = JSON.stringify(modified); }); })();最初にJQueryでページにセレクトボックスを追加します。
デフォルト、電子クーポン、紙クーポンの間で選択可能にし、localStorage.setItem('mode', this.selectedIndex);でローカルストレージに設定を保存するようにしています。
本来はGM.setValueを使用するべきなのですが、これを使用するとなぜかXHRにHookできなくなるため断念しました。理由がわかる方はコメントをいただけると幸いです。次にXHookを使用してresponseを書き換えています。
このスクリプトを使用することで、かんたんにXHR(Ajax)を傍受、改変することが可能です。
本来は外部スクリプトを利用する予定ではなかったのですが、responseの書き換えがどうやってもうまく行かず断念しました。(responseTextの改変は簡単なのですが)最後に
最初は容易に検索できるよう、Webサイトを作成しようと思ったのですが怒られたくないのでやめました。
「初めから絞込めるようにしろよ」とは思うのですが、まあそれやられるとGoToキャンペーン本来の意味を失う部分もあるのかもしれません。
電子クーポンのほうが不便だなんて意味がわからないですけどね。それならどちらかに統一してくれたほうが便利なんですが。
- 投稿日:2020-10-27T00:13:47+09:00
lazyloadの使い方
- 投稿日:2020-10-27T00:07:01+09:00
Herokuへデプロイする手順
Herokuへデプロイする手順
ターミナルbrew tap heroku/brew && brew install herokuターミナルheroku --versionターミナル# Herokuへログインするためのコマンド % heroku login --interactive => Enter your Heroku credentials. # メールアドレスを入力し、エンターキーを押す => Email: # パスワードを入力して、エンターキーを押す => Password:Gemfile# ファイルの一番下の行に追記する group :production do gem 'rails_12factor' endターミナル# Gemをインストール % bundle installターミナル% git add . % git commit -m "gem rails_12factorの追加"ターミナル% heroku create アプリケーション名ターミナル% git config --list | grep herokuターミナル% heroku addons:add cleardb Creating cleardb on ⬢ アプリケーション名... free Created cleardb-vertical-00000 as CLEARDB_DATABASE_URL Use heroku addons:docs cleardb to view documentationターミナル% heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL`ターミナル% heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5} # 以下、コマンドの実行結果 Setting DATABASE_URL and restarting ⬢ アプリケーション名... done, v◯◯ DATABASE_URL: mysql2://000000000000:0aa0000@us-cdbr-east-02.cleardb.com/heroku_aaa00000000?reconnect=trueターミナル% EDITOR="vi" bin/rails credentials:editターミナル% heroku config:set RAILS_MASTER_KEY=`cat config/master.key`ターミナル% heroku configターミナル% git push heroku masterターミナル% heroku run rails db:migrate公開を確認
ターミナル% heroku apps:info ===ajax-app-123456 Addons: cleardb:ignite Auto Cert Mgmt: false Dynos: web: 1 Git URL: https://git.heroku.com/アプリケーション名.git Owner: sample@sample.com Region: us Repo Size: 165 KB Slug Size: 56 MB Stack: heroku-18 Web URL: https:/アプリケーション名.herokuapp.com/現場からは以上です!