20200724のJavaScriptに関する記事は30件です。

【JavaScript入門】モーダルウィンドウ

はじめに

Webサイトを作成する際、
・ボタン、画像を押したときにポップアップする小ウィンドウを設置したい
と思うことあると思います。
今回は、そんな機能についてご紹介します。

手順① 表示・非表示機能をつくる

まずは、モーダルウィンドウの根幹となる表示・非表示の機能を作ります。

html_javascript
<form>
  <input type="button" id="openBtn" value="モーダル">
</form>

<!-- モーダルウィンドウで表示する部分 -->
<div id="modal" class="modal">
  <div class="modal_content">
    <h1>Hello</h1>
    <input type="button" id="closeBtn" value="閉じる">
  </div>
</div>

<!-- モーダルウィンドウ -->
<script>
  var openBtn = document.getElementById('openBtn');
  var closeBtn = document.getElementById('closeBtn');
  var modal = document.getElementById('modal');

  // 表示(openBtnというidのボタンがクリックされたら、display:blockになる)
  openBtn.addEventListener('click', function(){
      modal.style.display = 'block';
  })

  // 非表示(closeBtnというidのボタンがクリックされたら、display:noneになる)
  closeBtn.addEventListener('click', function(){
      modal.style.display = 'none';
  })
</script>
css
/* ボタンがクリックされたときに表示したいので、displayはnoneに設定 */
.modal{
    display: none;
}

これでは、まだウィンドウとして形になっていないので、CSSにいくつか追記する必要があります。

手順② CSSでデザインする

次に、見栄えの良いモーダルウィンドウになるようCSSを書いていきます。

css
.modal{
  display: none;
  background: rgba(0,0,0,0.5);
  /* モーダルウィンドウの位置固定 */
  position: fixed;
  /* 背景色を画面いっぱいにする */
  top: 0;
  left: 0;
  height: 100vh;
  width: 100%;  
}

.modal_content{
  background: #fff;
  width: 500px;
  /* モーダルウィンドウを画面真ん中に表示 */
  margin: 25% auto;
}

これで一通りは完成ですが、最後にひと工夫入れます。

手順③ 背景色をクリックで非表示

最後に、背景をクリックしたときも閉じれるようにしていきます。

javascript
// 背景クリックで非表示(modalというidの部分がクリックされたら、display:noneになる)
addEventListener('click', function(close_bg){
  if(close_bg.target === modal){
    modal.style.display = 'none';
  }
})

以上でモーダルウィンドウの実装は完了です。

あとは、ご自身でCSSを触ることでバリエーションができると思います。

参考

Event.target
javascriptでモーダルウィンドウ

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

hyperscript(h())の使い方(ブラウザで)

hyperscript(h()でDOM生成)をブラウザから利用するサンプルです。

利用イメージ

    root.appendChild(
        h("ul", {}, 
            [h("li", "list1"),h("li", "list2"),h("li", "list3")]
        )
    );

準備

npm init
npm install hyperscript
npm install -D http-server

http-serverは動作確認用のhttpサーバです。

サンプルの内容

  1. ブラウザで利用可能とするため、jsファイル内でhyperscriptを読み込み、エクスポートします。
  2. Browserifyでバンドルを行います。ブラウザからCommonJSモジュール形式のhyperscriptを呼び出すために必要です。
  3. ブラウザからバンドル済みのjsファイルを読み込み、DOMの生成を行います。

実装の概要

1. jsファイルでhyperscriptを読み込みエクスポートする。

ブラウザが利用するjsファイル(main.js)

var h = require('hyperscript');

module.exports = {
    createSampleDom: ()=>{
        return h("div", {style:{color:"blue"}}, h("span", {}, "春はあけぼの"));
    },
    h: h,   // h()自体をエクスポート。ブラウザ側で直接利用できる。
}

hyperscriptとバンドルするためcommonjs形式(module.exports)でエクスポートを行います。

2. Browserifyでバンドルを行う

  1. バンドルしたモジュール公開するため「-r」(--requireオプション)をつける。
  2. モジュール名の指定(:app)を行う。(デフォルトのモジュール名が変なため)
    browserify -r ./main.js:app > app.js

:app がモジュール名指定 (require('app')でロードできる)

3. ブラウザ側で公開したメソッドを読み込み、実行してDOMに追加する。

    <script src=./app.js></script> 
    <script>
        window.onload = function() {
            var appModule = require('app');        
            const root = document.getElementById("rootNode")

            // modele.export されたfunctionを呼び出し、DOMを生成する。
            const node = appModule.creatoSampleDom();
            root.appendChild(node);

            // moduleで公開したh()を呼び出す。
            const h = appModule.h; //exportしたfunctionを変数に設定
            root.appendChild(
                h("ul", {}, 
                    [h("li", "list1"),h("li", "list2"),h("li", "list3")]
                )
            );
       }

ビルドと実行について

package.jsonの"scripts"に、下記を追加。

"build": "browserify -r ./main.js:app > app.js",`
"serve": "http-server",`
  • ビルドと実行
    • npm run build
    • npm run serve
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript find()関数とfindIndex()関数の使い方 (走り書き)

findとfindIndexの使い方

参考記事
https://ginpen.com/2018/12/04/array-find/

find(配列内の指定した数値や文字を取得)

const a = 'blue'
const ary = ['red', 'green','blues', 'blue', 'yellow', 'white','blue-bird']

// aと同じ要素があれば、抜き出します。
const result = ary.find(item => item === a)
console.log(result)
// result = 'blue'

findIndex(配列のインデックス(何番目か)を取得)

const ar = ['red', 'green','blues', 'blue', 'yellow', 'white','blue-bird']
const index = ar.findIndex(item =>item === 'red')//配列の0番から始まって何番目にあるかを抽出。見つからないと−1を返す
console.log(index)//結果:0(red以外を検索していたら、green:1, blues:2, blue:3 ,,,)

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

documentオブジェクトによるHTMLを取得するメソッド

今回はjsのdocumentオブジェクトのDOMを取得するメソッドを学んだので3種類まとめたいと思います。

  1. document.getElementByIdメソッド;
  2. document.getElementsByClassNameメソッド;
  3. document.querySelectorメソッド;

document.getElementByIdメソッドとは

引数にidを指定することでdomから特定のHTML要素を取得するメソッド一つです。

document.getElementById("id名");

上記のように書くことでマッチするidを持つHTMLの要素を取得します。

document.getElementsByClassNameメソッドとは

引数にclass名を指定することで同じclassの要素を取得できるメソッドです。

document.getElementsByClassName("クラス名");

ここで一つ気をつけなければいけないのがgetElementByIdと違いclassは複数存在する可能性があるのでgetelementsと複数系にしなければなりません。

document.querySelectorAllメソッドとは

引数にselectorを指定することで指定した要素を全て取得するメソッドです。

document.querySelector("セレクタ名");

一方、querySelectorというメソッドを用いれば、HTML上から引数で指定したセレクタに合致する要素のうち一番最初に見つかった要素1つを取得することもできます。

セレクターでクラス取得するのもgetElementsByClassNameでクラス取得するのも同じじゃねえか!!と思ったんですがquerySelectorALLはNodeListというオブジェクトが返り値となってgetElementsByClassNameはHTMLCollectionが返り値となっている点が違うようです。

HTMLCollectionオブジェクトだと配列になっておらずforeachメソッドを使えないとか。。。

Nodelistオブジェクトは配列になっているためfor eachメソッドも使えるようですね。

オブジェクトを配列の形にできるメソッドもありますがまたまとめます。

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

【JavaScript】event.preventDefault()が何をするのか

JSPrimerTodoアイテムの追加を実装する
より引用。

何をするのか?

event.preventDefaultメソッドは、submitイベントの発生元であるフォームが持つデフォルトの動作をキャンセルするメソッドです。

eventDefault の動作を prevent する(妨げる) なので読んだまま。

フォームが持つデフォルトの動作とは?

フォームが持つデフォルトの動作とは、フォームの内容を指定したURLへ送信するという動作です。

form要素に送信先が指定されていない場合、現在のURLに対してフォームの内容を送信するらしい。

event.preventDefaultを呼び出すとどうなるか?

event.preventDefaultメソッドを呼び出すことで、このデフォルトの動作をキャンセルしています。

以下サンプルコード

formElement.addEventListener("submit", (event) => {
    // submitイベントの本来の動作を止める
    event.preventDefault();
    console.log(`入力欄の値: ${inputElement.value}`);
});

現在のURLに対してフォームの送信が行われると、結果的にページがリロードされてしまいます。 そのため、event.preventDefault()を呼び出し、デフォルトの動作をキャンセルしていました。 event.preventDefault()をコメントアウトすると、ページがリロードされてしまうことが確認できます。

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

JavaScript の Promise, await, async 時のエラー捕捉について

new Promise() を返す関数内でのエラーは onError では拾えない。

window.addEventListener('error', (msg, file, no) => {
  alert(['onError', msg.message, file, no].join("\n--------\n"))
})

window.addEventListener('unhandledrejection', (evt) => {
  alert(['onUnhandledrejection', evt.reason.stack].join("\n--------\n"))
})

// onErrorでは拾えない。onUnhandledrejection では拾える。
new Promise((resolve,reject)=>{ throw new Error('xxx') })

エラーを捕捉するにはどうするか。

function p1(){ return new Promise((resolve,reject)=>{
  throw new Error('xxx')
})}
//-----
;(async()=>{
  try{ p1() }catch(err){ alert(err) } // × awaitで無い場合は拾えない
  try{ await p1() }catch(err){ alert(err) } // ○ await なら拾える
  p1().catch((err)=>{ alert(err) }) // ○ .catch()でも拾える

  try{
    await p1().catch((err)=>{ alert(err) }) // ○ こっちだけで拾える
  }catch(err){
    alert(err)
  }

  try{
    await p1().catch((err)=>{ alert(err); throw err }) // ○ 拾える
  }catch(err){
    alert(err) // ○ throw してるのでこちらでも拾える
  }
})();

new Promise() を返す関数内のエラーは自分で捕捉するしかない。
親には伝播しない。

function p2(){ return new Promise((resolve,reject)=>{
  p1()
})}
p2().catch((err)=>{ alert(err) }) // p1 のエラーは p2 に伝わらないので捕捉できない

伝播させるには手動で上げるしかない。

function p2(){ return new Promise((resolve,reject)=>{
  p1().catch(reject) // こちらで捕捉して上に上げる
})}
p2().catch((err)=>{ alert(err) }) // 捕捉できる

new Promise() では await(try ~ catch) と .catch() は直接の 自分自身 内のエラーのみ反応する。

new Promise() 内で new Promise() を使用するとそれはもう別世界でエラーは自動では伝播しないので注意。

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

canvasを指定ファイルサイズに収まるように解像度を落として保存する

前置き

こちらの記事の続き。

やりたかったこと

canvas上で生成した画像を、wikiwikiのアップロード可能ファイルサイズ512KBに収まるようにサイズを落として保存したかった。
サイズを落とし方としてjpgの品質を下げる方法と解像度を下げる方法が思いついたが、ブロックノイズだらけになるくらいならQVGAとかになる方がましだと思ってるので後者を採用した。
512KBにぴったり合わせるのではなく、ざっくり下回っていればヨシとした。

開発環境

Mery(x86) 2.5.6
Firefox 78.0.2(64ビット)

結果

コード

SaveReduced
function SaveReduced(canvas_src){
    console.debug('Saving reduced image.');
    //出力用canvas生成
    const canvas_out = document.createElement('canvas');
    const ctx_out = canvas_out.getContext('2d');

    //入力canvasのファイルサイズを計測
    const filesize_src = base64ToFile(canvas_src.toDataURL("image/jpeg", 0.90))["size"]
    console.debug('filesize_src: ' + filesize_src + 'B');

    //制限サイズを設定
    const filesize_cap = 5120000;
    console.debug('filesize_cap: ' + filesize_cap + 'B');

    //縮小率を計算、制限サイズより小さければ縮小なし
    let rate_reducing = 1
    if (filesize_src > filesize_cap){rate_reducing = filesize_cap / filesize_src}

    //出力用canvasの解像度を縮小率に合わせて縮小し入力canvasを書き出し
    canvas_out.width = Math.floor(canvas_src.width * Math.sqrt(rate_reducing));
    canvas_out.height = Math.floor(canvas_src.height * Math.sqrt(rate_reducing));
    console.debug('canvas_out_size(w*h): ' + canvas_out.width + ' * ' + canvas_out.height);
    ctx_out.drawImage(canvas_src, 0, 0, canvas_src.width, canvas_src.height, 0, 0, canvas_out.width, canvas_out.height);

    //制限サイズを切るまで10%ずつ解像度を落とす
    while (base64ToFile(canvas_out.toDataURL("image/jpeg", 0.90))["size"] > filesize_cap){
        canvas_out.width = Math.floor(canvas_out.width * 0.9);
        canvas_out.height = Math.floor(canvas_out.height * 0.9);
        console.debug('canvas_out_size(w*h): ' + canvas_out.width + ' * ' + canvas_out.height);
        ctx_out.drawImage(canvas_src, 0, 0, canvas_src.width, canvas_src.height, 0, 0, canvas_out.width, canvas_out.height);
    }

    //アンカータグ経由でダウンロード
    GeneratedDownloadAnker(canvas_out.toDataURL('image/jpeg', 0.90),'output.jpg');
}

function GeneratedDownloadAnker(base64, name){
    //アンカータグを生成しhrefへBase64文字列をセット
    const a = document.createElement('a');
    a.href = base64;

    //ダウンロード時のファイル名を指定
    a.download = name;

    //クリックイベントを発生させる
    a.click();
}

function base64ToFile(data){
    try{
        let separetedDate = data.split(',');
        let mimeTypeData = separetedDate[0].match(/:(.*?);/);
        let mimeType = Array.isArray(mimeTypeData) ? mimeTypeData[0] : '';
        let decodedData = atob(separetedDate[1]);
        let dataLength = decodedData.length;
        let arrayBuffer = new ArrayBuffer(dataLength);
        let u8arr = new Uint8Array(arrayBuffer);
        for( let i = 0; i < dataLength; i +=1){
            u8arr[i] = decodedData.charCodeAt(i);
        }
        return new Blob([u8arr] , {type:mimeType});
    }catch (errors){
        console.log(errors);
        return new Blob([])
    }
}

画面

512KBを指定した場合→433KBまで削減
2020-07-24_205809.png
320KBを指定した場合→298KBまで削減
2020-07-24_205825.png
いい感じに実装できた。

苦労したところ

canvasのファイルサイズが分からん

ファイルサイズを落とそうにも元々のサイズが分からなかった。
元々のサイズの取得はcanvasをバイナリ化した上でblobオブジェクトに変換し、sizeプロパティを読み込むらしい(参考)。
バイナリ化はcanvas.toDataURL("image/jpeg", 0.90)で実装。
jpgの品質が90なのは見た目とファイルサイズのバランスが良いと俺の中で評判だから。
blobオブジェクトへの変換はbase64ToFile()関数で実装。こちらの丸コピ。

ファイルサイズが上手く下回らない

最初はファイルサイズと解像度は正比例していることを前提において、元のサイズと制限サイズの比率に応じて解像度を落とせばいい(比率の平方根を縦と横にそれぞれかける)と思っていた。
↓がそれを実装している部分。

//出力用canvasの解像度を縮小率に合わせて縮小し入力canvasを書き出し
canvas_out.width = Math.floor(canvas_src.width * Math.sqrt(rate_reducing));
canvas_out.height = Math.floor(canvas_src.height * Math.sqrt(rate_reducing));
console.debug('canvas_out_size(w*h): ' + canvas_out.width + ' * ' + canvas_out.height);
ctx_out.drawImage(canvas_src, 0, 0, canvas_src.width, canvas_src.height, 0, 0, canvas_out.width, canvas_out.height);

しかしこれだけだとほとんどの場合制限サイズを下回らなかった。
仕方ないので↑の縮小を実施した後に、確実に制限サイズを下回るまで少しずつ縮小することにした。

//制限サイズを切るまで10%ずつ解像度を落とす
while (base64ToFile(canvas_out.toDataURL("image/jpeg", 0.90))["size"] > filesize_cap){
    canvas_out.width = Math.floor(canvas_out.width * 0.9);
    canvas_out.height = Math.floor(canvas_out.height * 0.9);
    console.debug('canvas_out_size(w*h): ' + canvas_out.width + ' * ' + canvas_out.height);
    ctx_out.drawImage(canvas_src, 0, 0, canvas_src.width, canvas_src.height, 0, 0, canvas_out.width, canvas_out.height);
}

単に縮小するだけなら比率に基づく縮小なしにこのwhile文による縮小だけで要件は満たすが、端からこのwhile文にcanvasを突っ込むと周回数が多くなり負荷になりそうだったので結局両方使っている。
比率に基づく縮小でざっくり落としてwhile文で細かく調整しているイメージ。

今後実装したい機能

行・列数の操作
画像のドラッグ&ドロップによる任意の並び替え

参考

https://qiita.com/geek_duck/items/2db28daa9e27df9b861d
https://maku77.github.io/js/canvas/auto-resize.html
http://javascriptist.net/ref/Math.min.html
https://murashun.jp/blog/20191110-53.html
https://w3g.jp/blog/intermittent_event_load_reduce
http://matz.hatenablog.jp/entry/2017/05/01/235824
https://blog.ver001.com/canvas_save_file/
https://tech.chick307.com/2014/04/26/javascript-split-ext/
https://itsakura.com/js-number
https://qiita.com/masash49/items/b01d0205b437a8d1ec72

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

React + IndexedDBでCRUD作成、Form登録など #React #React.js #node

概要

React ,node.js版で
IndexedDBのCRUD作成となります
dexieライブラリで、IndexedDB操作して。
ブラウザ内に保存する形で。使用を想定しています

環境

react
react-dom
react-router-dom
dexie

画面

・リスト

ss-crud-0724a.png

・編集
ss-crud-edit-0724b.png

参考のコード

https://github.com/kuc-arc-f/react_cms1_1crud


実装など、React Component

・Create
https://github.com/kuc-arc-f/react_cms1_1crud/blob/master/src/component/Task/Create.js

・index
https://github.com/kuc-arc-f/react_cms1_1crud/blob/master/src/component/Task/Index.js

・edit/delete
https://github.com/kuc-arc-f/react_cms1_1crud/blob/master/src/component/Task/Edit.js


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

ノンフレームワークなJavaScriptでもDOMとうまく付き合う方法

ReactやVueなどのフロントエンドフレームワークが全盛期を迎えているJavaScriptですが、様々な制約から導入を足踏みしているプロジェクトは多々あると思います。

そして、そのようなプロジェクトではおそらくjQueryが現役で使われており、フロントエンドのコードはスパゲッティと化し、ネストされたコードは可読性を落とし、どの関数がどこで使われているのかわからない、そんな状態に陥っているのではないでしょうか。

この記事では、そんなプロジェクトを対象に、ノンフレームワークでも出来る限り可読性を向上させるための工夫をまとめてみました。

JavaScriptからHTMLをできるだけ触らない

JavaScriptとDOMは密結合になりがち

JavaScriptとDOM・HTMLの密結合がスパゲッティ化を招きます。逆に、JavaScriptとDOMを疎結合にしてしまえばスパゲッティ化しにくいといえます。

JavaScriptはHTMLによって運ばれブラウザで実行される言語なので、JavaScriptとHTMLを完全に分離することは不可能です。ただし、アプローチの仕方次第で結合度合いを下げることは可能です。

よくない例

下は、あるハンバーガショップのメニュー選択ページのJavaScriptです。
要望として、サラダが選択された時にはドレッシングの選択項目を出して欲しいと言われ、その実装を行いました。

document.getElementById('selectMenu').addEventListener('click', (event) => {
    const isSaladSelected = event.currentTarget.value === 'salad';
    if (isSaladSelected) {
        // ドレッシングメニューを表示する
        const dressingSelect = document.createElement('select');
        dressingSelect.id = 'dressingSelect';
        dressingSelect.innerHTML = `
            <option value="goma">ゴマドレッシング</option>
            <option value="wafu">和風ドレッシング</option>
            <option value="seasar">シーザードレッシング</option>
        `;
        document.getElementById('selectMenu').append(dressingSelect);
    } else {
        // ドレッシングメニューを消す
        document.getElementById('dressingSelect').remove();
    }
})

この時点ではまだ簡潔なように見えます。しかし、将来的にドレッシングがシーザーだったときにはクルトンの有無を、メニューがナゲットだったときにはソースの種類を、と要望が増えていったときはどうでしょう。破綻するのは目に見えています。

解決策: JavaScriptは表示の切り替えに専念する

この問題は、あらかじめHTMLにフォームを定義しておき、JavaScriptでは切り替えだけを行うことで解決できます。下は、あらかじめ項目を定義したHTMLです。 dressingSelect が非表示になっていることに注目してください。

<form>
  <select id="selectMenu">
    <option value="hamburger">ハンバーガー</option>
    <option value="potato">ポテト</option>
    <option value="salad">サラダ</option>
  </select>
  <select id="dressingSelect" style="visibility: hidden">
    <!-- visibility: hidden で最初は非表示 -->
    <option value="goma">ゴマドレッシング</option>
    <option value="wafu">和風ドレッシング</option>
    <option value="seasar">シーザードレッシング</option>
  </select>
</form>

JavaScriptはサラダだったときの切り替えのみに専念します。

document.getElementById('selectMenu').addEventListener('click', (event) => {
    const dressingSelect = document.getElementById('dressingSelect');
    const isSaladSelected = event.currentTarget.value === 'salad';
    dressubgSelect.style.visible = isSaladSelected ? 'visible' : 'hidden';
})

このように内容がすでに決まっているものはあらかじめHTMLに定義しておき、JavaScriptでは display: none 、もしくは visibility: hidden のつけ外しで表示・非表示を行なっていくと、JavaScript中でHTMLのあれこれを考える必要がなくなり、コードの見通しがよくなります。

動的に生成するときはページの表示時に

この方法は要素を動的に生成したい場合にも使えます。例えばドレッシングの在庫状況によりリストの内容を変更したい時は、切り替え時にリストを生成するのではなく、ページの読み込み時にあらかじめHTMLを生成しておけば良いのです。

<select id="dressingSelect" style="display: none">
  <!-- createDressingSelect -->
</select>
const createDressingSelect = (dressingList) => {
    // dressingListは配列で、ドレッシングの情報が入っているものとする。
    const dressingSelect = document.getElementById('dressingSelect');
    const dressingOptions = dressingList.map((dressing) => (
        `<option value="${dressing.value}">${dressing.name}</option>`
    ));
    dressingSelect.innerHTML = dressingOptions.join('\n');
}

getDressingList().then(createDressingSelect)

このアプローチを使用することにより、JavaScriptコードの中からDOMの操作をしている行を減らし、可読性を向上させることができます。

無名関数 & コールバックを多用するのは避ける

古いjQueryにより蘇る地獄

コールバック地獄という言葉があるように、JavaScriptでは以前からコールバックのネストが問題視されてきました。

jQueryのあるプラグインでは、オブジェクトにコールバックを設定させていました。

$('#dialog').dialog({
    title:"実行します。よろしいですか?",
    buttons: {
        "OK": () => {
            connection('/method', data)
                .then((response) => {
                    // ... 完了後の処理 ...
                });
        }
    }
});

見てわかるように、ネストがどんどん深くなっています。残念ながら、古いjQueryのプラグインにはこういったネストを増やしてしまうような設計が多くみられます。

解決策1: 関数を分割する

小手先の解決策ですが、無名関数を直接記述するのではなく、関数を分割することで対処できます。

const dialogOKButtonHandler = () => {
    connection('/method', data)
        .then((response) => {
            // ... 完了後の処理 ...
        });
}

$('#dialog').dialog({
    title:"実行します。よろしいですか?",
    buttons: {
        "OK": dialogOKButtonHandler
    }
});

解決策2: Promise化する

一番良い解決策は、このようなレガシーなjQueryプラグインをPromise対応のものに置き換える、もしくはラッパーを書くことです。

$('#dialog').dialog({
    title:"実行します。よろしいですか?",
    buttons: {
        "OK": dialogOKButtonHandler
    }
}).then(result => {
    return connection('/method', data)
}).then(response => {
    // ... 完了後の処理 ...
});

Promiseに対応させることで、関数を横にではなく縦に伸ばすことが可能になります。ネストも減らすことができ、可読性が向上します。

エレメントではなくデータを扱う

臭いものにはフタをする

臭いものにはフタをする という言葉があります。都合の悪いものを一時的に隠すという意味ですが、これはJavaScriptにも言えることです。

JavaScriptにとって、他のデータ形式であるHTMLを操作することは 臭いこと です。ですが、関数により処理を分割し、 フタをする ことができます。

DOMの操作とロジックは分割しましょう。ロジックの中では、エレメントではなくデータを扱うようにしましょう。
そうすることで、コードの見通しがよくなりロジックの再利用性が向上します。もしロジックの中にDOMの操作が紛れ込んでいたら、そのロジックは他所で使用することができなくなります。

よくない例

たとえばログインフォームを実装するときを考えましょう。下は良くない例です。
ログインボタンが押されたらユーザIDとパスワードをエレメントから取得してログインを試行します。ログインができたら、APIからデータを受け取り特定のDivに描写します。
要望として、3回アクセスに失敗したらキャプチャを出して欲しいと言われています。

// ログイン試行する
const tryLogin = async () => {
    // エレメントを取得
    const userIdInput = document.getElementById('userId');
    const passwordInput = document.getElementById('password');

    // エレメントからログイン試行
    const loginResultJSON = await connection('/login', {
        body: { userId: userIdInput.value, password: passwordInput.value }
    });
    return JSON.parse(loginResultJSON);
}

// データを取得しViewに追加する
const getDataListThenAppend = async () => {
    // データを取得
    const dataListJSON = await connection('/getData');
    const dataList = JSON.parse(dataListJSON);

    // データをDivに変換する
    const dataDivs = dataList.map((data) => {
        const div = document.createElement('div');
        div.innerHTML = `<p>${data}</p>`;
        return div;
    })

    // 変換したDivをHTMLに連結する
    const dataView = document.getElementById('dataView');
    dataView.append(...dataDivs);
}

let loginFailureCount = 0;
document.getElementById('loginButton').addEventListener('click', async () => {
    const loginResult = await tryLogin();

    // ログインできなかったときの処理
    if (!loginResult.ok) {
        loginFailureCount++;
        if (loginFailureCount > 3) {
            // 3回目でキャプチャを表示
            const caputureDiv = document.getElementById();
            caputureDiv.style.display = 'block';
        }
        return false;
    }

    await getDataListThenAppend();
});

一見、しっかりと関数が分割されているように見えます。しかしながら、それぞれの関数の中でDOMから情報を取得しており、これでは各関数とHTMLが密結合になってしまいます。密結合になると、その関数を他所で利用するためには、該当の関数のみならず結合されているHTMLをも移動させなければいけません。

解決策: ロジックの関数にDOMを触らせない

これを下のように分割し直しましょう。

// ログイン試行
const tryLogin = (userId, password) => {
    const loginResultJSON = await connection('/login', {
        body: { userId, password }
    });
    return JSON.parse(loginResultJSON);
}

// データを取得
const getDataList = () => {
    const dataListJSON = await connection('/getData');
    return JSON.parse(dataListJSON);
}

// データをHTMLに結合する
const appendDataListToHTML = (dataList) => {
    // データをDivに変換する
    const dataDivs = dataList.map((data) => {
        const div = document.createElement('div');
        div.innerHTML = `<p>${data}</p>`;
        return div;
    })

    // 変換したDivをHTMLに連結する
    const dataView = document.getElementById('dataView');
    dataView.append(...dataDivs);
}

let loginFailureCount = 0;
document.getElementById('loginButton').addEventListener('click', async () => {
    // エレメントを取得
    const userIdInput = document.getElementById('userId');
    const passwordInput = document.getElementById('password');

    const loginResult = await tryLogin(userIdInput.value, passwordInput.value);

    // ログインできなかったときの処理
    if (!loginResult.ok) {
        loginFailureCount++;
        if (loginFailureCount > 3) {
            // 3回目でキャプチャを表示
            const caputureDiv = document.getElementById();
            caputureDiv.style.display = 'block';
        }
        return false;
    }

    const dataList = await getDataList();
    appendDataListToHTML(dataList);
});

DOMの操作とロジックを意識して分割しました。これにより、関数に与える値のみを揃えれば関数を利用することができ、コードの再利用性を向上させることができます。

ESModules <script type="module"> を使う

生のJavaScriptでよくあがる問題として、下のような事柄が挙げられます。

  • ファイル分割・ライブラリ導入ごとに <script type="text/javascript"> が増える問題
  • とりあえず jQuery($ => {}) で囲んでしまう問題

2017年ごろからブラウザに順次導入されたESModulesを利用することで、これらの問題を解決することができます。

ES Modulesとは

ES Modulesは、JavaScriptファイルの分割と読み込みをサポートする仕組みの一つです。 export 文と import 文を組み合わせることで、JavaScriptを適時分割することが可能になり、コードの可読性とモジュール性を大幅に向上させることができます。

import / export

下の例を見てください。一番目は単体のJavaScriptファイル、二番目はHTMLです。

export 文を利用することで、関数を外側から読み込める形にしています。

// ./script.js
export const showDateText = (year, month, day) => {
    return `${year}${month}${day}日`;
}

こちらでは、 import 文を利用し他のファイルから関数を読み込んでいます。

<!-- index.html -->
<script type="module">
    import { showDateText } from './script.js';

    const pElm = document.getElementById('hoge');
    pElm.innerText = '私の誕生日は' + showDateText(1998, 4, 14) + 'です。';
    // p要素の内容は ”私の誕生日は1998年4月14日です。” になる
</script>

<p id="hoge">
</p>

コメントにある通り、 p 要素の内容は ”私の誕生日は1998年4月14日です。” になります。

上は簡単な例ですが、ES Modulesはページを跨いで使用される関数が増えるたびに利便性が向上します。
JavaScriptを分割し、それを読み込むためには <script src=""> が必要でしたが、ES Modulesを利用すると必要な関数を必要なときに外部のファイルから読み込むことが可能になり、グローバルスコープを汚さずにHTMLのheadも最低限のタグ数で済ますことができます。

onLoad いらず

また、moduleとして読み込まれたJavaScriptは、head内に定義されていたとしてもDOMの読み込みが完了した後に実行されます。(JavaScriptのイベント domcontentloaded 相当)

今までDOMを操作するためにスクリプトをHTMLの下部に書いたり、イベントリスナやjQueryに関数を渡してその中でスクリプトを書いていた人は、moduleに置き換えることで簡潔にかくことができるようになります。

<script type="text/javascript">
    window.addEventListener('domcontentloaded', () => {
        const pElm = document.getElementById('hoge');
    });
</script>

<p id="hoge"></p>

上をmoduleで置き換えると下のようになります。

<script type="module">
    const pElm = document.getElementById('hoge');
</script>

<p id="hoge"></p>

そして、ES Modulesは後述するWebComponentsと相性が非常に良いです。

ただし、ES ModulesはIEには対応されていません。IEへの対応が必要な場合はBabelやWebpackを使うなどしましょう。

Web Componentsを知る

最後に、発展系としてWeb Componentsを紹介します。

Web Componentsとは

Web Componentsは、独自のタグを定義することでHTML要素のコンポーネントかを助ける仕組みです。複数のページにまたがって利用したり、様々な箇所で何度も利用したりするときに、JavaScriptで独自のHTMLタグを定義することでコードの重複を防ぎます。ReactやVueを利用したことがある人はイメージしやすいかもしれません。

コンポーネントの定義

下の例を見てみましょう。これは、実際にオリジナルなコンポーネントである <original-header><original-footer> を定義しているところです。

// CommonElements.js

class OriginalHeader extends HTMLElement {
    constructor() {
        super();
        this.render();
    }

    render() {
        // Divを作成し、自身(HTMLElement)に連結させています。
        const div = document.createElement('div');
        div.style.backgroundColor = 'lightskyblue';

        div.innerHTML = `
            <p>これはヘッダーです。ようこそオリジナルページへ。</p>
            <p>JavaScriptを通して読み込むことで、様々なHTMLから呼び出すことができます。</p>
        `;

        this.append(div);
    }
}

class OriginalFooter extends HTMLElement {
    constructor() {
        super();
        this.render();
    }

    render() {
        // こちらも同じです。
        const div = document.createElement('div');
        div.style.backgroundColor = 'lightskyblue';

        div.innerHTML = `
            <p>これはフッターです。ページを見ていただきありがとうございました。</p>
            <p>WebComponentsの仕組みを利用して、再利用可能なコードを目指しましょう。</p>
        `;

        this.append(div);
    }
}

// HTMLElementを継承しただけでは使用できる状態になりません。
// customElements.define関数を使い、登録する必要があります。
customElements.define('original-header', OriginalHeader);
customElements.define('original-footer', OriginalFooter);

定義したコンポーネントの使用

上で定義したエレメントを実際に使用してみます。

<script type="module">
    import './CommonElements.js';
    // CommonElementsの中でcustomElements.define()しているため、
    // ここでは読み込むだけで完了です。
</script>

<original-header></original-header>
<div>
    これはページの内容です。このHTMLがとても綺麗なことに注目してください。
</div>
<original-footer></original-footer>

このページをWebブラウザで読み込むと、下のようなページが出現します。

スクリーンショット 2020-07-24 16.05.09.png

定義した内容が表示されていることがわかります。

例にあるように、ヘッダーやフッターなど様々なページで使いまわされる要素をCustomElementsとして定義しておけば、各ページごとにHTMLを定義する必要がなくなります。

ロジックやプロパティ渡しも可能

定義したエレメントは普段使っているエレメントと同じです。ReactやVueのように独自の記法を理解する必要はなく、基本的には今までと同じ感覚で定義することが可能です。
ロジックが必要なボタンやフォームなどでも、ロジックをあらかじめメソッドとして定義しておけば様々なページで使い回すことができ、結果的にページ独自のスクリプト量を減少させることが可能です。ReactやVueと同じように、HTML側からプロパティとしてデータを渡すことも可能です。

Web Componentsは(IEを除く)ブラウザ標準ですので、すぐに使い始めることができます。

フロントエンドフレームワークの導入を考え続ける

最後に、タイトルと相反する内容にはなりますがフロントエンドフレームワークの導入を考え続けましょう。

ReactやVueなどのフロントエンドフレームワークは、DOMとうまく付き合う方法を提供しています。
ReactはHTMLをJavaScriptのオブジェクトとして扱い、VueはDOMの操作を抽象化することでコードを簡潔にすることを目指しています。

開発者は設けられた枠組みの中でコードを書くことで、コードは分割され、コンポーネントとして再利用が可能な形になり、見通しが向上します。最近では、DOMの更新をできるだけ抑え、ページの高速化ができるような仕組みも整えられています。

一方、生のJavaScriptを利用すると、全て自由にコードを記述することができるようになります。ReactやVueなどとは違い、直感的な操作ができます。

ですが、DOMの操作には制限がありません。そのような中でDOMを使うとあっという間にJavaScriptとDOM、延いてはHTMLと密結合なコードを生成することになります。機能の追加や削除を繰り返しているうちに画面描写に関するコードがスクリプト中に散らばり、可読性が低下していきます。

ここまでに書いた様々な工夫を試すことで、ある程度コードの品質を維持することはできます。しかしながら、これは考え方でありフレームワークによって提供される枠組みではありません。可能であれば、フロントエンドフレームワークを導入を進めてみましょう。

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

Unchecked runtime.lastError: The message port closed before a response was received. というエラー解決方法

Chromeブラウザで、謎のエラーが出現

パソコンを購入して間もない時に、Chromeブラウザ Consoleにて、
Unchecked runtime.lastError: The message port closed before a response was received.というエラーが現れた。

原因

ChromeのNorton Safe Webという拡張機能が ON
無題.png

解決方法

ChromeのNorton Safe Webという拡張機能を OFFに設定
無題1.png

結果Consoleはキレイな状態となった。
無題2.png

*もしセキュリティソフトが原因でconsoleにエラーが表示されているんじゃないか疑ってみ、確認しよう。

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

Javascriptの復習(6)

本記事について

この記事はJavascript初学者がアウトプットの場として書いている記事です。大半の人にとっては既知の内容だと思われますので、無視してください。記事の内容に間違いがある場合は指摘してくださると助かります。

配列を操作するメソッド

タイトル通りです。Javascriptにおける配列とは、複数の要素が纏められた1つのオブジェクトです。今回学習したのは"push"、"forEach"、"find"、"filter"、そして"map"というメソッドです。

script.js
const fruits = ["リンゴ", "バナナ", "オレンジ"];

const numbers = [1, 2, 3];

pushメソッド

pushメソッドは配列に要素を追加するメソッドです。配列の最後に追加されます。

script.js
const numbers = [1, 2, 3];

//配列numbersに要素4を追加
numbers.push(4);
//コンソール上に [1, 2, 3, 4] と表示される。
console.log(numbers);

forEachメソッド

forEachメソッドは配列内の全ての要素に同じ処理を施すメソッドです。
以下の例ではforEachメソッドの中にアロー関数が入っております。配列内の各要素はアロー関数の引数(今回はnumber)に代入されて、アロー関数の処理が行なわれます。
今回の例のようにメソッドの引数として入っている関数は"コールバック関数"といいます(コールバック関数については後ほど詳しく書きます)。

script.js
const numbers = [1, 2, 3];

numbers.forEach((number) => {
  console.log(number);
});

//コンソール上で以下のように表示される
1
2
3

findメソッド

findメソッドは、コールバック関数に記述されている条件を満たす配列内の要素を見つけ出すメソッドです。但し、条件を満たす要素を全て見つけ出すのではなく、最初に見つけ出された要素しか結果として表示されません。

script.js
const numbers = [1, 4, 9];

const foundNumber = numbers.find((number) => {
//3より大きい数字を結果として返す
  return number > 3;
});
//コンソール上に4のみ表示される(9は表示されない)。
console.log(foundNumber);

応用的な使い方ですが、配列内の要素がオブジェクトでもfindメソッドで結果を出すことができます(結果はオブジェクトが表示される)。

script.js
const members = [{id: 1, name: "Taro"}, {id: 2, name: "Hanako"} ];

const foundMember = members.find((member) => {
//オブジェクトのidと一致するものを探し出す。
  return member.id === 1;
});
console.log(foundmember);

//コンソール上には以下のように表示される。
{id: 1, name: "Taro"}

filterメソッド

filterメソッドは、条件を満たす配列内の要素を纏めて再び配列にするメソッドです(findメソッドとは違い全て抽出される)。

script.js
const numbers = [1, 4, 9];

const filteredNumber = numbers.filter((number) => {
//3より大きい数字を配列に纏める。
  return number > 3;
});
//コンソール上には[4, 9]として表示される。
console.log(filteredNumber);

mapメソッド

mapメソッドは、配列内の全ての要素にメソッド内のコールバック関数の処理を施すメソッドです。

script.js
const numbers = [1, 2, 3];

const doubleNumber = numbers.map((number) => {
//全ての要素(数字)を2倍にする。
  return number*2;
});
//コンソール上には[2, 4, 6]として表示される。
console.log(doubleNumber);

これまでのメソッドと同様にオブジェクトでも使用できます

script.js
const members = [{firstName: "Taro", lastName: "Yamada"}, {firstName: "Hanako", lastName: "Suzuki"}];

const fullName = members.map((member) => {
//オブジェクト内のfirstNameとlastNameを合体させる。
  return member.firstName + member.lastName;
});
console.log(doubleNumber);

//コンソール上には以下のように表示される。
["TaroYamada", "HanakoSuzuki"]

コールバック関数について

ここまでしれっと使ってきたコールバック関数について書いていきます。Javascriptでは文字列や数字を引数として関数に渡すことができますが、関数も引数として渡すことができます。この引数として使用される関数をコールバック関数と言います。

 コールバック関数を呼び出す時と渡す時

コールバック関数を呼び出す時は関数名の後ろに()を書きます。引数として渡す時は()は不要です。

script.js
const greet = () => {
  console.log("こんにちは");
};

const call = (callback) => {
  console.log("やぁ");
//コールバック関数の呼び出し(今回は関数greetの呼び出し)
  callback();
};
//関数greetを引数として扱っている。
call(greet);

//コンソール上には以下のように表示される。
"やぁ"
"こんにちは"

関数を定義せずに直接()の中に関数を書いても機能します。

script.js
const call = (callback) => {
  console.log("やぁ");
  callback();
};


call(() => {
  console.log("こんにちは");
});

//コンソール上には以下のように表示される。
"やぁ"
"こんにちは"

 コールバック関数の引数について

コールバック関数は、普通の関数と同様に引数を渡すことができます。
このように書くメリットとしてはコールバック関数を柔軟に変更できることだと思います。

script.js
const profile = (callback) => {
  callback("タカシ");
};

//ここを書き変えることでコンソール上の結果を変更できる?
profile((name) => {
  console.log(name);
});

//コンソール上には以下のように表示される。
"タカシ"

引数は複数渡すことができます(複数渡す時はコールバック関数の引数と実際に渡す引数の数を揃える必要があります)。

script.js
const profile = (callback) => {
  callback("タカシ", "18");
};

profile((name, age) => {
  console.log(`${name}${age}歳です`);
});

//コンソール上には以下のように表示される。
"タカシは18歳です"

感想

progateで学習した内容はこれで終わりです。現状、コンソール上で結果が表示されるコードしか書いてないので今後はjQueryを学習して動的な表現を付与する方法を身に付けたいです。

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

【Nuxt.js】mixin実践編:Sass & mixinで簡単メディアクエリ

? この記事はWP専用です
https://wp.me/pc9NHC-sl

前置き

Sass変数とmixinを使って
レスポンシブ 対応を簡単に管理していきます✨

スタイルを当てるごとに毎回
@media screen and (max-width: 480px)
などと書き足していくのは面倒ですよね?

これが@includeだけ書けばよくなります!
また、pxを変数化して共通化&変更が
とっっっても簡単になります?

mixinについてはこちら
https://wp.me/pc9NHC-s6

Sassの導入方法、変数の使い方はこちら
https://wp.me/pc9NHC-sc

簡単な使い方

メディアクエリ をやる前に!
まずはSassとmixinを使った簡単な例をどうぞ?‍♀️

引数を$変数にする

変数名(変数に設定された値)を
引数で上書きできるやり方。

ただこれならmixin使わずとも
タグ別に普通の共通cssで指定しとけば良いです?‍♀️

今回はSassとmixinを簡単に
理解するためだけのサンプルです?

スクリーンショット-2020-07-14-10.30.49.png

nuxt.config.js
export default {
 styleResources: {
   scss: [
     '@/assets/scss/_mixin.scss',
   ]
 },
}
mixin.scss
@mixin hoge($color: pink, $fSize: 32px) {
 color: $color;
 font-size: $fSize;
}

【index.vue】

デフォルトをred, 20pxでマージしています。
引数を渡さなければデフォルトが適応されます。

コンフリクトが起きた場合のマージ
https://wp.me/pc9NHC-s6

index.vue
<template>
 <div class="page">
   <p>Hello Nuxt.js!</p>
 </div>
</template>

<script>
export default {
}
</script>

<style lang="scss" scoped>
 .page {
   padding: 20px;
   @include hoge(red, 20px)
 }
</style>

メディアクエリ

ではメディアクエリ をやっていきましょう!

? 続きはWPでご覧ください?
https://wp.me/pc9NHC-sl

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

簡単レシート印刷 receiptline と 20 行の JavaScript でレジプリンターをインスタントカメラにしてみた

日本発のオープンソース receiptline でレシート印刷に少しずつトライしています。
今回は、前回のレシートプリンターと変換 API を使ってアプリを作ります。

実はこれを作りたかった

“レジプリンター式インスタントカメラで「レシート日記」はなぜ楽しいのか?”
https://weekly.ascii.jp/elem/000/004/004/4004167/

“1枚1円の安さが正義、レシート現像のトイカメラが楽しい|ベストバイ2019”
https://japanese.engadget.com/jp-2019-12-23-1-1-2019.html

“むしろ大人が欲しい多機能ぶり! プリンター内蔵の子ども用カメラ「myFirst Camera Insta 2」”
https://capa.getnavi.jp/news/339006/

そうです。インスタントカメラです。
記事を読んでいるだけで楽しさが伝わってきます。

早速これを receiptline で作ります。
もちろんカメラデバイスが必要です。

インスタントカメラのコード

アプリの開発環境は何でも構わないのですが、最短コースで。
receiptline の Node.js サンプルプログラムをベースにします。

作業フォルダを作成して example/nodejs 以下をまるごとコピー。
コピーした wwwroot/index.html 書き換えます。

wwwroot/index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Instant Camera</title>
    <script type="text/javascript">
        async function initialize() {
            const video = document.querySelector('video');
            const canvas = document.querySelector('canvas');
            // video
            video.srcObject = await navigator.mediaDevices.getUserMedia({ audio: false, video: true });
            video.onclick = event => {
                // image
                canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
                let data = `{i:${canvas.toDataURL('image/png').slice(22)}}\n`;
                // barcode
                const now = new Date();
                const iso = new Date(now.getTime() - now.getTimezoneOffset() * 60000).toISOString();
                data += `{c:${iso.replace(/\D/g, '').slice(2, 14)};o:ean,24}|`;
                // send data
                const xhr = new XMLHttpRequest();
                xhr.open('POST', 'printer');
                xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');
                xhr.send(data);
            };
        }
    </script>
</head>
<body onload="initialize()">
    <video autoplay style="width: 100%;"></video>
    <canvas width="576" height="432" style="display: none;"></canvas>
</body>
</html>

20 行の JavaScript コードで出来てしまいました。

写真サイズ

576 × 432 ピクセルに固定してあります。
レシートプリンターの幅に合わせて Canvas のサイズを変更してください。

  • TM-T88V (80mm)
    • 512 × 384 ピクセル
  • mC-Print3 (80mm)
    • 576 × 432 ピクセル (変更不要)
<canvas width="576" height="432" style="display: none;"></canvas>

撮影日時

バーコードで撮影日時を印刷しています。
2023 年 7 月 20 日 12 時 34 分 56 秒の場合。

  • データ (12 桁)
    • 23 (年)
    • 07 (月)
    • 20 (日)
    • 12 (時)
    • 34 (分)
    • 56 (秒)
  • チェックデジット (1 桁)
    • 1 (自動計算)

バーコードの種類は JAN (EAN) コードです。
バーコード / QR コードリーダーアプリで読み取ることができます。

ReceiptLine
{c:230720123456;o:ean,24}|

01.png

プリント先

プリンター ID を printer に固定してあります。
印刷設定に合わせて変更することができます。

xhr.open('POST', 'printer');

印刷設定

コピーした printers.json で設定します。
使用するレシートプリンターに合わせて調整が必要です。
特にガンマ補正 gamma の値は写真の画質に影響します。

TM-T88V (80mm)

printers.json
{
    "printer": {
        "host": "192.168.1.2",
        "port": 9100,
        "cpl": 42,
        "encoding": "cp932",
        "gamma": 1.8,
        "upsideDown": false,
        "command": "escpos"
    }
}

mC-Print3 (80mm)

printers.json
{
    "printer": {
        "host": "192.168.1.3",
        "port": 9100,
        "cpl": 48,
        "encoding": "cp932",
        "gamma": 1.8,
        "upsideDown": true,
        "command": "starmbcs"
    }
}

いざ撮影!

サーバーを起動します。実行環境は Windows 10 です。

$ node start.js

起動後、http://localhost:10080 を開きます。
カメラに対するアクセス権を求められるので許可してください。

02.png

映像だけのシンプルなページです。クリックすると即時印刷します。

03.jpg

04.jpg

業務用レジプリンタ式インスタントカメラ、略して “レジカメ” 誕生です!

新たな可能性

インスタ時代の新型プリントシール機が発売されたそうです。

“セガ、最新プリクラ機「fiz」でプリントシール機市場に20年ぶりの再参入”
https://weekly.ascii.jp/elem/000/004/019/4019040/

ふと思いました。これも出来るのでは!?
Canvas でキャプチャした画像を加工すればいいのです。
スタンプ押してよし、文字を入れてよし、ご当地フレームもよし。

しかも、業務用レジプリンタ式なら、このような特長もあります。

  • 圧倒的低コスト!
  • お財布といつも一緒!
  • 盛らなくても映える! (個人の感想です)

撮り放題にして集客するなど、プリントシール機と違うビジネスが生まれそうな予感。

これにて receiptline シリーズは一旦完結。
また何か作ったら投稿します。ではまた!

05.jpg

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

Vuye.Js カーソルがリストに重なると文字が拡大され、値を取得する

VueJs初学者のちょっとした倉庫です。簡単だけどシンプルだけど使えそうな表現を目指しています。

カーソルが要素に重なると文字が拡大され、選択された値を取得する

←気が向いたらLGTMボタンぽちっとお願いします。

カーソルが要素に重なると拡大され、要素の値を取得します。

VueJsPickUp2.gif

画面左下がカーソルが重なっている値です。取得した値によってわんちゃんの画像を表示するとかよさそうですね。今回は柴犬にカーソルが重なると画像が表示されるようになっています。

shuffle.html
<!doctype html>
<html lang="ja">
<head>

    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>PickUp</title>

    <style>
        body {
            font-family: "Montserrat", "游ゴシック", YuGothic, "ヒラギノ角ゴ ProN W3", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, sans-serif;
        }

        ul {
            margin-left: 700px;
            list-style: none;
        }

        div#mainlist {
            color: #8EF1FF;
            font-size: 25px;
        }

        div#mainlist ul li:hover {
            list-style-type: ">";
            color: #7B3CFF;
            font-size: 75px;
        }


        .SubTitle {
            margin-left: 700px;
            position: relative;
            display: inline-block;
            font-weight: bold;
            padding: 0.25em 0;
            text-decoration: none;
            color: #67c5ff;
        }

        .SubTitle:before {
            position: absolute;
            content: '';
            width: 100%;
            height: 4px;
            top: 100%;
            left: 0;
            border-radius: 3px;
            background: #67c5ff;
            transition: .2s;
        }

        .SubTitle:hover:before {
            top: -webkit-calc(100% - 3px);
            top: calc(100% - 3px);
        }

    </style>
</head>
<body>
<div id="app">
    <h1>
        <div class="SubTitle">あなたの好きな犬種は??</div>
        <div id="mainlist">
            <ul>
                <li v-for="dog in dogs" :key="dog.name" @mouseover="activeDog=dog.name">
                    {{dog.name}}
                </li>
                <img v-show="this.activeDog=='柴犬'" src="sibainu.png"></img>
            </ul>
        </div>
    </h1>
    <p v-show="activeDog">selected:{{activeDog}}</p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js"></script>

<script>
    var app = new Vue({
        el: "#app",
        data: {
            dogs: [
                {name: 'チワワ'},
                {name: 'トイプードル'},
                {name: 'ポメラニアン'},
                {name: '柴犬'},
                {name: 'ダックスフンド'},
                {name: 'パグ'},
                {name: 'ブルドック'},
                {name: 'コーギー'},
                {name: 'パピヨン'},
                {name: 'マルチーズ'},
                {name: 'シェパード'},
                {name: 'ブルドッグ'},
                {name: 'プードル'},
                {name: 'ラブラドール'},
                {name: 'ビーグル'},
                {name: 'ゴールデンレトリバー'},
                {name: 'ラブラドールレトリバー'},
                {name: '秋田犬'},
            ],
            activeDog: ""
        },
        methods: {}
    });
</script>
</body>
</html>

以上です。気が向いたらLGTMぽちっとお願いします。

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

Vuye.Js カーソルが要素に重なると文字が拡大され、値を取得する

VueJs初学者のちょっとした倉庫です。簡単だけどシンプルだけど使えそうな表現を目指しています。

カーソルが要素に重なると文字が拡大され、選択された値を取得する

←気が向いたらLGTMボタンぽちっとお願いします。

カーソルが要素に重なると拡大され、要素の値を取得します。

VueJsPickUp2.gif

画面左下がカーソルが重なっている値です。取得した値によってわんちゃんの画像を表示するとかよさそうですね。今回は柴犬にカーソルが重なると画像が表示されるようになっています。

shuffle.html
<!doctype html>
<html lang="ja">
<head>

    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>PickUp</title>

    <style>
        body {
            font-family: "Montserrat", "游ゴシック", YuGothic, "ヒラギノ角ゴ ProN W3", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, sans-serif;
        }

        ul {
            margin-left: 700px;
            list-style: none;
        }

        div#mainlist {
            color: #8EF1FF;
            font-size: 25px;
        }

        div#mainlist ul li:hover {
            list-style-type: ">";
            color: #7B3CFF;
            font-size: 75px;
        }


        .SubTitle {
            margin-left: 700px;
            position: relative;
            display: inline-block;
            font-weight: bold;
            padding: 0.25em 0;
            text-decoration: none;
            color: #67c5ff;
        }

        .SubTitle:before {
            position: absolute;
            content: '';
            width: 100%;
            height: 4px;
            top: 100%;
            left: 0;
            border-radius: 3px;
            background: #67c5ff;
            transition: .2s;
        }

        .SubTitle:hover:before {
            top: -webkit-calc(100% - 3px);
            top: calc(100% - 3px);
        }

    </style>
</head>
<body>
<div id="app">
    <h1>
        <div class="SubTitle">あなたの好きな犬種は??</div>
        <div id="mainlist">
            <ul>
                <li v-for="dog in dogs" :key="dog.name" @mouseover="activeDog=dog.name">
                    {{dog.name}}
                </li>
                <img v-show="this.activeDog=='柴犬'" src="sibainu.png"></img>
            </ul>
        </div>
    </h1>
    <p v-show="activeDog">selected:{{activeDog}}</p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js"></script>

<script>
    var app = new Vue({
        el: "#app",
        data: {
            dogs: [
                {name: 'チワワ'},
                {name: 'トイプードル'},
                {name: 'ポメラニアン'},
                {name: '柴犬'},
                {name: 'ダックスフンド'},
                {name: 'パグ'},
                {name: 'ブルドック'},
                {name: 'コーギー'},
                {name: 'パピヨン'},
                {name: 'マルチーズ'},
                {name: 'シェパード'},
                {name: 'ブルドッグ'},
                {name: 'プードル'},
                {name: 'ラブラドール'},
                {name: 'ビーグル'},
                {name: 'ゴールデンレトリバー'},
                {name: 'ラブラドールレトリバー'},
                {name: '秋田犬'},
            ],
            activeDog: ""
        },
        methods: {}
    });
</script>
</body>
</html>

以上です。気が向いたらLGTMぽちっとお願いします。

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

【Redux】基礎文言

最近Reduxについて勉強を始めたのですが、難しくて、初見では頭にスパッと入らず苦労しています。。。
なので今回は、技術的な投稿ではなく、基礎文言を復習していきたいと思います。

(ただのアウトプットなので、参考にはなりません。)

Actionとは

Actionとは、
・JavaScirptのオブジェクトで、オブジェクトの内部で type というキーと、それに対応する値を持つ。
・Applicationの中で何が起きたかを示すデータのこと。

特徴として、
type はユニークなものではいけないという特徴がある。

ActionCreater

ActionCreaterとは、
Actionを返す関数のことです。

Actionで定義したオブジェクトの記述のみでは、アプリケーションで活用することができません。
なので、Actionを返す関数を定義する必要があります。

Reducer

Reducerとは、
Action が発生した時に、そのActionに組み込まれている type に応じて、状態をどう変化させるかを定義するものです。

connect

connectとは、
connect関数を使用して、state や action と Component とを関連付けて、ビューのイベントで状態を遷移させて、遷移後の状態を画面に再描画する。

mapStateToPropsとは、
state の情報から Component に必要なものを取り出して、 Component 内の props としてマッピングする機能を持つ関数です。引数には状態のトップレベルを持つ state を書いてどういったオブジェクトを props として対応させるのかを関数の戻り値として定義します。

mapStateToPropsとは、
ある action が発生した時に、 reducer に type に応じた状態繊維を実行させるための関数が dispatch になります。

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

SVGを書くのが面倒なので入力補完できるようにしてみる 4

今回はゆるーく、便利系の実装。
SVGタグだけじゃなく、HTMLタグも扱えるようにしたいのでそのあたりで楽をするためのモノ作っていきます。

シリーズ

  1. SVGを書くのが面倒なので入力補完できるようにしてみる 1
    内部に持った状態を変更させるオブジェクト、メソッドチェイン、基本シェイプ、<path>のd要素
  2. SVGを書くのが面倒なので入力補完できるようにしてみる 2
    useに潜んでいた罠、<animate>
  3. SVGを書くのが面倒なので入力補完できるようにしてみる 3
    render周りを便利にしたい
  4. SVGを書くのが面倒なので入力補完できるようにしてみる 4

引き続き ↓ コレ作ってるときの話です。
GitHub: mafumafuultu/svg.js dev

とりあえず今回で一区切り

便利系の実装

  • fill, stroke は割と使う
  • viewBox で中央に表示する座標と倍率指定があるとよさそう
  • undefined 長い
  • textContent 長い
  • onloadpromise になっていた方が使い勝手がいいと思う。
  • 通常のHTMLタグも喰えるようにしたので、HTMLElementが混じっていても使えるようにする
  • すでに作っていた関数を、抱えている要素にあてたい

準備

 これらを用意する前に、まず内部的にエラーを防ぐ仕組み・製薬を用意。

const __BASE_PROTO__ = {
  type : { value (v, is) {return typeof t === is;} },
  of : { value (v, t) {return v instanceof t;} },
  has : { value (prop) {return prop in this['@'];} }
};

属性は前方互換としても存在している__BASE_PROTO__.attrs を呼ぶと、だいたい解決する。

よく使う属性とか

// 長い
const none = undefined;

const __BASE_PROTO__ = {
    fill: {
        value(color = 'transparent') {
            return this.type(color, 'string')
                ? this.attrs({'fill': color})
                : this;
        }
    },
    stroke: {
        value(color = 'transparent', width) {
            return this.type(color, 'string')
                ? Object.is(width, undefined)
                    ? this.attrs({stroke : color})
                    : this.attrs({stroke : color, 'stroke-width' : width})
                : this;
        }
    },
    fillStroke : {
        value(fill = 'transparent', stroke = 'transparent') {
            return this.type(fill, 'string') && this.type(stroke, 'string')
                ? this.attrs({fill, stroke})
                : this;
        }
  },
  txt : {
        value(txt = '') {
            if (this.has('textContent')) {
                this['@'].textContent = txt;
            }
            return this;
        }
  }
};

viewbox zoom

viewBox の指定方法は rect と同じなので計算は簡単ですね。

const __BASE_PROTO__ = {
  zoom: {
        value(center=[0,0], x) {
            if (this.has('viewBox')) {
                let {width, height} = this.attrs();
                let w = width/x, h = height/x;
                this.attrs({viewBox: `${center[0] - w/2} ${center[1] - h/2} ${w} ${h}`})
            }
            return this;
        }
    },
};

onloadをpromiseに

const onload = () => document.readyState !== 'complete'
    ? new Promise(r => document.addEventListener('readystatechange', () => {
        switch (document.readyState) {
            case 'complete': r();break;
            default:
        }
    }))
    : Promise.resolve();

普通のHTMLタグも扱いやすく

 なんだかんだ使う id とか class とか data とか

const __BASE_PROTO__ = {
  id : {
    value (id) {
      if (this.has('id') && this.type(id, 'string')) {
        this['@'].id = id;
      }
      return this;
    }
  },
    cls : {
        value(v, force = true) {
            if (this.has('classList') && this.type(v, 'string')) {
                this['@'].classList.toggle(v, force);
            }
            return this;
        }
  }, 
    data : {
        value(o) {
            if (this.has('dataset') && this.type(k, 'string')) {
                v == null
                    ? delete this['@'].dataset[k]
                    : this['@'].dataset[k] = v;
            }
            return this;
        }
    }
}

すでに作っていた関数を、抱えている要素にあてたい

全部をErrにするのもいまいちイケてないので try-catch

const __BASE_PROTO__ = {
    f: {
        value(f = el => {}) {
            try {
                f(this['@'])
            } catch (e) {
                console.error(e);
            }
            return this;
        }
    },
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactを初めて使ってCRUDアプリを作ってみた記録

JSフレームワークをやってみようと思い立ったので、Reactを使ってみた記録です。

マシンスペック

  • Mac mini 2018
  • macOS Catalina(10.15.x)
  • Intel Core-i7 3.2GHz 6コア
  • メモリ 32GB
  • SSD 512GB

サーバー環境

  • CentOS7.x
  • MySQL5.7.x
  • Nginx最新版
  • PHP7.1.x
  • Laravel5.6
  • Visual Studio Code最新版

フロント環境

  • React最新版
  • Bootstrap最新版
  • Visual Studio Code最新版

やること

  • ReactとLaravelでCRUDアプリを作る

補足

各環境の操作は、下記のように記載します。

[Cent]$ MacのターミナルからCentOSに接続して作業
[Mac]$ MacのターミナルでMac内の作業

前提

サーバー環境は構築済み
【コピペ】VirtualBox+Vagrant+AnsibleでLaravel開発環境を構築その弐

サーバーの準備

この記事用に作りました!
https://github.com/bobtabo/laravel5.6

[Mac]$ vagrant ssh
[Cent]$ rm -fdR laravel5
[Cent]$ git clone https://github.com/bobtabo/laravel5.6.git laravel5
[Cent]$ cd laravel5
[Cent]$ composer install
[Cent]$ chmod -R 777 storage
[Cent]$ chmod -R 777 bootstrap/cache
[Cent]$ cp -p .env.example .env
[Cent]$ php artisan key:generate
[Cent]$ vi .env
★DB設定を置換
:%s/DB_DATABASE=homestead/DB_DATABASE=hoge/g
:%s/DB_USERNAME=homestead/DB_USERNAME=fuga/g
:%s/DB_PASSWORD=secret/DB_PASSWORD=vagrant#VAGRANT1234/g
:wq
[Cent]$ bin/clear-laravel.sh
[Cent]$ php artisan migrate:refresh --seed

フロントの準備&練習

[Mac]$ brew install npm
[Mac]$ brew install node
[Mac]$ mkdir react-dev
[Mac]$ cd react-dev
[Mac]$ mkdir -p src/js
[Mac]$ npm init -y

★npm install --save-dev webpack webpack-cli webpack-dev-serverでエラーになったら行う
[Mac]$ sudo rm -rf $(xcode-select -print-path)
[Mac]$ sudo rm -rf /Library/Developer/CommandLineTools
[Mac]$ xcode-select --install

[Mac]$ npm install --save-dev webpack webpack-cli webpack-dev-server
[Mac]$ npm install -g webpack webpack-cli
[Mac]$ npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader
[Mac]$ npm install --save-dev react react-dom

下記のサイトに従って、ファイルを作成する
今から始めるReact入門 〜 React の基本

  • webpack.config.js
  • src/index.html
  • src/js/client.js
[Mac]$ webpack --mode development
[Mac]$ open -a '/Applications/Google Chrome.app' ./src/index.html

※参考
https://qiita.com/TsutomuNakamura/items/72d8cf9f07a5a30be048
https://qiita.com/baby-0105/items/18f6fbc073e160bf83ac
https://qiita.com/nwtgck/items/4b9c83227bc334576552

フロントを作ってみた

上記の練習で作ったファイル(src/index.html、src/js/client.js)を色々いじってみた
https://github.com/bobtabo/react

[Mac]$ cd ..
[Mac]$ rm -fdR react-dev
[Mac]$ git clone https://github.com/bobtabo/react.git react-dev
[Mac]$ cd react-dev
[Mac]$ npm install
[Mac]$ webpack --mode development
[Mac]$ open -a '/Applications/Google Chrome.app' ./src/index.html

APIのレスポンス表示できた!
スクリーンショット 2020-07-24 15.29.33.png

※参考
https://qiita.com/dyoshikawa/items/c8b09cde728388c8feec
https://qiita.com/rei67/items/273ebef44d19912733b7

感想

LaravelをVSCodeで書いてみたけど、やっぱりPhpStormが良いかな。
プラグインあれこれ入れたけど、PhpStormの快適さを超えられなかった。

ReactでCRUDアプリ作ろう!と思ったけど、R作ったところで、お腹いっぱい。
続きは、また今度!

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

Vue.Js リストをランダムにゆっくり入れ替える

VueJs初学者のちょっとした倉庫です。簡単だけどシンプルだけど使えそうな表現を目指しています。

クリックでリストがゆっくり入れ替わる

←気が向いたらLGTMボタンぽちっとお願いします。

ボタン押下でリストのゆっくりと順番が変わり、上位3つの要素にcssが適用されます。

VueJsShuffle.gif

shuffle.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>shuffle</title>

    <style>
        /*要素が並び変えにかかる時間*/
        .flip-list-move {
            transition: transform 1.5s;
        }

        ul {
            margin-left: 800px;
            list-style: none;
        }

        div#mainlist {
            color: #8EF1FF;
            font-size: 15px;
        }

        div#mainlist ul li:first-child {
            list-style-type: "1:";
            color: #7B3CFF;
            font-size: 75px;
        }

        div#mainlist ul li:nth-child(2) {
            list-style-type: "2:";
            color: #B384FF;
            font-size: 50px;
        }

        div#mainlist ul li:nth-child(3) {
            list-style-type: "3:";
            color: #EAD9FF;
            font-size: 25px;
        }

        .btn-border-bottom {
            margin-left: 750px;
            position: relative;
            display: inline-block;
            font-weight: bold;
            padding: 0.25em 0;
            text-decoration: none;
            color: #67c5ff;
        }

        .btn-border-bottom:before {
            position: absolute;
            content: '';
            width: 100%;
            height: 4px;
            top: 100%;
            left: 0;
            border-radius: 3px;
            background: #67c5ff;
            transition: .2s;
        }

        .btn-border-bottom:hover:before {
            top: -webkit-calc(100% - 3px);
            top: calc(100% - 3px);
        }

    </style>
</head>
<body>
<div id="app">
    <h1>
        <div class="backGround">
            <div class="btn-border-bottom" v-on:click="shuffle">あなたにお勧めの犬種は??</div>
            <div id="mainlist">
                <transition-group name="flip-list" tag="ul">
                    <li v-for="dog in dogs" :key="dog.name">
                        <p>
                            {{dog.name}}
                        </p>
                    </li>
                </transition-group>
            </div>
            </ul>
        </div>
    </h1>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js"></script>

<script>
    var app = new Vue({
        el: "#app",
        data: {
            error: "error",
            dogs: [
                {name: 'チワワ'},
                {name: 'トイプードル'},
                {name: 'ポメラニアン'},
                {name: '柴犬'},
                {name: 'ダックスフンド'},
                {name: 'パグ'},
                {name: 'ブルドック'},
                {name: 'コーギー'},
                {name: 'パピヨン'},
                {name: 'マルチーズ'},


            ]
        },
        methods: {
            shuffle: function (e) {
                this.dogs = _.shuffle(this.dogs);

            }
        }
    });
</script>
</body>
</html>

以上です。気が向いたらLGTMぽちっとお願いします。

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

Vue.Js リストをランダムに入れ替え、動的に表示する

VueJs初学者のちょっとした倉庫です。

クリックでリストが入れ替わる

ボタン押下でリストの順番が変わり、上位3つの要素にcssが適用されます。

VueJsShuffle.gif

shuffle.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>shuffle</title>

    <style>
        /*要素が並び変えにかかる時間*/
        .flip-list-move {
            transition: transform 1.5s;
        }

        ul {
            margin-left: 800px;
            list-style: none;
        }

        div#mainlist {
            color: #8EF1FF;
            font-size: 15px;
        }

        div#mainlist ul li:first-child {
            list-style-type: "1:";
            color: #7B3CFF;
            font-size: 75px;
        }

        div#mainlist ul li:nth-child(2) {
            list-style-type: "2:";
            color: #B384FF;
            font-size: 50px;
        }

        div#mainlist ul li:nth-child(3) {
            list-style-type: "3:";
            color: #EAD9FF;
            font-size: 25px;
        }

        .btn-border-bottom {
            margin-left: 750px;
            position: relative;
            display: inline-block;
            font-weight: bold;
            padding: 0.25em 0;
            text-decoration: none;
            color: #67c5ff;
        }

        .btn-border-bottom:before {
            position: absolute;
            content: '';
            width: 100%;
            height: 4px;
            top: 100%;
            left: 0;
            border-radius: 3px;
            background: #67c5ff;
            transition: .2s;
        }

        .btn-border-bottom:hover:before {
            top: -webkit-calc(100% - 3px);
            top: calc(100% - 3px);
        }

    </style>
</head>
<body>
<div id="app">
    <h1>
        <div class="backGround">
            <div class="btn-border-bottom" v-on:click="shuffle">あなたにお勧めの犬種は??</div>
            <div id="mainlist">
                <transition-group name="flip-list" tag="ul">
                    <li v-for="dog in dogs" :key="dog.name">
                        <p>
                            {{dog.name}}
                        </p>
                    </li>
                </transition-group>
            </div>
            </ul>
        </div>
    </h1>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js"></script>

<script>
    var app = new Vue({
        el: "#app",
        data: {
            error: "error",
            dogs: [
                {name: 'チワワ'},
                {name: 'トイプードル'},
                {name: 'ポメラニアン'},
                {name: '柴犬'},
                {name: 'ダックスフンド'},
                {name: 'パグ'},
                {name: 'ブルドック'},
                {name: 'コーギー'},
                {name: 'パピヨン'},
                {name: 'マルチーズ'},


            ]
        },
        methods: {
            shuffle: function (e) {
                this.dogs = _.shuffle(this.dogs);

            }
        }
    });
</script>
</body>
</html>

以上です。気が向いたらLGTMぽちっとお願いします。

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

Javascript Array.splice()の使い方

splice()の引数による挙動の違いについて解説

今回は1〜3までの引数を持つときの動きを記述
最終的にコンソール結果はこの通り
スクリーンショット 2020-07-24 13.14.51.png

1個の時

//引数が1つの場合
let array = ['1番目', '2番目', '3番目', '4番目', '5番目','6番目','7番目'];
console.log(array);

let arr = array.splice(2);//結果:['3番目', '4番目', '5番目']←引数が1つだと配列の○番目のインデックス未満のインデックスを削除
                          //ここで注意なのは大元のarrayの3番目以降の要素が削除されること。つまりarrで表示される部分がarrayからarrに引越しした感じ
console.log(array);//結果: ["1番目", "2番目"]
console.log(arr);//結果:['3番目', '4番目', '5番目']

2個の時

//引数が2つの場合
let array2 = ['1番目', '2番目', '3番目', '4番目', '5番目','6番目','7番目'];
console.log(array2)
let arr2 = array2.splice(2,2)//配列の○(第一引数)番目から△(第二引数)個のインデックスを抜き取る。
console.log(array2);//結果:["1番目", "2番目", "5番目", "6番目", "7番目"]
console.log(arr2);//結果:["3番目", "4番目"]

3個の時

//引数が3つの場合
let array3 = ['1番目', '2番目', '3番目', '4番目', '5番目','6番目','7番目'];
console.log(array3)//3番目、4番目を抜き取り、その場所に新規値を追加(今回は'インデックスに追加'を追加)
let arr3 = array3.splice(2,2,'インデックスに追加')// ["1番目", "2番目", "インデックスに追加", "5番目", "6番目", "7番目"]
console.log(array3)//
console.log(arr3)//

ポイント

引数(n, l , o)

1個だったら引数からn番目以降のインデックスを取り出す(元の配列から削除)

2個だったら引数からn番目以降インデックスからl個のインデックスを取り出す(元の配列から削除)

3個だったら引数からn番目以降インデックスからl個のインデックスを取り出し(元の配列から削除)、n番目の次にoの値を追加する(lが0であれば単純にoを追加するということになる)

つまり

引数が1 or 2 なら削除で使う

引数が3 なら追加(削除も可能)

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

JavaScript: Set(?,?,?)とSet(?,?,?)の共通要素Set(?,?)を求める方法

この投稿では、JavaScriptの2つのSetから、その共通要素(intersection)を調べる方法を紹介します。

2つのSetの共通要素を調べる方法

const a = new Set(['?', '?', '?'])
const b = new Set(['?', '?', '?'])
const intersection = new Set([...a].filter(x => b.has(x)))
console.log(intersection)
//=> Set { '?', '?' }

このnew Set([...a].filter(x => b.has(x)))の部分が共通要素のSetを作るコードです。やっていることは、1つ目のセットの配列を作った上で、filterメソッドでその要素ひとつひとつに対して「2つ目のセットの要素かどうか」の基準で絞り込み、最終的に絞り込み結果の配列をSetに戻しているだけです。

汎用的に使う場合は関数化しておくといいかもしれません。

function intersectionOf(a, b) {
  return new Set([...a].filter(x => b.has(x)))
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptのthisどう理解したら

jsのthisについて色々なページを見たものを貼り散らかしています。
貼り付けているだけです、すみません。
貼り付けたようなページを見るときは理解できた気分になるのですが、
実際のコードを見ると途端にわからないことが多いです。

今わからないのは、Reactのページにあった
this.handleChange = this.handleChange.bind(this); です。

JSを理解するにはどうしたらいいんだろうか、、

NameForm.js
class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

ReactDOM.render(
  <NameForm />,
  document.getElementById('root')
);

jsのthisについて

https://qiita.com/takkyun/items/c6e2f2cf25327299cf03

jsのthis.js
function Test(){
  console.log("f:",this)
}
var obj = {name:"test",Test:test}

var obj = new Test();
obj.test();
console.log("obj:",this);

this

ES5 で bind() メソッド 呼び出し方にかかわらず関数の this の値を設定するために導入され、さらに ES2015 では、独自のthisの関連付けを行わないアロー関数が導入されました (これは包含する字句コンテキストの this の値を保持します)。

これならわかる.js
const test = {
  prop: 42,
  func: function() {
    return this.prop;
  },
};

console.log(test.func());
// expected output: 42

bind関数

  • bind は一度しか機能しない
bind関数.js
function f() {
  return this.a;
}

var g = f.bind({a: 'azerty'});
console.log(g()); // azerty

var h = g.bind({a: 'yoo'}); // bind は一度しか機能しない
console.log(h()); // azerty

var o = {a: 37, f: f, g: g, h: h};
console.log(o.a, o.f(), o.g(), o.h()); // 37,37, azerty, azerty

アロー関数

アロー関数.js
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject);

関数とthis

  • 関数を呼んだ時の .の前についているオブジェクトを指している。
  • .を省略するとグローバルオブジェクトを参照する

メソッドチェーン

callとappply.js
function test(){
  console.log(this)
}
var obj = {name:"obj"}
test()
test.call(obj)

コンストラクタ

コンストラクタ.js
var obj = new function(){
  this.name = "obj"
  console.log(this)
}


var obj = new function() {
    this.name = "obj"
    name ="obj2"
    console.log(this) // => {name: "obj"}
    console.log(name);
}

thisの4種類のパターン
1:メソッド呼び出しパターン メソッド基準
2:関数呼び出しパターン グローバル
3:コンストラクタ呼び出しパターン
4:apply,call呼び出しパターン

メソッド呼び出しパターン.js
//メソッド呼び出しパターン
var myObject = {
  value: 10,
  show: function() {
    console.log(this.value);
  }
}
myObject.show(); // 10
関数呼び出しパターン.js
function show(){
  console.log(this);
  this.value = 1;
}
show();

関数呼び出しパターン2.js
var myObject = {
  value: 1,
  show: function(){
    console.log(this.value);
    function show(){
      console.log(this.value);
    }
    show();
  }
};
myObject.show();

関数呼び出しパターン3.js
var myObject = {
  value: 1,
  show: function() {
    var self = this;
    console.log(self.value); // 1

    function show() {
      console.log(self.value); // 1
    }
    show();
  }
};
myObject.show();

コンストラクタ呼び出し
- 生成されるインスタンス自身が「this」にsetされる

コンストラクタ.js
function MyObject(value){
  this.value = value;
  this.increment = function(){
    this.value=this.value*4;
  };
}

var myObj = new MyObject(2);
console.log(myObj.value);

myObj.increment();
console.log(myObj.value);

apply, call呼び出しパターン

applycall.js
var myObject = {
  value: 1,
  show: function() {
    console.log(this.value);
  }
};
var yourObject = {
  value: 3
};

myObject.show(); // 1

myObject.show.apply(yourObject); // 3
myObject.show.call(yourObject); // 3

呼び出しパターン2

applycall.js
var myObject = {
  add: function(value1, value2) {
    console.log(this.value + value1 + value2);
  }
};
var yourObject = {
  value: 3
};

myObject.add.apply(yourObject, [2, 10]); // 15
myObject.add.call(yourObject, 2, 10); // 15

クロージャ

コンストラクタの使い方

  • JSではECMAScript 2015(ES6)からクラスを使うことができます。
  • 「new」を付けることで、生成されるインスタンスが「this」にセットされる
constracta.js
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

class ChildPerson extends Person {
    get result() {
        this.checkAge();
    }

    checkAge() {
        if(this.age < 20) {
            console.log(this.name + 'は未成年です');
        } else {
            console.log(this.name + 'は成人です');
        }
    }
}

var person = new ChildPerson('太郎', 22);
person.result;

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

JS基礎復習

そろそろJsの復習をまた開始するのでアウトプットです。

JSコードの記述(呼び出し)
htmlファイルのScriptタグの中にコードを記述

qiita.rb
<body>
    <script>
        // この中にJavaScriptのコードを記述する
    </script>
</body>

CSSファイルの読み込み方

qiita.rb
<body>
    <script src="script.js"></script>
</body>

画面上部のアラート表示

qiita.rb
window.alert('こんにちは')
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptで花火を作ってみよう!

花火を作ってみよう

ブラウザでお手軽に花火を作っちゃいましょう!
javaScript(p5.js)だけで作れますので参考にしてみてください。

完成イメージ

fw5.gif

完成品

HTMLファイルを作って以下をコピーしてブラウザで開けば動きます。

firework.html
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>p5* firework</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>

<body>
  <canvas>not work...</canvas>
</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
<script>
  const Y_AXIS = 1;
  const X_AXIS = 2;
  let canvas;
  let fireworks = [];
  let star = [];

  function windowResized() {
    resizeCanvas(document.documentElement.clientWidth, document.documentElement.clientHeight);
    this.preStar();
  }

  function setup() {
    // キャンバスの設定
    canvas = createCanvas(document.documentElement.clientWidth, document.documentElement.clientHeight);
    canvas.position(0, 0);
    canvas.style("z-index", "-1");
    colorMode(RGB);
    frameRate(60);
    this.preStar();
  }

  function draw() {
    // 背景色を設定
    setGradient(0, 0, width, height, color(0, 0, 0), color(24, 32, 72), Y_AXIS);
    noStroke();

    // 星を描く
    this.drawStar();

    // 花火を打ち上げる間隔を調整
    if (0 === frameCount % 100) {
      // 打ち上がるスピード
      let speed = random(10, 30);
      fireworks.push(new FireWork(random(width), height, 0, speed, 0.98));
    }

    for (let fw of fireworks) {
      // 打ち切った花火を処理対象から外す(配列から削除する)
      if (2 === fw.getType || 30000 < fw.getFrame) {
        fireworks = fireworks.filter((n) => n !== fw);
        continue;
      }

      // 打ち上げアニメーションを呼び出す
      fw.fire();
    }
  }

  class FireWork {
    // 初期設定
    constructor(x, y, vx, vy, gv) {
      // フレームカウンター
      this.frame = 0;
      this.type = 0;
      this.next = 0;
      // 花火の色
      this.r = random(155) + 80;
      this.g = random(155) + 80;
      this.b = random(155) + 80;
      this.a = 255;

      // 初期位置
      this.x = x;
      this.y = y;

      // 玉の大きさ
      this.w = random(10, 5);

      // 打ち上がる高さ
      this.maxHeight = random(height / 6, height / 2);
      this.fireHeight = height - this.maxHeight;

      // 重力
      this.vx = vx;
      this.vy = vy;
      this.gv = gv;

      // 残像表示用配列
      this.afterImages = [];
      // 爆発用配列
      this.explosions = [];

      // 消えてから爆発までの遅延時間
      this.exDelay = random(10, 40);
      // 爆発の大きさ
      this.large = random(5, 15);
      // 爆発の玉の数
      this.ball = random(20, 100);
      // 爆発から消えるまでの長さ
      this.exend = random(20, 40);
      // 爆発のブレーキ
      this.exStop = 0.96;
    }

    get getFrame() {
      return this.frame;
    }

    get getType() {
      return this.type;
    }

    // 処理コントロール
    fire() {
      // 0:打ち上げ(初期) 1:爆発
      switch (this.type) {
        case 0:
          this.rising();
          break;
        case 1:
          this.explosion();
          break;
      }
    }

    // 打ち上げアニメーション
    rising() {
      // 頂点まで達したら消す
      if (this.y * 0.8 < this.maxHeight) {
        this.a = this.a - 6;
      }

      // 指定の高さまで上昇する
      this.x += this.vx;
      this.y -= this.vy * ((this.fireHeight - (height - this.y)) / this.fireHeight);

      // 残像を表示
      this.afterImages.push(new Afterimage(this.r, this.g, this.b, this.x, this.y, this.w, this.a));
      for (let ai of this.afterImages) {
        if (ai.getAlpha <= 0) {
          this.afterImages = this.afterImages.filter((n) => n !== ai);
          continue;
        }
        ai.rsImage();
      }

      // 打ち上げ表示
      this.update(this.x, this.y, this.w);

      // 全ての表示が消えたら処理の種類を変更する
      if (0 == this.afterImages.length) {
        if (0 === this.next) {
          // 消えてから爆発まで遅延させる
          this.next = this.frame + Math.round(this.exDelay);
        } else if (this.next === this.frame) {
          // 花火の大きさ
          for (let i = 0; i < this.ball; i++) {
            // 爆発の角度
            let r = random(0, 360);
            // 花火の内側を作る(バラバラ)
            let s = random(0.1, 0.9);
            let vx = Math.cos((r * Math.PI) / 180) * s * this.large;
            let vy = Math.sin((r * Math.PI) / 180) * s * this.large;
            this.explosions.push(new FireWork(this.x, this.y, vx, vy, this.exStop));
            // 花火の輪郭を作る(丸くなるようにする)
            let cr = random(0, 360);
            let cs = random(0.9, 1);
            let cvx = Math.cos((cr * Math.PI) / 180) * cs * this.large;
            let cvy = Math.sin((cr * Math.PI) / 180) * cs * this.large;
            this.explosions.push(new FireWork(this.x, this.y, cvx, cvy, this.exStop));
          }
          this.a = 255;
          this.type = 1;
        }
      }
    }

    // 爆発アニメーション
    explosion() {
      for (let ex of this.explosions) {
        ex.frame++;
        // 爆発し終わった花火を配列から除去する
        if (2 === ex.getType) {
          this.explosions = this.explosions.filter((n) => n !== ex);
          continue;
        }

        // 残像を描画
        if (0 === Math.round(random(0, 32))) {
          ex.afterImages.push(new Afterimage(this.r, this.g, this.b, ex.x, ex.y, ex.w, ex.a));
        }

        for (let ai of ex.afterImages) {
          if (ai.getAlpha < 0) {
            ex.afterImages = ex.afterImages.filter((n) => n !== ai);
            continue;
          }
          ai.exImage();
        }

        // 爆発を描画
        this.update(ex.x, ex.y, ex.w, ex.a);
        ex.x += ex.vx;
        ex.y += ex.vy;
        ex.vx = ex.vx * ex.gv;
        ex.vy = ex.vy * ex.gv;
        ex.vy = ex.vy + ex.gv / 30;
        if (this.exend < ex.frame) {
          ex.w -= 0.1;
          ex.a = ex.a - 4;
          if (ex.a < 0 && 0 === ex.afterImages.length) {
            ex.type = 2;
          }
        }
      }
    }

    // 花火を表示する
    update(x, y, w, a) {
      this.frame++;
      if (0 < this.a) {
        let c = color(this.r, this.g, this.b);
        c.setAlpha(a);
        fill(c);
        ellipse(x, y, w, w);
      }
    }
  }

  // 残像処理用クラス
  class Afterimage {
    constructor(r, g, b, x, y, w, a) {
      this.frame = 0;
      this.r = r;
      this.g = g;
      this.b = b;
      this.x = x;
      this.y = y;
      this.w = w;
      this.a = a;
      this.vx = random(-0.24, 0.24);
      this.vy = random(0.2, 0.8);
      this.vw = random(0.05, 0.2);
    }

    get getAlpha() {
      return this.a;
    }

    // 打ち上げ用
    rsImage() {
      if (0 < this.a) {
        this.update(this.r, this.g, this.b, this.x, this.y, this.w, this.a);
        this.r += 4;
        this.g += 4;
        this.b += 4;
        this.x = this.x + this.vx;
        this.y = this.y + this.vy;
        if (0 < this.w) {
          this.w = this.w - this.vw;
        }
        this.a = this.a - 4;
      }
    }

    // 爆発用
    exImage() {
      if (0 < this.a) {
        this.update(this.r, this.g, this.b, this.x, this.y, this.w, this.a);
        this.r += 2.5;
        this.g += 2.5;
        this.b += 2.5;
        this.x = this.x + this.vx;
        this.y = this.y + this.vy;
        if (0 < this.w) {
          this.w = this.w - this.vw;
        }
        this.a = this.a - 1.5;
      }
    }

    update(r, g, b, x, y, w, a) {
      this.frame++;
      let c = color(r, g, b);
      c.setAlpha(a);
      fill(c);
      ellipse(x, y, w, w);
    }
  }

  // グラデーションを描画
  function setGradient(x, y, w, h, c1, c2, axis) {
    noFill();

    if (axis === Y_AXIS) {
      // Top to bottom gradient
      for (let i = y; i <= y + h; i++) {
        let inter = map(i, y, y + h, 0, 1);
        let c = lerpColor(c1, c2, inter);
        stroke(c);
        line(x, i, x + w, i);
      }
    } else if (axis === X_AXIS) {
      // Left to right gradient
      for (let i = x; i <= x + w; i++) {
        let inter = map(i, x, x + w, 0, 1);
        let c = lerpColor(c1, c2, inter);
        stroke(c);
        line(i, y, i, y + h);
      }
    }
  }

  // 星を作成
  function preStar() {
    star = [];
    for (let i = 0; i < 100; i++) {
      star.push([random(width), random(height / 2), random(1, 4)]);
    }
  }

  // 星を描画
  function drawStar() {
    // 星を描く
    for (let s of star) {
      let c = color(random(150, 255), random(150, 255), 255);
      c.setAlpha(random(150, 200));
      fill(c);
      ellipse(s[0], s[1], s[2], s[2]);
    }
  }

</script>

</html>

GitHubリポジトリはこちら

https://github.com/soramoyou04/p5js-animation-shelf

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

Javascriptで花火を作ってみよう!

花火を作ってみよう

ブラウザでお手軽に花火を作っちゃいましょう!
javaScript(p5.js)だけで作れますので参考にしてみてください。

完成イメージ

fw5.gif

完成品

HTMLファイルを作って以下をコピーしてブラウザで開けば動きます。

firework.html
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>p5* firework</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>

<body>
  <canvas>not work...</canvas>
</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
<script>
  const Y_AXIS = 1;
  const X_AXIS = 2;
  let canvas;
  let fireworks = [];
  let star = [];

  function windowResized() {
    resizeCanvas(document.documentElement.clientWidth, document.documentElement.clientHeight);
    this.preStar();
  }

  function setup() {
    // キャンバスの設定
    canvas = createCanvas(document.documentElement.clientWidth, document.documentElement.clientHeight);
    canvas.position(0, 0);
    canvas.style("z-index", "-1");
    colorMode(RGB);
    frameRate(60);
    this.preStar();
  }

  function draw() {
    // 背景色を設定
    setGradient(0, 0, width, height, color(0, 0, 0), color(24, 32, 72), Y_AXIS);
    noStroke();

    // 星を描く
    this.drawStar();

    // 花火を打ち上げる間隔を調整
    if (0 === frameCount % 100) {
      // 打ち上がるスピード
      let speed = random(10, 30);
      fireworks.push(new FireWork(random(width), height, 0, speed, 0.98));
    }

    for (let fw of fireworks) {
      // 打ち切った花火を処理対象から外す(配列から削除する)
      if (2 === fw.getType || 30000 < fw.getFrame) {
        fireworks = fireworks.filter((n) => n !== fw);
        continue;
      }

      // 打ち上げアニメーションを呼び出す
      fw.fire();
    }
  }

  class FireWork {
    // 初期設定
    constructor(x, y, vx, vy, gv) {
      // フレームカウンター
      this.frame = 0;
      this.type = 0;
      this.next = 0;
      // 花火の色
      this.r = random(155) + 80;
      this.g = random(155) + 80;
      this.b = random(155) + 80;
      this.a = 255;

      // 初期位置
      this.x = x;
      this.y = y;

      // 玉の大きさ
      this.w = random(10, 5);

      // 打ち上がる高さ
      this.maxHeight = random(height / 6, height / 2);
      this.fireHeight = height - this.maxHeight;

      // 重力
      this.vx = vx;
      this.vy = vy;
      this.gv = gv;

      // 残像表示用配列
      this.afterImages = [];
      // 爆発用配列
      this.explosions = [];

      // 消えてから爆発までの遅延時間
      this.exDelay = random(10, 40);
      // 爆発の大きさ
      this.large = random(5, 15);
      // 爆発の玉の数
      this.ball = random(20, 100);
      // 爆発から消えるまでの長さ
      this.exend = random(20, 40);
      // 爆発のブレーキ
      this.exStop = 0.96;
    }

    get getFrame() {
      return this.frame;
    }

    get getType() {
      return this.type;
    }

    // 処理コントロール
    fire() {
      // 0:打ち上げ(初期) 1:爆発
      switch (this.type) {
        case 0:
          this.rising();
          break;
        case 1:
          this.explosion();
          break;
      }
    }

    // 打ち上げアニメーション
    rising() {
      // 頂点まで達したら消す
      if (this.y * 0.8 < this.maxHeight) {
        this.a = this.a - 6;
      }

      // 指定の高さまで上昇する
      this.x += this.vx;
      this.y -= this.vy * ((this.fireHeight - (height - this.y)) / this.fireHeight);

      // 残像を表示
      this.afterImages.push(new Afterimage(this.r, this.g, this.b, this.x, this.y, this.w, this.a));
      for (let ai of this.afterImages) {
        if (ai.getAlpha <= 0) {
          this.afterImages = this.afterImages.filter((n) => n !== ai);
          continue;
        }
        ai.rsImage();
      }

      // 打ち上げ表示
      this.update(this.x, this.y, this.w);

      // 全ての表示が消えたら処理の種類を変更する
      if (0 == this.afterImages.length) {
        if (0 === this.next) {
          // 消えてから爆発まで遅延させる
          this.next = this.frame + Math.round(this.exDelay);
        } else if (this.next === this.frame) {
          // 花火の大きさ
          for (let i = 0; i < this.ball; i++) {
            // 爆発の角度
            let r = random(0, 360);
            // 花火の内側を作る(バラバラ)
            let s = random(0.1, 0.9);
            let vx = Math.cos((r * Math.PI) / 180) * s * this.large;
            let vy = Math.sin((r * Math.PI) / 180) * s * this.large;
            this.explosions.push(new FireWork(this.x, this.y, vx, vy, this.exStop));
            // 花火の輪郭を作る(丸くなるようにする)
            let cr = random(0, 360);
            let cs = random(0.9, 1);
            let cvx = Math.cos((cr * Math.PI) / 180) * cs * this.large;
            let cvy = Math.sin((cr * Math.PI) / 180) * cs * this.large;
            this.explosions.push(new FireWork(this.x, this.y, cvx, cvy, this.exStop));
          }
          this.a = 255;
          this.type = 1;
        }
      }
    }

    // 爆発アニメーション
    explosion() {
      for (let ex of this.explosions) {
        ex.frame++;
        // 爆発し終わった花火を配列から除去する
        if (2 === ex.getType) {
          this.explosions = this.explosions.filter((n) => n !== ex);
          continue;
        }

        // 残像を描画
        if (0 === Math.round(random(0, 32))) {
          ex.afterImages.push(new Afterimage(this.r, this.g, this.b, ex.x, ex.y, ex.w, ex.a));
        }

        for (let ai of ex.afterImages) {
          if (ai.getAlpha < 0) {
            ex.afterImages = ex.afterImages.filter((n) => n !== ai);
            continue;
          }
          ai.exImage();
        }

        // 爆発を描画
        this.update(ex.x, ex.y, ex.w, ex.a);
        ex.x += ex.vx;
        ex.y += ex.vy;
        ex.vx = ex.vx * ex.gv;
        ex.vy = ex.vy * ex.gv;
        ex.vy = ex.vy + ex.gv / 30;
        if (this.exend < ex.frame) {
          ex.w -= 0.1;
          ex.a = ex.a - 4;
          if (ex.a < 0 && 0 === ex.afterImages.length) {
            ex.type = 2;
          }
        }
      }
    }

    // 花火を表示する
    update(x, y, w, a) {
      this.frame++;
      if (0 < this.a) {
        let c = color(this.r, this.g, this.b);
        c.setAlpha(a);
        fill(c);
        ellipse(x, y, w, w);
      }
    }
  }

  // 残像処理用クラス
  class Afterimage {
    constructor(r, g, b, x, y, w, a) {
      this.frame = 0;
      this.r = r;
      this.g = g;
      this.b = b;
      this.x = x;
      this.y = y;
      this.w = w;
      this.a = a;
      this.vx = random(-0.24, 0.24);
      this.vy = random(0.2, 0.8);
      this.vw = random(0.05, 0.2);
    }

    get getAlpha() {
      return this.a;
    }

    // 打ち上げ用
    rsImage() {
      if (0 < this.a) {
        this.update(this.r, this.g, this.b, this.x, this.y, this.w, this.a);
        this.r += 4;
        this.g += 4;
        this.b += 4;
        this.x = this.x + this.vx;
        this.y = this.y + this.vy;
        if (0 < this.w) {
          this.w = this.w - this.vw;
        }
        this.a = this.a - 4;
      }
    }

    // 爆発用
    exImage() {
      if (0 < this.a) {
        this.update(this.r, this.g, this.b, this.x, this.y, this.w, this.a);
        this.r += 2.5;
        this.g += 2.5;
        this.b += 2.5;
        this.x = this.x + this.vx;
        this.y = this.y + this.vy;
        if (0 < this.w) {
          this.w = this.w - this.vw;
        }
        this.a = this.a - 1.5;
      }
    }

    update(r, g, b, x, y, w, a) {
      this.frame++;
      let c = color(r, g, b);
      c.setAlpha(a);
      fill(c);
      ellipse(x, y, w, w);
    }
  }

  // グラデーションを描画
  function setGradient(x, y, w, h, c1, c2, axis) {
    noFill();

    if (axis === Y_AXIS) {
      // Top to bottom gradient
      for (let i = y; i <= y + h; i++) {
        let inter = map(i, y, y + h, 0, 1);
        let c = lerpColor(c1, c2, inter);
        stroke(c);
        line(x, i, x + w, i);
      }
    } else if (axis === X_AXIS) {
      // Left to right gradient
      for (let i = x; i <= x + w; i++) {
        let inter = map(i, x, x + w, 0, 1);
        let c = lerpColor(c1, c2, inter);
        stroke(c);
        line(i, y, i, y + h);
      }
    }
  }

  // 星を作成
  function preStar() {
    star = [];
    for (let i = 0; i < 100; i++) {
      star.push([random(width), random(height / 2), random(1, 4)]);
    }
  }

  // 星を描画
  function drawStar() {
    // 星を描く
    for (let s of star) {
      let c = color(random(150, 255), random(150, 255), 255);
      c.setAlpha(random(150, 200));
      fill(c);
      ellipse(s[0], s[1], s[2], s[2]);
    }
  }

</script>

</html>

GitHubリポジトリはこちら

https://github.com/soramoyou04/p5js-animation-shelf

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

nvmをインストールする手順

概要

最新のgithubレポジトリのREAD.meを手順を参照してその通り実行すればよい。

インストール

インストーラーを実行

インストーラーをダウンロードして実行する。

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash

実行時にbash設定ファイルにnvm用の環境変数の設定処理などが自動で書き込まれるので、それが反映されるよう一度ターミナルを開き直す。
開き直したターミナルでnvmと打って、コマンドの説明文とかが表示されればok。

nodeをインストール

自動で最新版を入れる

何も考えず最新版をインストールする場合は以下を実行。

nvm install node 

バージョンを指定して入れる

nvm install 12.16.1

インストール可能なバージョン一覧を確認する

nvm ls-remote

現在インストール済のnodeのバージョンを確認

nvm ls

現在使用中のnodeのバージョンを確認

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

Vue.js を一から学んでみた。

Nuxt.jsを使ってみたい!

いきなりタイトルとは違うところを書きましたが、Nuxt.jsを使うためにはVue.jsの知識が必要

Vue.jsから一から学んでいく時にメモ+まとめ的につらつらと書いていきます。

誤字や誤認識もあるかと思いますが、その際はご指摘をいただけると非常に助かります:bow::bow::bow:

準備

簡単に打鍵して試したい時は、jsfiddleがオススメです。
image.png
HTMLとJS簡単に試せます。

詳しい使い方として、こちらのサイトを参考にさせていただきました。
簡単にコードを実装してシェアできる!超便利なjsFiddleの使い方

ちなみに開発時はVSCodeを使ってます。便利なプラグインも多くて助かります。
サーバーサイドも一緒に開発する時にはIntellJです(Kotlin&SpringFramework)。

1.概要

まずは本家のサイト を参考に。

ざっとみて、以下の点か重要かなと。

  • テンプレート構文を使って、MVVMしてる
  • ディレクティブを使って、DOMにリアクティブ可能。
    • タグの属性に処理を設定できる
    • タグ自体の分岐やループ等の処理もできる
    • イベントハンドラの設定もできる
  • コンポーネントシステムを使って、再利用可能な小さい部品を作って使いまわせそう
    • アトミックデザインに適した実装が可能かな?
    • でかいシステムでも、設計をちゃんとすれば複数人数で開発できそう

2.Vue定義

Vueインスタンス

Vueにはフィールドとメソッドを定義可能。

  • フィールド:data
  • メソッド:methods

Vueインスタンスの作成方法は簡単。ViewModelにあやかってvmという変数名をつけることが多いみたい。

var vm = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

Vueインスタンスのライフサイクル

以下が、Vueインスタンスのライフサイクル。

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyted

Vueインスタンスのライフサイクルをトリガーにフックしたくなる時は以下を参考に。

new Vue({
  data: {
    a: 1
  },
  // createdのフック
  created: function () {
    console.log('a is: ' + this.a)
  }
})

image.png

3.テンプレート構文

もっとも基本的なデータバインディングは、二重中括弧を利用したもの。
この「二重中括弧」、”Mustache(むすたっしゅ)” 構文という。初めて知ったけどそれは隠そうと思う。

<!--Vueインスタンスのmsgフィールドの情報をバインディングする。 -->
<span>Message: {{ msg }}</span>

JSでmsgフィールドの内容が変更されたら、上記の内容も変更されます(双方向バインディング)。

ただし、一度表示したらmsgフィールドの内容がかわろうとも、変更されないような書き方もあります。
v-onceディレクティブを使います。

<span v-once>This will never change: {{ msg }}</span>

このように、痒いところに届きそうなものから、すげー使うやつまでディレクティブの種類が多すぎそうなので主要なものと言われるのをまとめてみます。

4.ディレクティブ

特徴としては、以下かと。

  • v- から始まる特別な属性
  • 属性値は、単一の JavaScript 式を期待する(例外はv-for
  • 属性値の式が変更された時に、DOMを更新する
  • 省略記法あり。
    • 例:<a v-bind:href="url"> ... </a><a :href="url"> ... </a>

v-のディレクティブについて捉えることが必要そうなので、まとめようと思います。

※次の記事にもまとまっており、参考にもさせていただきました。
体で覚えるVue.js - ディレクティブ編 〜 JSおくのほそ道 #023

※以下、随時追加。

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

Vue.js を1から学んでみた。

Nuxt.jsを使ってみたい!

いきなりタイトルとは違うところを書きましたが、Nuxt.jsを使うためにはVue.jsの知識が必要

Vue.jsから一から学んでいく時にメモ+まとめ的につらつらと書いていきます。

誤字や誤認識もあるかと思いますが、その際はご指摘をいただけると非常に助かります:bow::bow::bow:

準備

簡単に打鍵して試したい時は、jsfiddleがオススメです。
image.png
HTMLとJS簡単に試せます。

詳しい使い方として、こちらのサイトを参考にさせていただきました。
簡単にコードを実装してシェアできる!超便利なjsFiddleの使い方

ちなみに開発時はVSCodeを使ってます。便利なプラグインも多くて助かります。
サーバーサイドも一緒に開発する時にはIntellJです(Kotlin&SpringFramework)。

1.概要

まずは本家のサイト を参考に。

ざっとみて、以下の点か重要かなと。

  • テンプレート構文を使って、MVVMしてる
  • ディレクティブを使って、DOMにリアクティブ可能。
    • タグの属性に処理を設定できる
    • タグ自体の分岐やループ等の処理もできる
    • イベントハンドラの設定もできる
  • コンポーネントシステムを使って、再利用可能な小さい部品を作って使いまわせそう
    • アトミックデザインに適した実装が可能かな?
    • でかいシステムでも、設計をちゃんとすれば複数人数で開発できそう

2.Vue定義

Vueインスタンス

Vueにはフィールドとメソッドを定義可能。

  • フィールド:data
  • メソッド:methods

Vueインスタンスの作成方法は簡単。ViewModelにあやかってvmという変数名をつけることが多いみたい。

sample0-1.js
var vm = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

Vueインスタンスのライフサイクル

以下が、Vueインスタンスのライフサイクル。

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyted

Vueインスタンスのライフサイクルをトリガーにフックしたくなる時は以下を参考に。

sample0-2.js
new Vue({
  data: {
    a: 1
  },
  // createdのフック
  created: function () {
    console.log('a is: ' + this.a)
  }
})

image.png

3.テンプレート構文

もっとも基本的なデータバインディングは、二重中括弧を利用したもの。
この「二重中括弧」、”Mustache(むすたっしゅ)” 構文という。初めて知ったけどそれは隠そうと思う。

<!--Vueインスタンスのmsgフィールドの情報をバインディングする。 -->
<span>Message: {{ msg }}</span>

JSでmsgフィールドの内容が変更されたら、上記の内容も変更されます(双方向バインディング)。

ただし、一度表示したらmsgフィールドの内容がかわろうとも、変更されないような書き方もあります。
v-onceディレクティブを使います。

<span v-once>This will never change: {{ msg }}</span>

このように、痒いところに届きそうなものから、すげー使うやつまでディレクティブの種類が多すぎそうなので主要なものと言われるのをまとめてみます。

4.ディレクティブ

特徴としては、以下かと。

  • v- から始まる特別な属性
  • 属性値は、単一の JavaScript 式を期待する(例外はv-for
  • 属性値の式が変更された時に、DOMを更新する
  • 省略記法あり。
    • 例:<a v-bind:href="url"> ... </a><a :href="url"> ... </a>

v-のディレクティブについて捉えることが必要そうなので、まとめようと思います。

※次の記事にもまとまっており、参考にもさせていただきました。
体で覚えるVue.js - ディレクティブ編 〜 JSおくのほそ道 #023

v-text

  • DOMの内側に展開。
  • Mustache構文({{}}で囲んだコード)と一緒。
  • 省略系→

サンプルコード

sample1.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-1">
  <!-- フィールド展開の例 -->
  <p v-text="message"></p>
  <p>{{ msg }}</p>

  <!-- メソッド展開の例 -->
  <p v-text="showMessage()"></p>
  <p>{{ showMsg() }}</p>
</div>
sample1.js
var vm = new Vue({
  el: '#app-1',
  data: {
    message: 'Hello Vue.js!',
    msg: 'サンキュー'
  },
  methods: {
    showMessage: function () {
      return "はろー、Vue.js"
    },
    showMsg: function () {
      return "Thank you!!"
    }
  }
})

結果
image.png

v-once

  • 1度DOM展開したら、そのあとは変更させない。
  • なんかの処理で、バインディングしたデータが変更されても、描画を変更させたくないときに有効。
    • 個人的にはこれを使うのは最終手段かなと。感覚的に。
  • 省略系→

サンプル(v-onceを使わない)

sample2-1.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-2">
  <p v-text="message"></p>
  <p v-text="showMessage()"></p>
</div>
sample2.js
var vm = new Vue({
  el: '#app-2',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    showMessage: function () {
      this.message = 'hoge';
      return "はろー、Vue.js"
    }
  }
})

結果(「Hello Vue.js!」がshowMessage()で書き換えられて表示される)
image.png

サンプル(v-onceを使う・JavaScriptは変更しないので割愛)

sample2-2.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-2">
  <!-- v-onceを追加 -->
  <p v-once v-text="message"></p>
  <p v-text="showMessage()"></p>
</div>

結果(「Hello Vue.js!」が表示される)
image.png

v-html

  • DOMの内側に展開。
    • v-textは「文字列」で展開する
    • v-htmlは「HTML」で展開する
  • HTMLをそのまま展開されるので、XSSに注意して使うことが必要。
  • 省略系→

サンプルコード:h1タグを表示させたいときー

sample3.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-3">
  <p v-text="message"></p>
  <p v-html="message"></p>
</div>
sample3.js
var vm = new Vue({
  el: '#app-3',
  data: {
    message: '<h1>タイトルです<h1>'
  }
})

結果
image.png

v-bind(その1:属性値)

  • htmlタグの属性に埋め込む時に使用。
  • 引数が必要なディレクティブ。引数は:の後ろに設定するv-bind:href="url
  • 使う頻度がスーパー高そう。
  • 省略系→:href='url'のように、コロンだけでOK。

サンプルコード:aタグのリンク先を設定したいときー

sample4.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-4">
  <!-- :(コロン)の後に引数を設定する-->
  <a v-bind:href="url">モノタス</a>


  <!-- 省略系
  <a :href="url">モノタス</a>
  -->
</div>
sample4.js
var vm = new Vue({
  el: '#app-4',
  data: {
    url: 'https://www.monotas.net/'
  }
})

結果
aaa.gif

v-bind(その2:属性)

  • htmlタグそのものを定義することも可能
  • 属性を[]で囲うことで指定可能。例:<a :[attr]="hogehoge">
  • つまり、v-bindだけで、各種タグに属性とそのvalueのバインディングができる。
  • これも使う頻度がスーパー高そう。
sample5.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-5">
  <a :href="monotas_url">モノタス</a>
  <br>
  <a :[attr]="jta_url">日本テニス協会</a>
</div>
sample5.js
var vm = new Vue({
  el: '#app-5',
  data: {
    attr: 'href',
    monotas_url: 'https://www.monotas.net/',
    jta_url: 'https://www.jta-tennis.or.jp/'
  }
})

結果
aaa2.gif

v-bind(その3:オブジェクト化)

  • 1つのタグに複数のv-bindを指定することもできるが、一つのオブジェクトとして記載することも可能。
  • Vueフィールドにオブジェクトを用意して、それをv-bindで指定することも可能。
  • 見た目すっきり系でもあるし、DOMの操作しやすくなりそうな感覚。まだふんわりとしかわかってない。
sample6.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-6">
  <!-- 複数指定できる -->
  <a :id="monotas_id" :href="monotas_url">モノタス(省略系でそれぞれ指定)</a>
  <br>
  <!-- 一つのオブジェクトでも書ける -->
  <a v-bind="{id: monotas_id, href: monotas_url}">モノタス(オブジェクトを直接指定)</a>
  <br>
  <!-- VueインスタンスフィールドのオブジェクトもOK -->
  <a v-bind="monotas">モノタス(Vueインスタンスのオブジェクトを指定)</a>
  <br>
</div>
sample6.js
var vm = new Vue({
  el: '#app-6',
  data: {
    attr: 'href',
    monotas_id: 2,
    monotas_url: 'https://www.monotas.net/',
    monotas: {
        id: 2,
      href: 'https://www.monotas.net/'
    }
  }
})

結果:全て同じIDとurlが設定されている
image.png
image.png

v-on

  • クリック等のDOMが提供しているイベントのリスナー。
  • 引数が必要なディレクティブ。引数は: の後ろに設定するv-on:click="change()"
    • clickはDOMが提供しているイベント
    • change()が Vueで定義したメソッド。
  • 使う頻度は超スーパー高そう。
  • 省略系→

サンプルコード:ボタンが押されたら文字列を変更します的なときー

sample7.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-7">
  <button v-on:click="change()">クリックすると文字が変わります</button>
  <p>{{ first_str }}</p>
</div>
sample7.js
var vm = new Vue({
  el: '#app-7',
  data: {
    first_str: '最初に表示されています',
    clicked_str: 'ボタンが押されました'
  },
    methods:{
      change: function() {
        this.first_str = this.clicked_str
      }
    }
})

結果
aaa3.gif

※以下、随時追加。

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

ブラウザのconsoleからは要素を取得できるのに、scriptから取得するとnullになるときは、iframeの要素かもしれないというお話

はじめに

簡単なスクレイピングツールを書いていたら、不可解な現象に遭遇してハマってしまいました。
同じような現象に遭遇した人が無為な時間を過ごさないように共有します。

スクレイピングツールを書くときのお話

「この部分の内容を取得したい!」と思ったら、該当部分を右クリックして「要素の詳細を表示」(Safariの場合)からHTMLを眺めて、要素のattributeなどをチェックします。

そして、こうやれば取得できるかなというコマンドを、試しにconsoleに入力してみます。

document.querySelector('a[href^="/start_with"]) // => <a href="/start_with/12345">...</a>

無事に結果が返ってきました!

通常はこのような手順でうまくいったコマンドをスクリプトにまとめていくと思います。ところが……

スクリプトを実行するとnullが返ってくる

???

あれ?コピペしたのにおかしいな。このコマンドにたどり着くまでのページ遷移がおかしいのかな?いや、そもそも……という感じで1時間くらい哀しい時間を過ごしました。

iframeの中の要素だった

色々試すうちに気がつきました。あれ?取得しようとしている要素がiframeの中にあるぞ。
ページ上から要素の詳細を表示すると、対象の要素が見える状態まで展開されるのでなかなか気がつかなかったのです。

iframeの中の要素を取得する

というわけで結論です。
以下のように、iframe.contentDocumentから検索することでiframe内の要素を取得できます。

const iframe = document.querySelector('#iframe-selector')
// iframe.contentWindow.document === iframe.contentDocument
const targetElement = iframe.contentDocument.querySelector('a[href^="/start_with"]')

 // XPathで取得するときは、2つめの引数にiframe.contentDocumentを渡せばOK
const targetElementByXPath = document.evaluate(
  '//a[contains(@href, "/start_with")]',
  iframe.contentDocument,
  null,
  XPathResult.FIRST_ORDERED_NODE_TYPE,
  null
).singleNodeValue

どうやら、Webインスペクタやデベロッパーツールからiframe内の要素を手動で表示した時点で、iframe内の要素が、通常のdocument内に展開されるようです。そのため、ブラウザのconsoleからは取得できてしまっていたのでした……(かなしみ)

参考

javascriptでiframe内のDOM要素を取得したり操作する

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