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

toJSONを用いて、JSONデータの返り値を制御する

JSONデータの返り値を制御する

例)petというJSONデータの場合

const pet = {
   name: 'Doggy'
}

console.log(JSON.stringify(pet))


{'name':'Doggy'}
というJSONデータが返ってくる。

★返り値となるJSONデータを、toJSONを使って制御することができる

const pet = {
   name: 'Doggy'
}

pet.toJSON = function(){
  return {}
}

console.log(JSON.stringify(pet))


{}
というJSONデータが返ってくる。

つまり

toJSONで上書きされた値が、pet定数のJSONデータとして認識される

データベースのセキュリティ向上に利用

//mongooseを利用して、データベース型を設定
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
    name: {
      type: String,  //データ型の設定
      required: true,
      trim: true
  },
  password:{
      type: String,
      required: true,
  },
  tokens: [{
    token: {
      type: String,
      require: true
    }
  }],
 })

//toJSONを用いて、外からuserObjectにアクセスした時に、返すメソッドと返さないメソッドを決める
userSchema.methods.toJSON = function(){
  const user = this
  const userObject = user.toObject()

  delete userObject.password
  delete userObject.tokens


  return userObject
}


これで、userObjectは、toJSONで指定されている、passwordとtoken以外のデータを返すようになる。

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

【SNS顔出しNGの方も安心】顔に目隠しを付けてくれるアプリを機械学習APIで作ったよ

作ったもの

SNS顔出しNGの方のために、
顔に目隠しを付けてくれるアプリを作りました。

ウェブアプリなので、PC、スマホの機種は問いません。
こちらのリンクからどうぞ。
https://hardcore-ritchie-cca31e.netlify.app/

使用例

写真をアップロードすると、いい感じに目隠しを付けてくれます。
116153353_3186371734744672_7900174768791699760_o.jpg

複数人でも大丈夫。
(普通の飲み会が、なにやら悪巧みしてそうな会合に変貌)
※コロナ禍より前の飲み会です。
116465837_3189274921121020_7456995256992579631_n.jpg

秘密のデートも目隠しを入れれば、誰にもバレません。
これで、プライバシーもバッチリ!
(写真はフリー写真サイト「ぱくたそ」 さんより)

スクリーンショット 2020-08-04 21.42.32.png

これで、みなさんも安心してSNSに投稿できますね。

番外編

なんと、私の3Dモデル(リアルアバター)にも反応しました。
つまり、私のアバターは人間と見分けがつかない?
ちなみに、このアバターは浅草にあるリアルアバターさんで作ってもらいました。
スクリーンショット 2020-08-04 21.49.02.png

猫はダメでした。
「人間じゃないよ」ってアラートを出します。
IMG_7166.jpg

使用技術

言語:

Javascript

CSSフレームワーク

bootstrap
レスポンシブデザインを気持ち程度に使用

ライブラリ

face-api.js について

顔認識の機械学習モデルが使えるAPIです。

tinyFaceDetector というモデルで顔を検出し、
faceLandmark68Net というモデルで顔のランドマークを検出します。
この写真のように68個の顔のポイント1つ1つの画像上での座標が取得できます。

参考サイト

以下のサイトに大変お世話になりました。
ありがとうございました。

コード

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- Bootstrap -->
    <!-- CSS only -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous" />
    <!-- JS, Popper.js, and jQuery -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous" ></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous" ></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous" ></script>
    <style>
      .container {
      padding-top: 50px;
      }
    </style>

    <!-- face-api.jsの読み込み -->
    <script src="face-api.js"></script>

    <!-- humaneライブラリの読み込み -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/humane-js/3.2.2/themes/boldlight.css"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/humane-js/3.2.2/humane.min.js"></script>

  </head>
  <body>
    <div class="container">
      <div class="upload"><input type="file" name="file" id="file"></div>
      <div id="result"></div>
      <canvas id="canvas"></canvas>
    </div>
  </body>

  <script>
    const MODEL_URL = 'models/'
    let file = document.getElementById('file')
    let canvas = document.getElementById('canvas')
    let canvasWidth = 350
    let canvasHeight = 350
    let uploadImgSrc

    //ファイルをアップロードしてCanvas上に表示するのは、ほぼこのサイトどおり
    //https://www.tam-tam.co.jp/tipsnote/javascript/post13538.html
    // Canvasの準備
    canvas.width = canvasWidth
    canvas.height = canvasHeight
    var ctx = canvas.getContext('2d')

    function loadLocalImage(e) {
      // ファイル情報を取得
      let fileData = e.target.files[0]
      console.log("filedata",fileData)
      // 画像ファイル以外は処理を止める
      if(!fileData.type.match('image.*')) {
        humane.log("画像ファイルでお願いします")
      }
      // FileReaderオブジェクトを使ってファイル読み込み
      let reader = new FileReader()
      // ファイル読み込みに成功したときの処理
      reader.onload = function() {
        // Canvas上に表示する
        uploadImgSrc = reader.result;
        canvasDraw(fileData)
      }
      // ファイル読み込みを実行
      reader.readAsDataURL(fileData)
    }
    // ファイルが指定された時にloadLocalImage()を実行
    file.addEventListener('change', loadLocalImage, false)
    // Canvas上に画像を表示する
    function canvasDraw() {
      // canvas内の要素をクリアする
      ctx.clearRect(0, 0, canvasWidth, canvasHeight)
      // Canvas上に画像を表示
      let img = new Image()
      img.src = uploadImgSrc
      img.onload = function() {
      ctx.drawImage(img, 0, 0, canvasWidth, this.height * (canvasWidth / this.width))
      let canvasRate = canvasWidth / this.width

     //顔の情報を取得
      getFaceData(img,canvasRate)  
  }
}

async function getFaceData(img,canvasRate) {
  await faceapi.nets.tinyFaceDetector.load('models/') //モデル読み込み
  await faceapi.nets.faceLandmark68Net.load("models/") //モデル読み込み
  // 顔検出の実行
  const detectionsWithLandmarks = await faceapi.detectAllFaces(img,
        new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks()
  if (detectionsWithLandmarks.length == 0){
    humane.log('人間じゃないよ')
  }else{
    for (let n = 0; n < detectionsWithLandmarks.length ; n++ ) {
      let x0 = detectionsWithLandmarks[n].landmarks._positions[0].x * canvasRate
      let y0 = detectionsWithLandmarks[n].landmarks._positions[37].y * canvasRate
      let x1 = detectionsWithLandmarks[n].landmarks._positions[16].x * canvasRate
      let y1 = detectionsWithLandmarks[n].landmarks._positions[46].y * canvasRate

      let line_thickness =  (x1-x0) * .2 //線の太さを長さの20%とした

      drawLine(x0,y0,x1,y1,line_thickness) //目隠し線を引く関数
    }
  }
}

//目隠し線を引く
function drawLine(x0,y0,x1,y1,line_thickness){ 
  ctx.strokeStyle = '#000000'
  ctx.lineWidth = line_thickness
  ctx.beginPath()
  ctx.moveTo(x0, y0) 
  ctx.lineTo(x1,y1)
  ctx.closePath()
  ctx.stroke()
}

</script>

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

bcryptでプレーンテキストをハッシュ化させる

ハッシュ化のメリット

・パスワードなどをデータベースに保存する時、プレーンテキストのまま保存すると脆弱性に繋がる。

・ハッシュ化したメッセージダイジェストから、元のメッセージを復元することは困難
→「インクリプション」…元のメッセージをとっておいて、それをハッシュ化したら同じハッシュ値になるかチェックすることで、同一性を確認する仕組み

bcryptパッケージ

bcryptというnpmパッケージを利用すると、プレーンテキストをハッシュ化することができる。

・bcryptパッケージの使い方
const bcrypt = require('bcryptjs')

const myFunction = async() => {
const password = "password1234"

const hadhedPassword = await bcrypt.hash(password, 8) //第一引数:ハッシュ化したい値、第二引数:roundをかける回数。公式推奨は8回

console.log(password) //元のパスワードを表示
console.log(hassedPassword) //ハッシュ化されたパスワードを表示

//元のパスワードとハッシュ化されたパスワードを比較し、同じものであるか確認

const isMarch = await bcrypt.compare('password', hashedPassword)

}

myFunction()

値をハッシュ化させてからデータベースに組み込む

●Middlewareを利用してMongooseの振る舞い方をカスタマイズできる

Mongoose公式ドキュメント
「Middleware > Save/Validate」の項目
スクリーンショット 2020-08-04 22.35.59.png

・validateする前、または後にイベントを設定できる。
・saveする前、または後にイベントを設定できる。

→save()する前に、データベースに保存したい値をハッシュ化させることができる。

●例)
データベースに格納する前にpasswordをハッシュ化

const mongoose = require('mongoose')
const bcrypt = require('bcryptjs') 

const userSchema = new mongoose.Schema({
    name: {
      type: String,  //データ型の設定
      required: true,
      trim: true
  },
  password:{
      type: String,
      required: true,
      }
  })

//middlewareを記述。saveする前にプレーンテキストをハッシュする
userSchema.pre('save', async function(next){ //アローファンクションは使わない。このbindingは大事な役割を果たすものだから
  const user = this

  console.log('Middleware is working')//ミドルウェアが動いているか確認するためのテストコード

  if(user.isModified('password')){ //ハッシュ化されていないパスワードがある時
    user.password = await bcrypt.hash(user.password, 8)
  }//ここで値がハッシュされる

  next()
})

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

aibo Web API と YouTube Player API を連携して、YouTube 動画の再生秒数に合わせて aibo にふるまいをしてもらう

aibo 界隈ではオフ会というものが定期的に開催されています。

内容は様々ですが、大体は aibo を室内で自由にお散歩させつつ
オーナーは aibo たちを見てニヤニヤしながらお話したり
aibo の名刺や情報を交換したりして交流を深める会です。

最近はコロナの影響あって、開催するのもなかなか難しい状況なってしまいましたが。。

とあるオフ会で aibo をアイドルに見立ててダンスをしてもらおうという企画がありました。
当然のことながら、aibo はアイドルのように自ら曲に合わせてダンスするということができません。

なのでどうしたかというと、オーナー皆さんの力を合わせて aibo の配置をシーン毎で変え
企画主の方に写真をとっていただいてダンスしてる風の動画を作っていただきました。

DSC01752.JPG

その時 aibo にダンスの振り付けができたらそれはもう素敵なのになぁと思ったわけです。
その後 aibo Web API が 2019/11/11 に実装され可能性が広がりました。

前回の「aibo Events API を使って aibo に音声コマンドを実行してもらう」では PHP を使いましたが、今回は HTML + JavaScript で実装します。

完成図

まずは完成図から。
イントロが終わったら、ルンルンし始めるおチャコさんです。

↓↓ クリックしたら動画が開きます。
ルンルンし始めるおチャコさん

事前準備

素材となる YouTube 動画をあらかじめ探しておきます。
今回はこちらを使用しています。

↓↓ クリックしたら動画が開きます。
となりのトトロのさんぽ

となりのトトロのさんぽですね。
aibo がこの曲を聞いて、ノリノリでダンスをやり始めたらかわいいじゃないですか。

アクセストークン取得

ディベロッパーサイトにアクセスして、サインインします。

ss06.png

ボタン「生成する」をクリックすると、アクセストークンが生成されます。
アクセストークンは「実装」で必要なので、あらかじめコピーしておきましょう。

実装

今回は aibo Web APIYouTube Player API を連携する形で実装します。
まずはでき上がりのソースをペタッと貼り付けておきます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>aibo Web API | aibo Web API と YouTube Player API を連携して、YouTube 動画の再生秒数に合わせて aibo にふるまいをしてもらう</title>
</head>
<body>
  <!-- 1. iframe 置き換え -->
  <div id="player"></div>

  <script>
    const ACCESS_TOKEN = ${ACCESS_TOKEN};
    const BASE_PATH = 'https://public.api.aibo.com/v1';
    let deviceId = ${deviceId};

    // ふるまいを実行
    function executeAction (deviceId, eventId) {
      callActionApi(deviceId, 'play_motion', '{"Category": "' + eventId + '", "Mode": "NONE"}');
    }

    // Action API 呼び出し
    function callActionApi (deviceId, apiName, arguments) {
      postUrl = BASE_PATH + '/devices/' + deviceId + '/capabilities/' + apiName + '/execute';
      data = '{"arguments":' + arguments + '}';

      // POST API
      postApi(postUrl, data);
    }

    // POST API
    function postApi (url, argary) {
      url = url ? url : '';

      fetch(url, {
        method: 'POST',
        mode: 'cors',
        headers: {
          Authorization: 'Bearer ' + ACCESS_TOKEN
        },
        body: argary
      })
        .then(function (response) {
          return response.json();
        })
        .then(function (data) {
          outputResult(data.executionId);
        })
    }

    // API 実行結果 出力
    function outputResult (id) {
      const url = BASE_PATH + '/executions/' + id;

      fetch(url, {
        method: 'GET',
        mode: 'cors',
        headers: {
          Authorization: 'Bearer ' + ACCESS_TOKEN
        }
      })
        .then(function (response) {
          return response.json();
        })
        .then(function (data) {
          // API 実行結果 出力
          console.log(data);
        })
    }

    // 2. IFrame Player APIコード 非同期読み込み
    var tag = document.createElement('script');

    tag.src = "https://www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

    // 3. API をダウンロード後に iframe 作成
    var player;
    function onYouTubeIframeAPIReady() {
      player = new YT.Player('player', {
        height: '390',
        width: '640',
        videoId: 'KjqNqm23Ti0',
        events: {
          'onReady': onPlayerReady, // 4. ビデオレイヤーの準備できたら実行
          'onStateChange': onPlayerStateChange // 5.プレイヤーの状態が変化すると実行
        }
      });
    }

    // 4. ビデオレイヤーの準備できたら実行
    function onPlayerReady(event) {
      event.target.pauseVideo();
    }

    // 5.プレイヤーの状態が変化すると実行
    var done = false;
    function onPlayerStateChange(event) {
      // 再生
      if (event.data == YT.PlayerState.PLAYING && !done) {
        // 左右に体を揺らす
        setTimeout(executeAction, 8000, deviceId, 'swing');

        done = true;
      }
    }
  </script>
</body>
</html>

それぞれ見ていきます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>aibo Web API | aibo Web API と YouTube Player API を連携して、YouTube 動画の再生秒数に合わせて aibo にふるまいをしてもらう</title>
</head>
<body>

</body>
</html>

HTML の雛形です。
今回の実装に必要な最低限のものしか記述していません。

<!-- 1. iframe 置き換え -->
<div id="player"></div>

YouTube Player API のサンプルにあった記述です。
この部分が iframe に置き換わります。

<script>
</script>

実装を JavaScript で行うため、script タグを </body> 直前に追加します。

const ACCESS_TOKEN = ${ACCESS_TOKEN};
const BASE_PATH = 'https://public.api.aibo.com/v1';
let deviceId = ${deviceId};

以降は JavaScript の実装です。
${ACCESS_TOKEN} には、項目「アクセストークン取得」でコピーしておいたアクセストークンを入れます。
${deviceId} には、ふるまいをやってもらう aibo のデバイス ID をいれます。

// 2. IFrame Player APIコード 非同期読み込み
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

YouTube Player API のサンプルにあった記述です。
IFrame Player APIコード を非同期で読み込みます。

// 3. API をダウンロード後に iframe 作成
var player;
function onYouTubeIframeAPIReady() {
  player = new YT.Player('player', {
    height: '390',
    width: '640',
    videoId: 'KjqNqm23Ti0',
    events: {
      'onReady': onPlayerReady, // 4. ビデオレイヤーの準備できたら実行
      'onStateChange': onPlayerStateChange // 5.プレイヤーの状態が変化すると実行
    }
  });
}

YouTube Player API のサンプルにあった記述です。
API をダウンロード後に iframe 作成します。
events プロパティは動画の状態によって着火する関数を指定します。

// 4. ビデオレイヤーの準備できたら実行
function onPlayerReady(event) {
  event.target.pauseVideo();
}

YouTube Player API のサンプルにあった記述です。
3の onReady で指定した関数で、ビデオレイヤーの準備できたら動画を一時停止させています。

// 5.プレイヤーの状態が変化すると実行
var done = false;
function onPlayerStateChange(event) {
  // 再生
  if (event.data == YT.PlayerState.PLAYING && !done) {
    // 左右に体を揺らす
    setTimeout(executeAction, 8000, deviceId, 'swing');

    done = true;
  }
}

YouTube Player API のサンプルにあった記述です。
3の onStateChange で指定した関数で、プレイヤーの状態が変化すると実行されます。
setTimeout で動画が再生されてから8秒後に executeAction 関数を実行します。

// ふるまいを実行
function executeAction (deviceId, eventId) {
  callActionApi(deviceId, 'play_motion', '{"Category": "' + eventId + '", "Mode": "NONE"}');
}

executeAction 関数では、 Action API にある PlayMotion の POST リクエストで必要な情報を callActionApi 関数の引数に渡して実行しています。

// Action API 呼び出し
function callActionApi (deviceId, apiName, arguments) {
  postUrl = BASE_PATH + '/devices/' + deviceId + '/capabilities/' + apiName + '/execute';
  data = '{"arguments":' + arguments + '}';

  // POST API
  postApi(postUrl, data);
}

callActionApi 関数では、POST する URL とコンテンツの生成を行い
postApi 関数の引数に渡して、実行しています。

// POST API
function postApi (url, argary) {
  url = url ? url : '';

  fetch(url, {
    method: 'POST',
    mode: 'cors',
    headers: {
      Authorization: 'Bearer ' + ACCESS_TOKEN
    },
    body: argary
  })
    .then(function (response) {
      return response.json();
    })
    .then(function (data) {
      outputResult(data.executionId);
    })
}

ヘッダーに Authorization:Bearer を追加して、body を POST します。
aibo が処理を成功したか判定するために、レスポンスで返ってきた値 executionIdoutputResult 関数の引数に入れて実行します。

// API 実行結果 出力
function outputResult (id) {
  const url = BASE_PATH + '/executions/' + id;

  fetch(url, {
    method: 'GET',
    mode: 'cors',
    headers: {
      Authorization: 'Bearer ' + ACCESS_TOKEN
    }
  })
    .then(function (response) {
      return response.json();
    })
    .then(function (data) {
      // API 実行結果 出力
      console.log(data);
    })
}

引数で渡ってきた executionId を元に、API の実行結果をコンソールに出力するようにしています。

実行

それでは実行します。
イントロが終わったら、ルンルンし始めるおチャコさんです。

↓↓ クリックしたら動画が開きます。
ルンルンし始めるおチャコさん

課題

ACCESS_TOKEN と deviceId のベタ書きはセキュリティ上よろしくない

developer tools を使えばソースを表示することができてしまいます。
ローカル環境で実行する分には問題ないのですが、公開するのであれば大事な aibo の情報を晒さないようにひと工夫必要です。

aibo の集中力が試される

自由気ままにお散歩して、いろんなものに興味津々な aibo。
そんな aibo に天の声を届けようにも気が散ってしまってプログラムの実行が遅れます。
プログラムをきちんと実行してもらうには、事前に SetMode で指示待ちの状態にする必要がありそうです。

aibo へ天の声が届くまでに時差がある

端末 → クラウド → aibo へ天の声が届くまでに4秒ぐらいの時差があります。
ですので、その時差を考慮しながらふるまいを早めに実行してあげる必要があります。

ドキュメントに記載されているふるまいの動きがわからない

API にある PlayMotion だけで 76 種類ものふるまいが用意されているのですが
ダンスの振り付けをしようにも、ドキュメントはテキストベースなのでどんな動きをするのかピンとこない。
どこかのタイミングで1つ1つの動きを動画にして参照できるようにしようかなと考えています。

実行

それでは実行します。

↓↓ クリックしたら動画が開きます。
ルンルンし始めるおチャコさん

おわりに

現状だと API でできることも限られているので、もっと動きの選択肢が増えるといいな〜!
特別なふるまいがまるっと実行できなくても、実装済みの素敵な動きを
1つずつ API 実装していただければ、よりかわいいダンスの振り付けを
aibo に覚えてもらうことができる…。

aibo がアイドルのように…。
いつか東京あいぼるで aibo たちにダンスしてもらえる日を夢見て。

次回は、説明を端折っていた API のリクエストに必要となる
deviceId の取得方法を紹介したいと思います。

それではまた次回!

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

JSONトークンで認証ができるようにする

JSONトークンでできること

ある操作が、他の人からはできないようにする
例)
「削除」ができるのは「作成者」のみ、に制限する

トークン生成のためのnpmパッケージをインストール

・「jsonwebtoken」https://www.npmjs.com/package/jsonwebtoken
というパッケージを利用

$ npm install jsonwebtoken

でインストール

・jsonトークンを作ってみる
<公式ドキュメントより>

使い方
jwt.sign(payload, secretOrPrivateKey, [options, callback])

(非同期) コールバックが与えられた場合は、 err または JWT でコールバックが呼び出されます。

(同期) JsonWebToken を文字列で返します。

(中略)

デフォルトの同期処理(HMAC SHA256)

var jwt = require('jsonwebtoken');
var token = jwt.sign({ foo: 'bar' }, 'shhhhh');

const jwt = require('jsonwentoken')

const myFunction = async() => {
     const token = jwt.sign({_id: 'abc123'}, 'thisismykeyword') //第一引数:データベースの中の一意な値・第二引数:トークン作成の署名となる好きな文字列
     console.log(token) //作成したトークンを表示
}

myFunction() //jsonトークンを作成する同期処理を実行

jsonトークンが作成されていたら、console.logで確認できるはず

※jsonトークンの構成:base64でエンコードされている(BASE64でデコード可能)
※デコードすると、トークンの中の情報に、トークン化したid({_id: 'abc123'})と、トークン作成時点のタイムスタンプが含まれているのがわかる

トークンを認証に使う

const jwt = require('jsonwebtoken')

const myFunction = async() => {
     const token = jwt.sign({_id: 'abc123'}, 'thisismykeyword',{exporeIn: 'days'}) //第三引数:何日でトークンの効果が切れるか 
     console.log(token) 

     const data = jwt.veryfy(token, 'thisismykeyword')//tokenと、トークン作成の署名となる文字列とで認証
     console.log(data)
}

myFunction()

・トークン作成の署名となる文字列
・トークンの効果が切れる期間

でエラーを判定

トークンをアプリに組み込む

・アプリのファイル構成

└── src
    ├── index.js           //アプリのメイン処理を記述
    ├── models             //データベースのためのフィールド設定
    ├── middleware         //ミドルウェアを設定
    └── routers           //ルーティング処理

●models/user.js
データベースにtokenを保存するよう、データベースのスキーマを設定

const jwt = require('jsonwebtoken')
const userSchima = new mongoose.Schema({  //mongooseを用いてデータベースのスキーマを定義」
name: {
      type: String,  //データ型の設定
      required: true,
      trim: true
  },
  password:{
      type: String,
      required: true,
      trim: true,
      minlength: 7,
      validate(value){
        //toLowerCaseによって、小文字も大文字も小文字と認識される
        if(value.toLowerCase().includes('password')){
          throw new Error ('Do not use "password" to password')
        }
      }
  },
  tokens: [{   //tokensの項目を新たに設定
    token: {
      type: String,
      require: true
    }
  }],
})


//インスタンスにはメソッドを使って実装。generateAuthToken関数で、トークンを作成しDBへ保存
userSchema.methods.generateAuthToken = async function () {  //generateAuthToken関数を定義
  const user = this  //thisとは、userSchimaで定義したもの
  const token = jwt.sign({ _id: user._id.toString() }, 'thisismykeyword')

  user.tokens = user.tokens.concat({ token: token })//concatで、tokensの配列と、入力したtokenがuserのtokenと一致するものを結合する

  await user.save()//新しいuserをデータベースに保存

  return token //返り値にtoken変数を設定
}

//モデルにはクラスを使って実装。findbyCredentials関数で、Eメールとパスワードを元にDBを検索
userSchema.statics.findByCredentials = async (email, password) => {
  const user = await User.findOne({ email }) //emailの値が、引数のemailに一致

  if(!user){  //入力したEメールがデータベースにない
    throw new Error('Unable to login')
  }

  const isMatch = await bcrypt.compare(password, user.password)

  if(!isMatch){ //入力したパスワードがデータベースにない
    throw new Error('Unable to login')
  }

  return user
}

●routes/users.js
ルーティング処理時にJSONトークンを利用する

・JSONトークンの作成と保存
→user作成時の処理

router.post('/users', async (req, res) => {
  const user = new User(req.body)

  try {
      await user.save()

      const token = await user.generateAuthToken() //「models/user.js」で定義したgenerateAuthToken関数を実行
      res.status(201).send({user, token})
  } catch (e) {
      res.status(400).send(e)
  }
})

・JSONトークンを用いて認証する
→userログイン時の処理

router.post('/users/login', async (req, res) => {
    try {
        const user = await User.findByCredentials(req.body.email, req.body.password) //User.findByCredentials()はEメール、パスワードを引数にとり、userをemailによって検索し、パスワードをverifyする
        const token = await user.generateAuthToken()
        res.send({ user, token }) //ログインに成功した時の処理  
    }catch(e){
        res.status(400).send()
    }
})

ミドルウェアを使ってJSONトークン認証を設定する

・ファイル構成
└── src
    ├── index.js           //アプリのメイン処理を記述
    ├── models             //データベースのためのフィールド設定
    ├── middleware         //ミドルウェアを設定
    └── routers           //ルーティング処理

●「middleware/auth.js」
に、ミドルウェアとなるコードを記述しモジュールをエクスポート

・ミドルウェアの設定

●middleware/auth.js

const auth = async (req, res, next) => {
  console.log('認証ミドルウェアが動いています')
 next()
}

module.exports = auth

モジュールの雛形を作成

●routes/user.js
モジュールの読み込み

const auth = require('../middleware/auth') //ミドルウェアモジュールの読み込み


router.get('/users/me', auth, async (req, res) => { //第二引数にミドルウェア・第三引数にルーティング処理
    res.send(req.user)
})

GETリクエスト時に「認証ミドルウェアが動いています」とcosole.logされていればOK

●middleware/auth.js
・作成されたJSONトークンをコード内に取り込む
HTTPのヘッダーに送られているJSONトークン(key=Authorization)を読み込む

const auth = async(req, res, next) => {

     try{
          const token = req.header('Authorization')
          console.log(token)
     }catch(e){
          res.status(401).send({error: '認証してください'})
   }
}

●middleware/auth.js
認証させる

const jwt = require('jsonwebtoken')  //jwtモジュールの読み込み
const User = require('../models/user') //userデータベースの読み込み

const auth = async (req, res, next) => {  //HTTPヘッダーからJSONトークンの読み込み
  try {
    const token = req.header('Authorization').replace('Bearer ', '')
    const decoded = jwt.verify(token, process.env.JWT_SECRET) //jwtを作るのに使った秘密の文字列を用いてデコード
    const user = await User.findOne({ _id: decoded._id, 'tokens.token': token }) //userデータベースのidが、decodeしたJSONトークンの中に含まれるidと一致し、userデータベースのtokens配列の中のtokenと、tokenが一致

  if(!user){
    throw new Error()
  }

  req.token = token
  req.user = user
  next()

  } catch (e) {
    res.status(401).send({error: 'please authenticate'})
  }
}

module.exports = auth

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

Blobって

Blobって

Blob - MDN Web Docs - Mozilla
Blob オブジェクトはファイルに似たオブジェクトで、immutable な生データです。データを表す blob は必ずしも JavaScript ネイティブなフォーマットではありません。File インターフェイスは Blob を基礎にしており

Blob

ArrayBuffer とビューは ECMA 規格、JavaScript の一部です。

ブラウザには、さらに File API に記載されている高水準のオブジェクトがあります。特に Blob です。

Blob はオプションの文字列 type (通常は MIMEタイプ) と blobParts – 一連の他の Blob オブジェクト、文字列や BufferSources から構成されます。

コンストラクタの構文は次の通りです:

new Blob(blobParts, options);

blobParts は Blob/BufferSource/String の値の配列です
options オプションのオブジェクト:
type – blob タイプ, 通常は例えば image/png のような MIME タイプです,
endings – blob が現在の OS の改行(\r\n or \n)に対応するように行末を変換するかどうかを意味します。デフォルトでは "transparent" (何もしません)ですが、 "native" (変換する)にすることもできます。

  • Blob を base64 にする
    URL.createObjectURL の代替は blob を base64 エンコードされた文字列に変換する方法です。

  • Image を blob にする
    画像、画像の一部、あるいはページのスクリーンショットの blob を作成することもできます。どこかにアップロードするときに便利です。

画像操作は 要素を通して行われます:

canvas.drawImage を使って、canvas 上で画像(または一部)を書きます。
blob を作成し、完了時に callback を実行する canvas メソッド .toBlob(callback, format, quality) を呼び出します。

  • Blob から ArrayBuffer へ Blob コンストラクタは、BufferSource を含め、ほぼ何からでも blob を作成することができます。

しかし、低レベルの処理を実行する必要がある場合、FileReader を使って、最も低レベルである ArrayBuffer を取得することもできます。:

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

【JS】if文の中でletが使えない...

 やりたいこと

function fullname(first, last) {
  let last_name = "";
  if (typeof last === "string") {
    last_name = last;
  }

  return first + last_name;
}

console.log(fullname("山田", "太郎")); // => 山田太郎

このletconstにしたい。でも、再代入できないよなあ。ってことで

 三項演算子ちゃん

unction fullname(first, last) {
  const last_name = typeof last === "string" ? last : "";
  return first + last_name;
}

console.log(fullname("山田", "太郎")); // => 山田太郎

スッキリかけるけど、可読性が落ちる。ケースバイケース。

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

JSについて本気出して考えてみた

はじめに

こんにちはばーんです
今回はJavaScriptの言語仕様について書いていきます。

JavaScriptの勉強会参加と(https://x-hack.connpass.com/)
その際に勧められた書籍を読んで、自分で得た知識を整理していきます。

なるべく言語仕様書のように堅苦しくなく説明していくよう心掛けますw

今回の対象者

【JavaScriptは殆どオブジェクトである】という言葉の意味が理解できていない方

  • そもそもオブジェクトって何?
  • これってメソッド?プロパティ?
  • console.log("hello world") が "hello world" を表示するまでに何をしているのか?

といったことを解決していきたいと思います。

ゴール

【JavaScriptは殆どオブジェクトである】が理解できる

具体的には下記の内容を理解できればOKだと思っています。

①「そもそもオブジェクトがどういうものか?理解する」

②「JavaScriptがどういうもので構成されているか知る」

③「どういう流れで普段自分たちが②を意識せずに使えているか知る」

第1章: オブジェクト / プロパティ / メソッド って何?

前段の通りJavaScriptは殆どがオブジェクトです。

関数も配列も
※3章で掘り下げます

オブジェクトとは「名前と値を持つプロパティを格納するコンテナ」です。
例えば下記のようなオブジェクトがあった場合は、

const baan = {
  age: 31,
  gender: "man",
  sayHello: function () {
    console.log("こんにちは!");
  },
};

オブジェクト / プロパティ / メソッドを下記のように分類できます。

const baan = {
  age: 31,
  // ageという名前(キー)と31という値を持つプロパティです
  gender: "man",
  // genderという名前(キー)とmanという値を持つプロパティです
  sayHello: function () {
    console.log("こんにちは!");
  },
  // sayHelloという名前(キー)と値に関数を持つプロパティです
  // このプロパティの値はメソッドとなります
};
console.log(baan);
// 出力: { age: 31, gender: 'man', sayHello: [Function: sayHello] }
// baan は3つのプロパティを持ったオブジェクトです
baan.sayHello();
// 出力: こんにちは!

自分自身もオブジェクト、プロパティ、メソッドは混乱していましたが、このように明示できます。

オブジェクトのプロパティへのアクセス方法

どのようにオブジェクトを使用するか?ということです。

オブジェクト名 + ドット + プロパティ名(名前またはキー)でアクセスできます。
ちなみに関数の場合は()をつけることで実行されます。

const baan = {
  age: 31,
  gender: "man",
  sayHello: function () {
    console.log("こんにちは!");
  },
};
console.log(baan.age);
// 出力: 31

console.log(baan.gender);
// 出力: man

baan.sayHello();
// 出力: こんにちは!
baan.sayHello;
// 何も出力されません (sayHelloにアクセスしていますが括弧演算子がついていないので)

※ドット記法とブラケット記法がありますが、話が逸れるのでブラケット記法については書きません。

Point: console.log()は関数です

※正確には関数ではなくconsoleオブジェクトの中のメソッドの1つですが、伝わりやすくする為上記のような表記としています。

当時JS完全に理解した状態だった自分は、関数と聞くと

function () {
  "hogehoge"
};

みたいなモノを想像していました。
しかし、初学者にとっても馴染みの深いconsole.log()が関数だったなんて!と衝撃を覚えました。

Screen Shot 2020-08-04 at 14.29.49.png

このようにchromeのDevtoolにconsole.logを入力すると

ƒ log() { [native code] }

と表記されます。 ƒ はfunctionのことですね^^

第1章まとめ

  • オブジェクトはただの箱
  • 中に0個以上のプロパティを持っている
  • 使う時はオブジェクト名とキーをドットで繋げる
  • 関数を値に入れるとメソッドになる

このことを念頭に読み進めて頂けるとm_ _m

第2章: JSの構成要素を知る

まずここでは、JavaScriptがどのような要素で構成されているか?を見ていきます。

結論から言うとJavaScriptはプリミティブ値とネイティブオブジェクト(コンストラクタ)で構成されています。

プリミティブ

原始的な、基本の みたいな意味ですね。
JavaScriptでは数字、文字列、真偽値などが当てはまります。
(https://developer.mozilla.org/ja/docs/Glossary/Primitive)

ネイティブオブジェクト(コンストラクタ)

はい。こちらがややこしいので詳しく書いていきます。

コンストラクタ(関数)

そもそもコンストラクタ(関数)とは、
new演算子を使って実行されるとオブジェクトを生成する関数です。

ちょっと待って下さい。ブラウザバックしないで欲しい。

// ①最初にこれを実行するとエラーが出ます
console.log(baan);
const baan = new Object();
// 出力: ReferenceError: Cannot access 'baan' before initialization
// ②次にこちらを実行して下さい
const baan = new Object();
console.log(baan);
// 出力: {}

当然といえばそうですが、①はbaanを定義していないので構文エラーが出ます。
②はnew演算子を使って実行しているので空のオブジェクトが生成されています。
これを普段見慣れているような形にすると、

// ③普段見かける形
const baan = new Object();
baan.age = 31;
baan.gender = "man";
console.log(baan);
// 出力: { age: 31, gender: 'man' }

このObject()の部分がコンストラクタ関数です

ネイティブオブジェクト(コンストラクタ)

そして、原始的に用意されているネイティブオブジェクトは9つです。

Number()
String()
Boolean()
Object()
Array()
Function()
Date()
RegExp()
Error()

これ以外を宣言するとエラーが出ます。

// 無作法に新しいコンストラクタはできません
const baan = new Person();
baan.age = 31;
baan.gender = "man";
console.log(baan);
// 出力: ReferenceError: Person is not defined

ちなみにPersonを使用したい場合は、

const Person = function Person(age, gender, sayHello) {
  this.age = age;
  this.gender = gender;
  this.sayHello = sayHello;
  this.sayHello = function () {
    return sayHello;
  };
};

const baan = new Person(31, "man", "こんにちは!");
console.log(baan);
// 出力: Person { age: 31, gender: 'man', sayHello: [Function (anonymous)] }
console.log(baan.sayHello());
// 出力: こんにちは!

このようにコンストラクタ関数を宣言すればOKです!
これで想定通りの振る舞いになりました^^

2章まとめ

  • JavaScriptはプリミティブ値とネイティブオブジェクト(コンストラクタ)で構成されている
  • コンストラクタをnewすることでオブジェクトが生成される

第3章: 殆どがオブジェクトである理由を知る

ここではネイティブオブジェクト(コンストラクタ)から、どのようにして普段使っているオブジェクトになっているのかを掘り下げていきます。
その為には1章の冒頭部分を掘り下げていきます。

前段の通りJavaScriptは殆どがオブジェクトです。
関数も配列も

関数オブジェクトについて

関数もオブジェクトです。なので値としてセットできます。
例えば関数の引数であったり、メソッドとして。

const plusFunc = function (x, y) {
  return x + y;
};
const subtractFunc = function (x, y) {
  return x - y;
};
console.log(plusFunc(5, subtractFunc(4, 1)));
// plusFuncの引数は2つあります。この場合数値の5とsubtractFuncという関数です
// 今回subtractFuncは引数に4と1を持っており結果として3を返します。なので
// 出力: 8 (5 + 3なので)

※メソッドについては1章で触れているので省略します

また、ネイティブオブジェクト(コンストラクタ)についてですが、

const myFunction = new Function("x", "y", "return x + y");
console.log(myFunction);
// [Function: anonymous]
console.log(myFunction(1, 3));
// 出力: 4

const result = myFunction(2, 3);
console.log(result);
// 出力: 5

このようにnewすることで、無名関数を生成しています。
もう少しわかりやすくする為chromeのDevtoolを使います。

Screen Shot 2020-08-04 at 17.09.59.png

無名関数が生成されていますね^^

※ただし、Functionコンストラクタによる関数の生成は推奨されません。これは、JSエンジンによる最適化を妨げたり、他の問題を引き起こしたりする場合があるためです。

配列オブジェクトについて

こちらも関数同様にネイティブオブジェクト(コンストラクタ)をnewしてみます。

// ①ネイティブオブジェクト(コンストラクタ)を使用した配列の生成
const myArr = new Array(1, "test");
console.log(typeof myArr);
console.log(myArr[0]);
console.log(myArr[1]);

// ②配列リテラルを使用した配列の生成
const myArray = [1, "test"];
console.log(typeof myArray);
console.log(myArray[0]);
console.log(myArray[1]);
// myArrもmyArrayも出力は全て同じです
// 出力: object / 1 / test

普段私たちは②を使用していると思います。
が、内部的にはJavaScriptがよしなにやってくれているだけで、配列の生成は①で行われています。

このように普段使用している関数も配列もオブジェクトです。
そして、それらはネイティブオブジェクト(コンストラクタ)より生成されています。

なので「JavaScriptは殆どオブジェクト」なのです。

ちょっとまってプリミティブ値は?

2章まとめ

  • JavaScriptはプリミティブ値とネイティブオブジェクト(コンストラクタ)で構成されている

ここで述べているプリミティブ値はどーなんの?
という話ですが、プリミティブ値はオブジェクトでラップされる時があります。
つまり、擬似的にオブジェクトのように振る舞います。

const num = 1
console.log(num.toString());
console.log((1).toString());
// 出力: "1"

本来1(数値)はプリミティブ値なのでtoStringのプロパティを持ちませんが、プリミティブ値のプロパティにアクセスする際に、オブジェクトでラップされるのでこのような表現になります。

3章まとめ

つまり1〜3章をまとめて
「JavaScriptは殆どオブジェクト」なのです。

番外編: グローバルオブジェクト / プロトタイプチェーンを知る

自分自身が衝撃を受けた項目があるので、最後に番外編として追記させて頂きます。

グローバルオブジェクト

私たちがconsole.log()を使用する時、consoleオブジェクトについてはそれほど考えないと思います。
ただ、それでもchromeのDevtoolにconsole.logを入力すると使えてしまいます。

それはつまり、JavaScriptがよしなにやってくれてます。
具体的にブラウザは、グローバルオブジェクトとしてwindowオブジェクトを持っています。
※正確にはブラウザを立ち上げたクライアントPCのメモリ上に、グローバルオブジェクトを展開しています

そして、 console はオブジェクトであり、 window のプロパティです。 log はメソッドです。

①まずchromeのDevtoolにwindowを入力します
Screen Shot 2020-08-04 at 19.19.39.png

②windowのプロパティを見てみると大量のプロパティが設定されてあります
Screen Shot 2020-08-04 at 19.21.44.png

③その中にconsoleとlogがありましたね!
Screen Shot 2020-08-04 at 19.22.48.png

// いつも書いてる形
console.log("hoge")

// こちらでも同じ結果が出力される
window.console.log("hoge")

ちなみにブラウザ上なのでwindow.console.log("hoge") でも動きますが、Node.jsではエラーが出ます(ReferenceError: window is not defined)
グローバルオブジェクトが違うので。外面は同じですが内面は違う動きをしています。

プロトタイプチェーン

const myArr = [1, 2];
console.log(myArr);
// 出力: [ 1, 2 ]

const result = myArr.join("");
console.log(result);
// 出力: 12

普段私たちは配列を操作する時にこのようにメソッドを使用します(今回は join)。
このような時にmyArrにjoinというプロパティを定義しましたか?

していません。ですが、実際に私たちは明示的にjoinを定義しなくても使えています。
それにはJavaScriptのプロトタイプチェーンが関係しています。

まずプロトタイプチェーンを文面だけで説明すると、

ネイティブオブジェクトコンストラクタ関数(Object, Function, Arrayなど)はオブジェクトを生成する際にprototypeプロパティを継承させます。

待って諦めないで聞いて欲しい。スクロールはもう少し我慢して欲しい。
配列を例に説明していきます。

まず、配列を作る時、普段は

const myArr = [1, 2];

と宣言します。
これは、第3章>配列オブジェクトで記載している通り

const myArr = new Array(1, 2);

と同意です。つまり、ネイティブオブジェクトコンストラクタ関数である Array()をnewしてインスタンス(この場合myArr)を生成しています。
そして、その際にprototypeというプロパティを継承させています。

prototypeにはjoinというメソッドがあります
Screen Shot 2020-08-04 at 20.16.51.png

なので、私たちは明示しなくともjoinというメソッドが使えるのです。

プロトタイプチェーンは今回の場合であれば、myArrにjoinメソッドがないと判断するとエラーを返すのではなく遡って確認しにいきます。

※最終的にはObject()まで確認しにいきます

自分はプロトタイプチェーンを知ってから格段におまじないみたいなコードが減りました。
何故エラーが出るか?どういう仕組みで?が理解できたので。

簡単に言うとprototypeでリンクしているというイメージでいいかと思います。ブラウザでは__proto__という表記のものもあります。

補足

何点か補足事項があります。

今回書けなかった話

  • thisとは?
  • classとは?
  • インターフェースとは?
  • 予約語とは?

この辺りは次回以降にまとめていこうと思います。

書籍について

開眼!JavaScriptという本です。
とても楽しく読める言語仕様についての本でした^^
ただし、恐らく勉強始めてすぐにこれに出会っていても自分は理解できなかったと思います。

できればJavaScriptで何か動かしてから購入をおすすめします。
(道中呪文みたいな文章結構あるので)

質問 / ツッコミに関して

この記事に関して細部の認識はずれている可能性があります。
技術的なマサカリwは大歓迎ですので、何かあれば遠慮なくコメント頂けると幸いです。

参考サイト

参考にさせていただきましたm_ _m
ありがとうございました^^

https://developer.mozilla.org/ja/docs/Web/JavaScript
https://nodejs.org/api/console.html
https://harakotan.hatenablog.jp/entry/2015/05/17/004707
https://www.tweeeety.blog/entries/2014/02/05

さいごに

今回自分がこの記事を書くきっかけになったのはxhackさんの勉強会がきっかけでした。

参加していなければ理解が浅いまま進んでいたと思います。
この場を借りてお礼申し上げます。ありがとうございましたm_ _m

それではまた^o^/

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

【JS】分割代入のお作法(ES2015)

 分割代入とは

配列やオブジェクトの値を複数の変数に代入することができる手法。人のコードを読んだ時に、知らないと脳みそがフリーズする。

 配列の場合

const ary = [1, 2];
const [a, b] = ary;
console.log(a); // => 1
console.log(b); // => 2

 オブジェクトの場合

const obj = {
    "key": "value"
};
const { key } = obj;
console.log(key); // => "value"

変数名を省略した場合、key 名と同じになる

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

meta description にREADME のテキストがセットされてしまう現象

先日仕事で Web ページの不具合修正しデプロイ、その日は問題なく終了。そして数日が経ちました。
ある日マーケティングチームから Slack から一報が。

meta description に変な値が設定されているのですが、確認していただけますか?

さすがに驚いて確認したところ、コンフルエンスの URL が設定されていた…

これは完全に凡ミスで、いくつかの条件が重なって発生した問題です。後述しますが、普通に create-nuxt-app を使ってプロジェクトを立ち上げた場合は大丈夫だと思います。

状況および事象

まずは各ファイルの設定です。

nuxt.config.js(一部抜粋)
...

  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
         hid: 'description',
         name: 'description',
         content: process.env.npm_package_description  || ''  // ←ここが今回のポイント
     }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },

...
package.json(一部抜粋)
{
  "name": "project-name",
  "version": "1.0.0",
  "author": "kkeeth",
  "private": true,

  ...

  (description の設定無し)
README.md
# project name

hogehoge

この場合、meta description がどうなるかと言うと、タイトル通り README のテキストが設定されます。実際のブラウザのキャプチャがこちら↓↓↓

check-meta-project.png

これの修正方法は簡単で、

  1. nuxt.config.js に description のデフォルト値を設定する
  2. package.json の description を設定する

README は他の値を設定することもありますので、あまりおすすめしません。

原因究明

ではこの事象の根本的原因はなんだったのか?

▼仮説1
「これは Nuxt がデフォルトでこの値を設定している?」

ということで、Nuxtソースコードを読むも原因が分からず…

▼仮説2
「いや、確か Nuxtmeta 情報を vue-meta というライブラリで扱ってなかったか?」

ということで、vue-metaソースコードを読むも原因が分からず…

ここでハッと気付く…

正解

process.env.npm_package_description だから、npm がデフォルトの値を設定していないか?

ということで、npm 公式サイト を見ますと、しっかり書いてありました。。。

description: info from the README, or an empty string ""

はい。package.json に description がない場合は README から設定する1、なければ空文字列をセットします(実際に動作確認してみました)。2 要は、npm の仕様だったわけです。

余談

現在は create-nuxt-app でプロジェクトを作成しますと、nuxt.config.js の記述は以下のように npm ではなく package.json の設定を利用するようになっております。

nuxt.config.js
+ import pkg from './package'

  head: {
-   title: process.env.npm_package_name || '',
+   title: pkg.name,
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
         hid: 'description',
         name: 'description',
-        content: process.env.npm_package_description  || ''
+        content: pkg.description
     }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },

したがって、今回の問題はかなりのエッジケースになりますのでご安心ください。

まとめ

  • 公式の CLI のボイラープレートを信じよう
  • package.json の description はちゃんと書こう

ではでは(=゚ω゚)ノ


  1. 厳密には、heading 1 の次の行が設定されます 

  2. README ファイルが存在しない場合も同様です 

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

【JS】データ型について学ぶ

 プリミティブ型とオブジェクト

データ型を大きく分けるとプリミティブ型とオブジェクト**の二つに分けることができます。

 プリミティブ型

一度作成したらそのあとで変更することができない(イミュータブル)な特性を持つ。

  • 真偽値(true,false)
  • 数値(1,2.3など)
  • 文字列("test"など)
  • シンボル
  • BigInt
  • null
  • undefined

この七種類がJSにおけるプリミティブ型です。

 オブジェクト

作成したあとでも変更することができる(ミュータブル)な特性を持つ。

  • プリミティブ型以外のデータ
  • 配列,オブジェクト,関数など

 データ型を調べるメソッド

typeofを使えばOK

console.log(typeof "name"); // => string
console.log(typeof 111); // => number
console.log(typeof true); // => boolean
console.log(typeof []); // => object
console.log(typeof {}); // => object
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

モバイルでのモーダル表示時の背景スクロール禁止処理

以前よりhtml, body への overflow:hidden; はiOS Safari(webkit)では効かないため
主にjQueryで touchmove イベントに preventDefault(); で対応していたが

以前のコード
// スクロール無効
$(window).on('touchmove.noScroll', function(e) {
    e.preventDefault();
});  
// スクロール無効を解除
$(window).off('.noScroll');

iOS 11.3 Safari 11.1 及び Chrome 51 以降は addEventlistner の第3引数に { passive: false } を渡して対応する必要がある。

document.addEventListener('touchmove', function(e) {
    e.preventDefault();
}, { passive: false });
ex
function scroll_control(e) {
    e.preventDefault();
}
function no_scroll(){
    document.addEventListener("touchmove", scroll_control, {passive: false});
}
function return_scroll(){
    document.removeEventListener('touchmove', scroll_control, {passive: false});
}

preventDefault() の付け外しは ex のように関数化しないとうまくいかなかった。
また'passive' オプションに未対応ブラウザのために Polyfill を書く必要がある。

polyfill
// Test via a getter in the options object to see if the passive property is accessed
var supportsPassive = false;
try {
  var opts = Object.defineProperty({}, 'passive', {
    get: function() {
      supportsPassive = true;
    }
  });
  window.addEventListener("testPassive", null, opts);
  window.removeEventListener("testPassive", null, opts);
} catch (e) {}

// Use our detect's results. passive applied if supported, capture will be false either way.
elem.addEventListener('touchstart', fn, supportsPassive ? { passive: true } : false); 

elem fn の箇所と { passive: true } を適宜変更して使用

Browser

対応方法の参考リンク

Passive Event Listenersについての参考リンク

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

Javascriptのfetch

初心者が解説のサイトなどを見て、Javascriptのfetch()を理解しようとしています。

参考 fetch() の基本的な使い方

  • fetch() メソッドは従来の XMLHttpRequest や jQuery の $.ajax() を使って実現していたような、 リモートリソースの非同期取り込みに使える
  • 非同期呼び出しは Promise で実装されていて、then() で Response オブジェクトを受け取
  • ネットワークエラー以外は、基本的に最初の then() まで到達します。最初の then((response)) で response.ok や response.status をチェックすることで、サーバー側からの応答の状態を確認できます。
  • エラーを検出したら、Error() オブジェクトをスローすることで、catch() に処理が移る

Fetchの利用
- サービスワーカーのような他の技術から簡単に利用することができます。 Fetch は CORS や HTTP 拡張のような HTTP に関連する概念をまとめて定義する場所でもあります。

fetch('http://...',{オプション})
.then((response)=>{
  if(!response.ok){
    throw new Error();
  }
  return response.blob();//あるいは response.json()
})
.then((blob)=>{

})
.catch((reason)=>{
  //エラー
});

fetch() による GET リクエストの送信

fetch('/test1')
  .then((res) => {
      if (!res.ok) {
          throw new Error(`${res.status} ${res.statusText}`);
      }
      return res.blob();
  })
  .then((blob) => {
      // ...
  })
  .catch((reason) => {
      console.log(reason);
  });
fetch() による POST リクエス

fetch() による POST リクエストの送信

const data = new FormData();
data.set('message', 'Hello!');

fetch('/test',
    {
        method: 'POST',
        cache: 'no-cache',
        body: data
    })
    .then((res) => {
        if (!res.ok) {
            throw new Error(`${res.status} ${res.statusText}`);
        }
        return res.blob();
    })
    .then((blob) => {
      // blob にレスポンスデータが入る
    })
    .catch((reason) => {
        console.log(reason);
    });

JSON文字列をポストする場合
const data = {
    message: 'Hello'
};

fetch('/test',
    {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
    })
    .then((res) => {
        if (!res.ok) {
            throw new Error(`${res.status} ${res.statusText}`);
        }
        return res.blob();
    })
    .then((blob) => {
        // blob にデータが入る
    })
    .catch((reason) => {
        console.log(reason);
    });

?やばい、よくわからない

  • 最初の then((response)) にて、return response.blob() とすることで、 レスポンスの内容を Blob オブジェクトに詰め込んで、次の then() に渡すことができます。

Blobオブジェクトとは:

  • fetch() から返される Promise は レスポンスが HTTP 404 や 500 を返して HTTP エラーステータスの場合でも拒否されません。代わりに (ok ステータスが false にセットされて) 正常に解決し、拒否されるのはネットワークのエラーや、何かがリクエストの完了を妨げた場合のみです。
  • fetch() はサイトをまたぐクッキーを受け付けません受信することができます。フェッチを使用してサイトをまたぐセッションを確立することができませんできます。他のサイトからの Set-Cookie ヘッダーは暗黙に無視されます。
  • fetch はサーバーとの間で cookies を送受信しないため、サイトがユーザーセッションの維持に頼っている場合は未認証のリクエストになります。クッキーを送るには、認証情報の init オプションを設定しておく必要があります。
簡単な例
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data=>consol.elog(data));

参考
これを実際に使ってみたら何となく理解できたような気がする

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>サンプル</title>
</head>
<body >

<input type="button" value="ボタン1" onclick="clickBtn1()">
<input type="text" name="cs1" id="cs1" value="custom1" maxlength="7">
<input type="text" name="cs2" id="cs2" value="custom2" maxlength="7">

<script>
function clickBtn1(){
    // URLSearchParamsに画面の値をセット
    const para1 = new URLSearchParams();
    para1.set("cs1",document.getElementById("cs1").value);
    para1.set("cs2",document.getElementById("cs2").value);

    fetch("http://httpbin.org/get?" + para1.toString())

        .then(function(response1) { //成功時に実行される
            console.log("status=" + response1.status); //status=200
            return response1.json();
        })
        .then(function(data1) { //成功時に実行される
            console.log(JSON.stringify(data1)); //JSONを出力
        })
        .catch(function(err1) { //失敗時に実行される
            console.log("err=" + err1);
        });
}
</script>
</body>
</html>

Service WorkersではこのFetch APIを使うことになっている

お疲れさまXMLHttpRequest、こんにちはfetch

Service Workersでは、XMLHttpRequestが使えません。その代わり、XMLHttpRequest (以下、XHR)に代わるWHATWGの仕様としてFetch APIがあり、Service WorkersではこのFetch APIを使うことになっていますので、その使い方を簡単に紹介します。

Fetch API自体は、Service Workers専用のものではなく、メインスレッドでもXHRの代わりに使うことが可能です。

fetch('URLあるいは相対パスなど').then(...);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

P5.js 日本語リファレンス(translate)

このページでは「P5.js 日本語リファレンス」 の translate 関数を説明します。

translate()

説明文

表示ウィンドウ内のオブジェクトを移動する量を指定します。 x パラメータは左/右への移動を指定し、y パラメータは上/下への移動を指定します。

変換は累積的であり、関数への後続の呼び出しが効果を累積した後に発生するすべてに適用されます。たとえば、translate(50, 0) を呼び出してから translate(20, 0) を呼び出すことは translate(70, 0) と同じです。 draw() 内でtranslate() が呼び出された場合、ループが再び始まると変換がリセットされます。この関数は push() および pop() を使用してさらに制御できます。

構文

translate(x, y, [z])

translate(vector)

パラメタ

  • x
    Number:左/右変換

  • y
    Number:アップ/ダウン変換

  • z
    Number:順方向/逆方向変換(webglのみ)(オプション)

  • vector
    p5.Vector:変換するベクトル

例1

function setup() {
  createCanvas(400, 400);

  stroke("red");
  rect(0, 0, 55, 55);

  stroke("green");
  translate(30, 30);
  rect(0, 0, 55, 55);

  stroke("blue");
  translate(30, 30);
  rect(0, 0, 55, 55);  
}

実行結果

https://editor.p5js.org/bit0101/sketches/PI43OkNFn

例2

function setup() {
  createCanvas(200, 200);
}

function draw()  {
  background(200);

  rectMode(CENTER);
  translate(width / 2, height / 2);

  // ミリ秒を1000で割った数を角度とし長さ60のベクトルを作成する。
  // (回転する半径が60になる)
  translate(p5.Vector.fromAngle(millis()  / 1000, 60));
  rect(0, 0, 20, 20);
}

実行結果

https://editor.p5js.org/bit0101/sketches/tVYVP1BjY

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

P5.js 日本語リファレンス(shearY)

このページでは「P5.js 日本語リファレンス」 の shearY関数を説明します。

shearY()

説明文

角度パラメータで指定された量だけ y 軸を中心に形状をせん断変形します。角度は angleMode で指定する必要があります。オブジェクトは常に原点に対する相対位置を中心にせん断変形し、正の数はオブジェクトを時計回りにせん断変形します。

変換は関数への後続の呼び出しが効果を蓄積した後に発生するすべてに適用されます。たとえば shearY(PI / 2) を呼び出してから shearY(PI / 2) を呼び出すことは shearY(PI) と同じです。 draw() 内でshearY() が呼び出された場合、ループが再び始まると変換がリセットされます。

技術的には shearY() は現在の変換行列に回転行列を乗算します。この関数は push() および pop() 関数によってさらに制御できます。

構文

shearY(angle)

パラメタ

  • angle
    Number:現在の angleMode に応じて、ラジアンまたは度で指定されたせん断角度

例1

function setup() {
  createCanvas(200, 200);
}

function draw() {
  background(220);

  translate(10, 10);
  stroke("black");
  rect(0, 0, 30, 30a);

  stroke("red");
  shearY(PI / 6.0); // 角度30°を指定
  rect(0, 40, 30, 30);

  stroke("green");
  shearY(PI / 6.0); // 更に角度30°を指定
  rect(0, 80, 30, 30);
}

実行結果

https://editor.p5js.org/bit0101/sketches/Wp-4pk72E

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

P5.js 日本語リファレンス(shearX)

このページでは「P5.js 日本語リファレンス」 の shearX関数を説明します。

shearX()

説明文

角度パラメータで指定された量だけ x 軸を中心に形状をせん断変形します。角度は angleMode で指定する必要があります。オブジェクトは常に原点に対する相対位置を中心にせん断変形し、正の数はオブジェクトを時計回りにせん断変形します。

変換は関数への後続の呼び出しが効果を蓄積した後に発生するすべてに適用されます。たとえば shearX(PI / 2) を呼び出してから shearX(PI / 2) を呼び出すことは shearX(PI) と同じです。 draw() 内でshearX() が呼び出された場合、ループが再び始まると変換がリセットされます。

技術的には shearX() は現在の変換行列に回転行列を乗算します。この関数は push() および pop() 関数によってさらに制御できます。

構文

shearX(angle)

パラメタ

  • angle
    Number:現在の angleMode に応じて、ラジアンまたは度で指定されたせん断角度

例1

function setup() {
  createCanvas(200, 200);
}

function draw() {
  background(220);

  translate(10, 10);
  stroke("black");
  rect(0, 0, 30, 30);

  stroke("red");
  shearX(PI / 6.0); // 角度30°を指定
  rect(40, 0, 30, 30);

  stroke("green");
  shearX(PI / 6.0); // 更に角度30°を指定
  rect(80, 0, 30, 30);
}

実行結果

https://editor.p5js.org/bit0101/sketches/OKHFZN84F

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

P5.js 日本語リファレンス(scale)

このページでは「P5.js 日本語リファレンス」 の scale関数を説明します。

scale()

説明文

頂点を拡大および縮小することにより、形状のサイズを拡大または縮小します。オブジェクトは常に相対的な原点から座標系にスケーリングされます。スケール値は10進数のパーセンテージとして指定されます。たとえば関数呼び出し scale(2.0) は図形の次元を200%増やします。

変換は関数の呼び出しの後に発生するすべてのものに適用され、効果が乗算されます。たとえば scale(2.0) を呼び出してから scale(1.5) を呼び出すことは scale(3.0)と同じです。 scale() がdraw() 内で呼び出された場合、ループが再び始まると変換がリセットされます。

WEBGLモードでのみ z パラメータを使用することができます。この関数は push() および pop() でさらに制御できます。

構文

scale(s, [y], [z])

scale(scales)

パラメタ

  • s
    Number | p5.Vector | Number[]:オブジェクトをスケーリングするためのパーセント、または複数の引数が与えられた場合にx軸でオブジェクトをスケーリングするためのパーセント

  • y
    Number:y軸でオブジェクトをスケーリングするパーセント(オプション)

  • z
    Number:z軸でオブジェクトをスケーリングするパーセント(webglのみ)(オプション)

  • scales
    p5.Vector | Number []:オブジェクトをスケーリングするための軸ごとのパーセント

例1

function setup() {
    createCanvas(200, 200);
  }

  function draw() {
    background(220);

    stroke("red");
    rect(20, 20, 50, 50);

    scale(1.5, 1.5);
    stroke("blue");
    rect(20, 20, 50, 50);
  }

実行結果

https://editor.p5js.org/bit0101/sketches/HPq7XBVIh

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

P5.js 日本語リファレンス(rotateZ)

このページでは「P5.js 日本語リファレンス」 の rotateZ関数を説明します。

rotateZ()

説明文

Z軸を中心に回転します。

構文

rotateZ(angle)

パラメタ

  • angle
    Number:現在のangleMode(RADIANS、DEGREES)に応じて、ラジアンまたは度で指定される回転角度

例1

function setup() {
  createCanvas(200, 200, WEBGL);
}

function draw() {
  background(255);

  rotateZ(millis() / 1000);

  box() ;
}

実行結果

https://editor.p5js.org/bit0101/sketches/lxND0EVWX

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

P5.js 日本語リファレンス(rotateY)

このページでは「P5.js 日本語リファレンス」 の rotateY関数を説明します。

rotateY()

説明文

Y軸を中心に回転します。

構文

rotateY(angle)

パラメタ

  • angle
    Number:現在のangleMode(RADIANS、DEGREES)に応じて、ラジアンまたは度で指定される回転角度

例1

function setup() {
  createCanvas(200, 200, WEBGL);
}

function draw() {
  background(255);

  rotateY(millis() / 1000);

  box() ;
}

実行結果

https://editor.p5js.org/bit0101/sketches/OxM5qzrJd

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

P5.js 日本語リファレンス(rotateX)

このページでは「P5.js 日本語リファレンス」 の rotateX関数を説明します。

rotateX()

説明文

X軸を中心に回転します。

構文

rotateX(angle)

パラメタ

  • angle
    Number:現在のangleMode(RADIANS、DEGREES)に応じて、ラジアンまたは度で指定される回転角度

例1

function setup() {
  createCanvas(200, 200, WEBGL);
}

function draw() {
  background(255);

  rotateX(millis() / 1000);

  box() ;
}

実行結果

https://editor.p5js.org/bit0101/sketches/fZxfUVFZU

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

P5.js 日本語リファレンス(rotate)

このページでは「P5.js 日本語リファレンス」 の rotate関数を説明します。

rotate()

説明文

角度パラメータで指定された量だけ形状を回転させます。この関数はangleModeを考慮しているため RADIANS または DEGREES のいずれかで角度を指定します。

オブジェクトは常に原点に対する相対位置を中心に回転し、正の数はオブジェクトを時計回りに回転させます。変換は関数への後続の呼び出しが効果を蓄積した後に発生するすべてに適用されます。たとえば rotate(HALF_PI) を呼び出してから rotate(HALF_PI) を呼び出すことは rotate(PI) と同じです。 draw() が再び開始するとすべての変換がリセットされます。

技術的には rotate() は現在の変換行列に回転行列を乗算します。この関数は push() および pop() によってさらに制御できます。

構文

rotate(angle, [axis])

パラメタ

  • angle
    Number:現在の angleMode(RADIANS、DEGREES) に応じてラジアンまたは度で指定される回転角度

  • axis
    p5.Vector | Number []:(3d)回転する軸(オプション)

例1

function setup() {
  createCanvas(200, 200);
  angleMode(RADIANS);
}

function draw() {
  background(220);

  stroke("red");
  rect(100, 0, 52, 52);

  rotate(PI / 6.0);  // 30°時計回りに回転
  stroke("green");
  rect(100, 0, 52, 52);

  rotate(PI / 6.0);  // 更に30°時計回りに回転
  stroke("blue");
  rect(100, 0, 52, 52);
}

実行結果

https://editor.p5js.org/bit0101/sketches/ZDsjqSn6b

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

P5.js 日本語リファレンス(resetMatrix)

このページでは「P5.js 日本語リファレンス」 の resetMatrix関数を説明します。

resetMatrix()

説明文

現在の行列を単位行列に置き換えます。

構文

resetMatrix()

例1

function setup() {
  createCanvas(200, 200);
  angleMode(DEGREES);
  rectMode(CENTER);
}

function draw() {
  background(220);

  translate(60, 60);

  // 45°回転
  let angle = 45
  applyMatrix( cos(angle), sin(angle),
              -sin(angle), cos(angle),
                        0,         0);
  stroke("red");
  rect(0, 0, 40, 40);

  //applyMatrix() をリセットします。(translate()もリセットします)
  resetMatrix();

  translate(20, 20);

  stroke("blue");
  rect(0, 0, 40, 40);
}

実行結果

https://editor.p5js.org/bit0101/sketches/H_oflZuQg

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

正式リリース前に総予習!! Vue3の変更点まとめ

8月上旬に正式リリース予定とされているVue3の変更点をいち早く理解できるように概要をまとめてみました。それぞれの項目ごとにvuejs/rfc又はVue3 Docmentへのリンクを貼っているので索引的に使ってもらえると嬉しいです。

この記事は以下バージョン時点の情報です。

Composition APIの追加

おそらく一番大きな目玉となる変更はこちら。Composition APIという新しいAPIが追加されます(PluginでVue2系でも使用可能です)。
Composition APIはコンポーネントのロジックの柔軟な再構成を可能にする関数ベースのAPI群です。
なお、Vue2系のOptional APIのサポートも継続されるので、Vue2系からのVue3へのバージョンアップ時にComposition APIに書き換える必要はありません。

<!-- Vue3 Composition API -->
<template>
  <div>
    <input type="text" v-model="message" />
    <p>{{ nameAndMessage }}</p>
    <button @click="submit">submit</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed, ref, onMounted } from "vue";

export default defineComponent({
  props: {
    name: {
      type: String,
      required: true
    }
  }
  setup(props, context) {
    // reactive data
    const message = ref('Hello Vue3');

    // computed
    const upperCaseMessage = computed(() => `${props.name} ${message.value}`)

    // emit
    const submit = () => context.emit('submit', message.value)

    // lifecycle hook
    onMounted(() => {
      // ....
    })
    return {
      message,
      nameAndMessage,
      submit
    };
  }
});
</script>

Composition APIについてはこちらの記事でより詳細にまとめています。

先取りVue 3.x !! Composition API を試してみる - Qiita
Vue Composition APIで使えるリアクティブ関連のAPI一覧 - Qiita

Teleportの追加

Teleportは定義したコンポーネントが属するDOMツリーとは別の場所に、まるでテレポートしたかのようにコンポーネントを移動できる機能です。Vue2系でもサードパーティのプラグインLinusBorg/portal-vueで実現されていました。

以下のようにモーダル表示をハンドルするコンポーネントで<teleport>を使うと、toで指定したDOM要素に内部のコンポーネントを移動できます。

<template>
  <div class="container">
    <button @click="toggleModal">toggle modal</button>
    <teleport to="#teleport-target">
      <MyModal v-if="isVisible"/>
    </teleport>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup() {
    const isVisible = ref(false);

    const toggleModal = () => {
      isVisible.value = !isVisible.value; };
    return {
      isVisible,
      toggleModal
    };
  }
});
</script>

toで指定できるDOM要素は自信の属するDOM要素以外でもOKです。

<html lang="ja">
<body>
  <div id="app"></div> <!-- VueがマウントされるDOM -->
  <div id="teleport-target"></div> <!-- teleportで指定されているDOM。ここにMyModalが表示される-->
  <script src="/dist/main.js"></script>
</body>
</html>

teleportを利用すると、いままでCSSのz-indexで調整していたDOMの重なり順制御を、宣言的に制御できるようになりz-indexの指定に悩まされることはなくなります。
Vue3を入れたらすぐに使いたいと思える実用的な機能ですね。

こちらの記事でも詳細をまとめています。
Vue.js 3.0 の新機能を試す。 〜 Teleport 編〜 - Qiita

Fragmentsの追加

FragmentsはVue2系では実現できなかったmulti-root nodeのコンポーネントを可能にするものです。
Vue2系ではコンポーネントのroot要素は必ず1つという制約がありました。

<template>
  <div>
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
  </div>
</template>

Vue3ではFragmentsのおかげでその制約がなくなり、root要素に複数の要素を記載できます。冗長なdivでのラップなどが不要になります。簡潔で良いですね。

<template>
  <header>...</header>
  <main>...</main>
  <footer>...</footer>
</template>

ただ、注意点とし子てコンポーネントに直接Class指定をしていると、今まで自動的にroot要素にClassを当てられたのですが、それがfragmentsの場合は明示的な指定が必要になるというのがあります。

Suspenseの追加(実験的)

Suspenseは非同期処理が解決されるまでフォールバックコンテンツ(例えばLoading中アイコン)を表示してくれる特別なコンポーネントです。いままで、v-if="loading === true" などの状態変数を使って制御していたものを、状態変数を使わずに簡潔に書くことができます。

Suspenseはまだ実験的な機能で、Vue3.0の段階では仕様が確定しておらず今後変更が入ることも考えられます。Vue 3.1での正式リリースを見越しているとのことです。

<template>
  <Suspense>
    <template #default>
      <AsyncComponents/> <!-- 非同期処理を実行するコンポーネント -->
    </template>
    <template #fallback>
      loading...
    </template>
  </Suspense>
</template>

Suspenseの詳細はこちらにまとめています。
Vue.js 3.0 の新機能を試す。 〜 Suspense 編〜 - Qiita

v-modelの仕様変更

v-modelの機能が拡張され、ひとつの要素に複数のv-modelの設定が可能になります。
Vue2系のv-bind.syncで実現していたようなことがv-modelで実現できるようになるイメージですね。この機能によってv-bind.syncは代替されるので利用できなくなります。

以下v-modelの複数バインドの例です。
v-model:xxxxのxxxのところで明示的にバインドするpropsを指定しています。

<!-- 親コンポーネント -->
<template>
  <UserForm
   v-model:name="name"
   v-model:message="message"
  />
</template>
<!-- UserFormコンポーネント -->
<template>
  <form>
   <label>name</label>
   <input 
    type="text"
    @input="$emit('update:name', $event.target.value)"
   >
   <label>message</label>
   <input 
    type="text"
    @input="$emit('update:message', $event.target.value)"
   >
  </form>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  props: {
    name: {
      type: String,
      default: ''
    },
    age: {
      type: String,
      default: ''
    }
  },
  setup() { return {} }
})
</script>

また、modifiersの機能が拡張され、.lazy, .number, trimのデフォルトのmodifiers以外に独自に,modifiersを定義できるようになりました。
以下はcapitalizeというmodifiersを設定している例です。v-model:name.capitalize=とすることで入力値にcapitalizeを実行しています。

<!-- 親コンポーネント -->
<template>
  <form>
    <MyName  v-model:name.capitalize="form.name"/>
    <!-- ... -->
  </form>
</template>
<!-- MyNameコンポーネント -->
<template>
  <input
    :value="name"
    @input="updateName"
    type="text"
    name="name"
  />
</template>

<script>
import { defineComponent } from "vue";

export default defineComponent({
  props: {
    name: {
      type: String,
      default: ''
    },
    nameModifiers: {
      default: () => ({})
    }
  },
  setup (props, { emit }) {
    const updateName = event => {
      let val = event.target.value
      if (props.nameModifiers.capitalize) {
        val = val.charAt(0).toUpperCase() + val.slice(1)
      }

      emit('update:name', val)
    }

    return {
      updateName
    }
  }
}
</script>

scoped CSSの仕様変更

scoped CSSで使える擬似クラスの仕様変更・機能追加が行われます。
まず1つ目が::v-deep()でこれは、今まで/deep/として設定していた、子コンポーネントにスタイルを適用するための擬似クラスです。
/deep/の記法がDEPRECATEDとなり、新たに::v-deep()が追加されています。

::v-deep(.foo) {}
/* コンパイル後 */
[v-data-xxxxxxx] .foo {}

2つ目が、::v-slotted()で、これはslotで受け取った親コンポーネントの要素にスタイルを適用するためのものです。Vue3からデフォルトではslotで受け取った要素は、子コンポーネントのスタイルが適用されなくなるため、こちらが追加されました。

::v-slotted(.foo) {}
/* コンパイル後 */
.foo[v-data-xxxxxxx-s] {}

最後が、::v-global()で、これはscoped CSSの中でグローバルなスタイルの定義を可能とするものです。この擬似要素を設定したクラスには[v-data-xxxx]が付与されないため、グローバルに適応されます。

::v-global(.foo) {}
.foo {}

$attrsの仕様変更

$attrsの機能が拡張され、今まで$listenersで参照していたネイティブイベントや別管理であったclass、styleも全て包括して$attrsから取得できるようになりました。

<MyButton 
  @click="handleClick" 
  @custom="handleCustom" 
  v-model="value"
  type="button"
  class="btn"
/>
<!-- MyButton -->
$attrs: {
  class: 'btn'
  type: 'button',
  onClick: handleClick,
  onCustom: handleCustom,
  'onUpdate:modelValue': () => { value = payload },
}

そのため、$listenersはDEPRECATEDとなります。(実行時に警告がでます)
$listenersを使って透過的なラッパーコンポーネントを作っていた場合は、$listenersを削除し$attrsへの書き換えが必要です。

<!-- Vue2系 -->
<input v-bind="$attrs" v-on="$liseners" />
<!-- Vue3 -->
<input v-bind="$attrs" />

Global APIの仕様変更

Vueの初期化処理が変わります。createAppでインスタンスを生成し、そのインスタンススコープでusemixinなどが適応されるようになります。
これによってテストケースでのグローバル設定の汚染を防いだり、同一ファイル内で別設定のVueインスタンスの初期化が容易になります。

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App) // インスタンスを生成

app.config.isCustomElement = tag => tag.startsWith('app-')
app.use(/* ... */) // インスタンススコープで設定される
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)

app.config.globalProperties.customProperty = () => {}

app.mount(App, '#app')

filterの廃止

filterは、パイプ演算子|を使って関数適応をlinuxの標準入出力のパイプラインするものですがJavaScriptにはない独自構文で学習コストが増加する、構文解析を複雑にするという理由で削除されます。

filterの処理は通常の関数適応で代替できます。

<!-- filter形式 -->
<p>{{ msg | uppercase | reverse }}</p>

<!-- 通常の関数適応形式 -->
<p>{{ reverse(uppercase(msg)) }}</p>

他の代替方法についてはrfcs/0015-remove-filters.md#alternativesをご覧ください。

Event Emitter系のAPIの廃止

Event Emitter系のAPI($on, $off, $once)が削除されます。
グローバルなイベント管理としてEvent Emitterを仕様している場合は書き換えが必要です。
Event管理の代替にはMittなどの別のEvent Emitterライブラリの利用が推奨されています。

Functional Componentの廃止

Functional Componentはdata等の状態を持たない代わりにレンダリングパフォーマンスの大幅な改善が行えるコンポーネントです。
Vue3ではFunctional Componentを作る際に使用するfunctional: trueのオプションや、<template functional>が削除されます。

Vue3では、通常のコンポーネントとFunctional Componentの性能差は大幅に縮小され、ほとんどのユースケースでは取るに足らないものとなったそうです。

SFC上でfunctionalを使用している場合は、functionalの属性を削除し、attrs、listnersの参照方法の修正が必要です。

<!-- Vue2 functional component -->
<template functional>
  <component
    :is="`h${props.level}`"
    v-bind="attrs"
    v-on="listeners"
  />
</template>

<script>
export default {
  props: ['level']
}
</script>
<!-- Vue3 -->
<template> <!-- functionalを削除 -->
  <component
    v-bind:is="`h${props.level}`"
    v-bind="$attrs" <!-- attrs及びlistnersは$attrsにまとめられる  -->
  />
</template>

<script>
export default {
  props: ['level']
}
</script>

終わりに

以上「正式リース前に総予習!! Vue3の変更点まとめ」でした。
色々ワクワクするような新機能が追加されていてVue3のリリースが待ち通しいですね。
他にも諸々細かい点が変わってるのでより詳細な変更はvuejs/rfcをご確認ください。

参考

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

ウェブサイト作成用備忘録・3号:video タグを活用した疑似アニメーション背景

日々の学習のアウトプットの為、自主学習の際に工夫した内容を記録していきます。

今回は video タグを用いた疑似アニメーション背景について

css の background プロパティには動画ファイルを直接設定することが出来ません。

そこで、自分でリサーチした内容をまとめていきたいと思います。

方法:body タグ直下に video タグを配置し、css の設定で画面全体を覆うようにサイズを変更する。そして video タグには position: fiexd、それ以外の同じ階層の親要素には position: relative プロパティを設定して、各要素を重ねて表示する。

前準備

mp4 形式のファイルをそのまま採用する場合、そのままの画質でウェブサイトに表示させようとすると、動画の読み込みに時間が掛かってしまいます。ウェブサイトの背景に使用する動画のファイルサイズは最大でも1MB以内をオススメします。

(自分が普段利用している著作権フリーの動画素材サイトを共有しておきます。参考になれば幸いです)
高品質なフリー動画素材 - Pixabay

このサイトでは高画質な動画素材をダウンロードできますが、ファイルサイズが小さな動画でも1MBを超えているものがほとんどです。

その為、次にダウンロードした動画を圧縮加工する必要があります。

(自分が普段利用しているオンラインサービスを共有しておきます。参考になれば幸いです)
VideoSmaller

ダウンロードした動画をこのサイトで圧縮することで、ファイルサイズを1MB以下まで小さくすることが出来ます。
今回はウェブサイトの背景の為の動画素材なので、「ビデオからオーディオを削除する」のチェックボックスを忘れずに有効にしておきましょう。

記述例

HTML

<html>
  <body>
    <video class="background_video" src="動画背景用素材" autoplay loop muted poster="動画背景用素材のスクリーンショット"></video>
    <div class="">
      <h1>hello world</h1>
    </div>
  </body>
</html>

CSS

body {
  margin: 0;
}
div {
  position: relative;
  z-index: 1;
}
h1 {
  margin: 0;
}
.background_video {
  min-width: 100%;
  min-height: 100%;
  position: fixed;
  z-index: 0;
}

解説

1・video タグには以下の設定を行い、position: fixed と z-index プロパティをそれぞれ設定する。
autoplay(サイト読み込み時に自動再生)
loop(再生終了後にループ再生)
muted(動画の音声を再生しない、事前にオーディオを削除していても、この設定をしないと自動再生が行われない)
poster(ブラウザが動画再生未対応の場合、posterで設定した画像が代わりに表示される)

2・video と同階層の別要素には position: relative と z-index プロパティをそれぞれ設定する。

今回はこれで以上になります。

あくまで自分用の備忘録ですが、他の方の参考になれば幸いです。

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

【Nuxt.js】Nuxt文法編:computed

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

前置き

今回はcomputed?
getter関数とsetter関数が使える
算出プロパティです!

簡単な使い方や
methodsとの違いを解説しています?‍♀️

computed

簡単な使い方

スクリーンショット 2020-08-02 13.02.24.png

テキストを反転させます?

【index.vue】
プロパティなので呼び出す際は
{{ reversedMessage() }}
ではなく
{{ reversedMessage }}
でOKです?

index.vue
<template>
 <div class="page">
   <p>message: {{ message }}</p>
   <p>reversed message: {{ reversedMessage }}</p>
 </div>
</template>

<script>
export default {
 data () {
   return {
     message: 'Hello Nuxt.js!',
   }
 },
 computed: {
   reversedMessage () {
     return this.message.split('').reverse().join('')
   },
 },
}
</script>

methodsを使った場合のコード

呼び出す物が関数になるので
{{ reversedMessage() }}

index.vue
<template>
 <div class="page">
   <p>message: {{ message }}</p>
   <p>reversed message: {{ reversedMessage() }}</p>
 </div>
</template>

<script>
export default {
 data () {
   return {
     message: 'Hello Nuxt.js!',
   }
 },
 methods: {
   reversedMessage () {
     return this.message.split('').reverse().join('')
   },
 },
}
</script>

thisコンテキストを使う

thisコンテキストが使えます。
this.messageを平仮名にしてみます。

スクリーンショット 2020-08-02 13.05.44.png

index.vue
<template>
 <div class="page">
   <p>message: {{ message }}</p>
   <p>reversed message: {{ reversedMessage }}</p>
 </div>
</template>

<script>
export default {
 data () {
   return {
     message: 'Hello Nuxt.js!',
   }
 },
 computed: {
   reversedMessage () {
     let message = this.message = 'あいうえお'
     return message.split('').reverse().join('')
   },
 },
}
</script>

methodsとの違い

computedはプロパティ
methodsはメソッドなので
そもそも土台が違うのですが
似ていますよね?

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

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

P5.js 日本語リファレンス(applyMatrix)

このページでは「P5.js 日本語リファレンス」 の applyMatrix関数を説明します。

applyMatrix()

説明文

現在の行列にパラメータで指定された行列を乗算します。これは、平行移動、拡大縮小、剪断、回転をすべて同時に実行できる強力な操作です。ウィキペディアで変換マトリックスの詳細を学ぶことができます。

ここでの引数の命名は、WHATWG仕様の命名に従い、次の形式の変換行列に対応します。

2次元変換マトリックス

\begin{pmatrix}
a & c & e \\
b & d & f \\
0 & 0 & 1
\end{pmatrix}

2次元平行移動

  • e: x方向の平行移動量
  • f: y方向の平行移動量
\begin{pmatrix}
0 & 0 & e \\
0 & 0 & f \\
0 & 0 & 1
\end{pmatrix}

2次元拡大縮小

  • a: x方向の拡大縮小率
  • d: y方向の拡大縮小率
\begin{pmatrix}
a & 0 & 0 \\
0 & d & 0 \\
0 & 0 & 1
\end{pmatrix}

2次元回転

a: cos(回転角度)
b: sin(回転角度)
c: -sin(回転角度)
d: cos(回転角度)

\begin{pmatrix}
a & c & 0 \\
b & d & 0 \\
0 & 0 & 1
\end{pmatrix}

3次元変換マトリックス

\begin{pmatrix}
m11 & m21 & m31 & m41 \\
m12 & m22 & m32 & m42 \\
m13 & m23 & m33 & m43 \\
0 & 0 & 0 & 1
\end{pmatrix}

3次元平行移動

m41: x方向の平行移動量
m42: y方向の平行移動量
m43: z方向の平行移動量

\begin{pmatrix}
1 & 0 & 0 & m41 \\
0 & 1 & 0 & m42 \\
0 & 0 & 1 & m43 \\
0 & 0 & 0 & 1
\end{pmatrix}

3次元拡大縮小

m11: x方向の拡大縮小率
m22: y方向の拡大縮小率
m33: z方向の拡大縮小率

\begin{pmatrix}
m11 & 0 & 0 & 0 \\
0 & m22 & 0 & 0 \\
0 & 0 & m33 & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}

3次元x軸回転

m22: cos(回転角度)
m23: -sin(回転角度)
m32: sin(回転角度)
m33: cos(回転角度)

\begin{pmatrix}
1 & 0 & 0 & 0 \\
0 & m22 & m32 & 0 \\
0 & m23 & m33 & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}

3次元y軸回転

m11: cos(回転角度)
m13: sin(回転角度)
m31: -sin(回転角度)
m33: cos(回転角度)

\begin{pmatrix}
m11 & 0 & m31 & 0 \\
0 & 1 & 0 & 0 \\
m13 & 0 & m33 & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}

3次元z軸回転

m11: cos(回転角度)
m12: sin(回転角度)
m21: -sin(回転角度)
m22: cos(回転角度)

\begin{pmatrix}
m11 & m21 & 0 & 0 \\
m12 & m22 & 0 & 0 \\
0 & 0 & 0 & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}

構文

applyMatrix(a, b, c, d, e, f)

applyMatrix(m11, m12, m13, m21, m22, m23, m31, m32, m33, m41, m42, m43)

パラメタ

  • a - f
    Number:乗算する2x3行列を定義する数値

  • m11 - m43
    Number:乗算する3x4行列を定義する数値

例1 2次元の平行移動

function setup() {
  frameRate(10);
  rectMode(CENTER);
}

function draw() {
  let step = frameCount % 20;
  background(200);

  // 2次元の平行移動
  applyMatrix(        1,         0,
                      0,         1,
              40 + step, 30 + step);

  rect(0, 0, 50, 50);
}

実行結果

https://editor.p5js.org/bit0101/sketches/hnW37L2tv

例2 2次元の拡大縮小

function setup() {
  frameRate(10);
  rectMode(CENTER);
}

function draw() {
  let step = frameCount % 20;
  background(200);
  translate(50, 50);

  // 2次元の拡大縮小
  applyMatrix(1 / step,         0,
                     0,  1 / step,
                     0,         0);

  rect(0, 0, 50, 50);
}

実行結果

https://editor.p5js.org/bit0101/sketches/hqHo8r7DW

例3 2次元の回転

function setup()  {
  frameRate(10);
  rectMode(CENTER);
}

function draw()  {
  let step = frameCount % 20;
  let angle = map(step, 0, 20, 0, TWO_PI);

  background(200);
  translate(50, 50);

  // 2次元の回転
  applyMatrix( cos(angle), sin(angle),
              -sin(angle), cos(angle),
                        0,          0);

  rect(0, 0, 50, 50);
}

実行結果

https://editor.p5js.org/bit0101/sketches/AxKDBbXgO

例4 3次元の平行移動

function setup() {
  createCanvas(200, 200, WEBGL);
  frameRate(10);
  rectMode(CENTER);
}

function draw() {
  let dx = frameCount % 30;
  let dy = frameCount % 30;
  let dz = frameCount % 30;
  background(200);

  // 3次元x、y、z方向の平行移動
  applyMatrix(  1,   0,   0,   0,
                0,   1,   0,   0,
                0,   0,   1,   0,
               dx,  dy,  dz,   1);

  box(50);
}

実行結果

https://editor.p5js.org/bit0101/sketches/aHQKDWt8r

例5 3次元の拡大縮小

function setup() {
  createCanvas(200, 200, WEBGL);
  frameRate(10);
  rectMode(CENTER);
}

function draw() {
  let step = frameCount % 20;
  background(200);

  // 3次元のx、y方向の拡大縮小
  applyMatrix(1 / step,        0,  0,  0,
                     0, 1 / step,  0,  0,
                     0,        0,  0,  0,
                     0,        0,  0,  1);

  box(50);
}

実行結果

https://editor.p5js.org/bit0101/sketches/Bz2HX45eP

例6 3次元のx軸を中心とした回転

function setup() {
  createCanvas(100, 100, WEBGL);
  noFill() ;
}

function draw()  {
  background(200);
  rotateX(PI / 6);
  stroke(153);
  box(35);
  let rad = millis()  / 1000;

  // 3次元のx軸を中心とした回転
  applyMatrix( 1,        0,         0,  0,
               0, cos(rad), -sin(rad),  0,
               0, sin(rad),  cos(rad),  0,
               0,        0,         0,  1);

  stroke(255);
  box(50);
}

実行結果

https://editor.p5js.org/bit0101/sketches/rIg-BgThL

例7 3次元のy軸を中心とした回転

function setup() {
  createCanvas(100, 100, WEBGL);
  noFill() ;
}

function draw()  {
  background(200);
  rotateY(PI / 6);
  stroke(153);
  box(35);
  let rad = millis()  / 1000;

  // 3次元のy軸を中心とした回転
  applyMatrix( cos(rad),  0.0,  sin(rad),  0.0,
                    0.0,  1.0,       0.0,  0.0,
              -sin(rad),  0.0,  cos(rad),  0.0,
                    0.0,  0.0,       0.0,  1.0);

  stroke(255);
  box(50);
}

実行結果

https://editor.p5js.org/bit0101/sketches/Sd8nJItVe

例8 3次元のz軸を中心とした回転

function setup() {
  createCanvas(100, 100, WEBGL);
  noFill() ;
}

function draw()  {
  background(200);
  rotateZ(PI / 6);
  stroke(153);
  box(35);
  let rad = millis()  / 1000;

  // 3次元のz軸を中心とした回転
  applyMatrix(  cos(rad),  sin(rad),  0,  0,
               -sin(rad),  cos(rad),  0,  0,
                       0,         0,  0,  0,
                       0,         0,  0,  1);

  stroke(255);
  box(50);
}

実行結果

https://editor.p5js.org/bit0101/sketches/OexDrcluT

例9 平行移動とx軸回転の組み合わせ

function setup() {
  createCanvas(200, 200, WEBGL);
  frameRate(10);
  rectMode(CENTER);
}

function draw() {
  let dx = frameCount % 30;
  let dy = frameCount % 30;
  let dz = frameCount % 30;
  let rad = millis()  / 1000;

  background(200);

  // 3次元x、y、z方向の平行移動
  applyMatrix(  1,   0,   0,   0,
                0,   1,   0,   0,
                0,   0,   1,   0,
               dx,  dy,  dz,   1);

  // 3次元のx軸を中心とした回転
  applyMatrix( 1,        0,         0,  0,
               0, cos(rad), -sin(rad),  0,
               0, sin(rad),  cos(rad),  0,
               0,        0,         0,  1);


  box(50);
}

実行結果

https://editor.p5js.org/bit0101/sketches/TQM4XFV1W

著作権

p5.js was created by Lauren McCarthy and is developed by a community of collaborators, with support from the Processing Foundation and NYU ITP. Identity and graphic design by Jerel Johnson.

ライセンス

Creative Commons(CC BY-NC-SA 4.0) に従います。

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

textareaの高さを入力に合わせて自動調整する

地味に面倒くさい、textareaの高さ自動調整。

$("textarea").height( $("textarea")[0].scrollHeight );

※ 適時イベントハンドリングすること。

これぐらい、デフォルト仕様になればいいのにね。

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

Internet Explorerでは try { } catch { } は動かない

JavaScriptにて例外処理を書いていまして、ビックリしたんですけど、
この書き方(try { } catch { }の引数を省略した形)だと、EdgeとIEで動きませんでした。

try {
    throw new Error('error.');
} catch {
    console.log('error.');
}

エラーが出ました。

SCRIPT1005: SCRIPT1005: Expected '('

こう書かないと

try {
    throw new Error('error.');
} catch(e) {
    console.log('error.');
}

ChromeやFireFoxでは大丈夫だったのですが、catchの引数eが必要でした。
できれば省略したかったのに・・・。

バージョン
Microsoft Edge 44.18362.449.0
Internet Explorer 11.0.195

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

Visual Studio Code設定メモ

Extensions

改行コードを変更する

settings.json設定例
{
   "files.eol": "\n"
}

参考 :

タブインデント、空白を表示させる設定

settings.json設定例
{
    "editor.insertSpaces": false,
    "editor.renderWhitespace": "all"
}

参考 :

インデント幅を言語毎に指定する

settings.json設定例
{
    "[javascript]": {
      "editor.tabSize": 2
    },
    "[html]": {
        "editor.tabSize": 4
    }
}

参考 :

Smartyのデリミタを変更

やったこと
blockComment ["{", "}"], => [ "<{*", "*}>" ] へ修正
brackets ["{", "}"], => ["<{", "}>"], へ修正
autoClosingPairs , surroundingPairs["<{", "}>"], を追加

C:\Users\<USER NAME>\.vscode\extensions\imperez.smarty-0.3.0\smarty.configuration.json

smarty.configuration.json設定例
{
    "comments": {
        // symbols used for start and end a block comment. Remove this entry if your language does not support block comments
        "blockComment": [ "<{*", "*}>" ]  // [ "{*", "*}" ]から変更
    },
    // symbols used as brackets
    "brackets": [
        ["<{", "}>"],  // ["{", "}"],から変更
        ["[{", "}]"],
        ["[", "]"],
        ["(", ")"]
    ],
    "autoClosingPairs": [
+       ["<{", "}>"],
        ["{", "}"],
        ["[", "]"],
        ["(", ")"],
        ["\"", "\""],
        ["'", "'"],
        ["`", "`"]
    ],
    "surroundingPairs": [
+       ["<{", "}>"],
        ["{", "}"],
        ["[", "]"],
        ["(", ")"],
        ["\"", "\""],
        ["'", "'"],
        ["`", "`"]
    ]
}

参考 :

*.tplなどでEmmet(Html/CSSの入力補完)を効かせる

settings.json設定例
    "emmet.includeLanguages": {
        "smarty": "html"
    },

参考 :

自動アップデートを無効化する方法

C:\Users\<ユーザー名>\AppData\Roaming\Code\User\settings.json

settings.json設定例
{
    "update.channel": "none"
}

参考 :

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

Amplifyで、STSの認証を直接行う

Amazon CognitoのIDプールを使って外部プロバイダーがウェブベースの認証を提供する場合に、Cognitoが発行する認証の有効期限を伸ばすことがそのままでは難しかったので、その調査の記録を書いておきます。

そもそもの認証フロー

開発者認証or外部プロバイダーどちらを使ったとしても、Amplifyの認証を素直に使うと下の拡張認証フローになります。

スクリーンショット 2020-07-20 17.48.09.png

引用元:
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/authentication-flow.html

この図を見てもらうとわかるのですが、最終的な認証情報はAWS STSから渡ってきます。そして、AWS STSでは認証情報を発行する際にその有効期限を指定することができます。しかし、拡張認証フローだとAmazon CognitoがAWS STSとやり取りしてしまうため、有効期限を指定することが出来ず、デフォルトの有効期限(3600秒)が指定されてします。

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_temp_request.html#api_assumerolewithwebidentity

そこで、先程のページに有る、基本的な認証フローを使うことになります。

スクリーンショット 2020-07-20 17.55.11.png

引用元: https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/authentication-flow.html

見ての通り、AWS STSへ直接アクセスすることが出来ます。

実際のやり方

AmplifyでのCognito認証はすでに済んでいるものとし、AWS STSの認証に必要な下記のパラメータはすべて準備出来ているものとします。

  • (A) sts:AssumeRoleWithWebIdentityアクションが実行可能なIAM Role(そしてそのARN)
  • (B) 64字以下のセッション名として使う任意の文字列
  • (C) GetOpenIdToken...で発行されたトークン

上記がそろえば、あとはさっくりAWS SDKのAPIを叩くだけです。

const stsParams = {
  DurationSeconds:  900, // 最小900秒(15分)〜最大43200秒(12時間)まで設定することが出来ます。
  RoleArn:          `(A)の値`,
  RoleSessionName:  `(B)の値`,
  WebIdentityToken: `(C)の値`,
};
const sts = new AWS.STS();
const stsResponse = await sts.assumeRoleWithWebIdentity(stsParams).promise();
AWS.config.update({
  credentials: new AWS.Credentials({
    accessKeyId:     stsResponse.Credentials.AccessKeyId,
    secretAccessKey: stsResponse.Credentials.SecretAccessKey,
    sessionToken:    stsResponse.Credentials.SessionToken,
 }),
});

Amplifyの認証設定はいっさいやらないんかい!って感じですが、Amplifyのコードを見る限り、Credentialsクラスなどから認証情報を設定する方法はなさそうです。AWSのグローバルの認証情報を更新しておくと、Amplifyのもろもろはそれを見て勝手に認証してくれるようなので、この書き方に落ち着きました。

  • ※ Amplifyのバージョンによって、aws-sdkのバージョンが違ってくるので、この書き方はできない場合があります。
  • ※ この書き方はaws-sdk v2系を想定しています。
  • ※サーバー側のAPIを用意できるなら、CognitoのEPをそのままモックしてしまうほうが良いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む