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

お絵かきできるSNSを作りたい!9

お絵かきできるSNSを作りたい後半戦、下のメニューを作っていきます。

まずはPNGで保存機能。

function saveCanvas(){
    var src1_img = new Image();
    var src2_img = new Image();
    src1_img.src = $("#canvas")[0].toDataURL();
    src1_img.addEventListener('load', function() {
        src2_img.src = $("#canvas2")[0].toDataURL();
        src2_img.addEventListener('load', function() {
            var dst_canvas = document.createElement('canvas');
            dst_canvas.width = 800;
            dst_canvas.height = 600;
            dst_canvas.getContext("2d").drawImage(src1_img, 0, 0, 800, 600);
            dst_canvas.getContext("2d").drawImage(src2_img, 0, 0, 800, 600);

            var imageType = "image/png";
            var fileName = "download.png";
            // base64エンコードされたデータを取得 「data:image/png;base64,iVBORw0k~」
            var base64 = dst_canvas.toDataURL(imageType);
            // base64データをblobに変換
            var blob = Base64toBlob(base64);
            // blobデータをa要素を使ってダウンロード
            saveBlob(blob, fileName);
        }, false);
    }, false);
}

// Base64データをBlobデータに変換
function Base64toBlob(base64){
    // カンマで分割して以下のようにデータを分ける
    // tmp[0] : データ形式(data:image/png;base64)
    // tmp[1] : base64データ(iVBORw0k~)
    var tmp = base64.split(',');
    // base64データの文字列をデコード
    var data = atob(tmp[1]);
    // tmp[0]の文字列(data:image/png;base64)からコンテンツタイプ(image/png)部分を取得
    var mime = tmp[0].split(':')[1].split(';')[0];
    //  1文字ごとにUTF-16コードを表す 0から65535 の整数を取得
    var buf = new Uint8Array(data.length);
    for (var i = 0; i < data.length; i++) {
        buf[i] = data.charCodeAt(i);
    }
    // blobデータを作成
    var blob = new Blob([buf], { type: mime });
    return blob;
}

// 画像のダウンロード
function saveBlob(blob, fileName){
    var url = (window.URL || window.webkitURL);
    // ダウンロード用のURL作成
    var dataUrl = url.createObjectURL(blob);
    // イベント作成
    var event = document.createEvent("MouseEvents");
    event.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    // a要素を作成
    var a = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
    // ダウンロード用のURLセット
    a.href = dataUrl;
    // ファイル名セット
    a.download = fileName;
    // イベントの発火
    a.dispatchEvent(event);
}

無駄に関数が増えました。
レイヤーが2枚あるので1枚にがっちゃんこしてます。
[github]修正内容はこちら

次はフルスクリーンボタンの実装です。

var blFullscreen = true;
function doFullscreen(){
    var elem = document.getElementById("screen");
    if (blFullscreen){
        if (elem.requestFullscreen) {
          elem.requestFullscreen();
        } else if (elem.webkitRequestFullScreen) {
          elem.webkitRequestFullScreen();
        } else if (elem.mozRequestFullScreen) {
          elem.mozRequestFullScreen();
        } else if (elem.msRequestFullscreen) {
          elem.msRequestFullscreen();
        }
        blFullscreen = false;
    } else {
        if (document.webkitCancelFullScreen) {
            document.webkitCancelFullScreen();
        } else if (document.mozCancelFullScreen) {
            document.mozCancelFullScreen();
        } else if (document.msExitFullscreen) {
            document.msExitFullscreen();
        } else if(document.cancelFullScreen) {
            document.cancelFullScreen();
        } else if(document.exitFullscreen) {
            document.exitFullscreen();
        }
        blFullscreen = true;
    }
}

フルスクリーン状態の判定をJSで行おうとするとブラウザによって挙動が怪しくなり悩むくらいならとグローバルおじさんになりました。

あと細かい修正もしたので詳しくは↓こちらを。
[github]修正内容はこちら

そして、左右反転。
これがちょっと手間で、左右反転するとレイヤー1とレイヤー2のX座標も反転させています。
メインの関数は↓こちら。

var blReflect = true;
function doReflect(){
    if (blReflect){
        $("#canvas").css('transform', 'scale(-1, 1)');
        $("#canvas2").css('transform', 'scale(-1, 1)');
        blReflect = false;
    } else {
        $("#canvas").css('transform', 'scale(1, 1)');
        $("#canvas2").css('transform', 'scale(1, 1)');
        blReflect = true;
    }
}

あとは、押したとき、動かしている時、離したときに左右反転状態かを判定してX座標を左右逆になるようにしました。

        if (blReflect) {
            ox=event.clientX-event.target.getBoundingClientRect().left;
        } else {
            ox=$("#canvas").width() + (parseInt($("body").css('padding-left')) * 2) - event.clientX-event.target.getBoundingClientRect().left;
        }

[github]修正内容はこちら

今回最後は最初から書き直すの実装です。
これはページリロードでいいんじゃ・・?とも思いましたが、折角なのでちゃんと実装しました。

function doClear(){
    if(confirm('本当にいいんですね?')){
        ct.clearRect(0, 0, $("#canvas").width(), $("#canvas").height());
        ct2.clearRect(0, 0, $("#canvas2").width(), $("#canvas2").height());
    }
}

ちゃんとアラートで消すことを確認する新設設計。
レイヤー1とレイヤー2をクリアしています。

[github]修正内容はこちら

次回、Ctrl+Zをしたい。お楽しみに。

最初:お絵かきできるSNSを作りたい!

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

非同期通信の手順書②

前提条件

以下のブログを読んでから本記事をお読みください。
非同期通信の手始めに

createアクションに何を記入するか

何をjsonで返して、何をHTMLで返すか…
それはrespond_toで決めるのでした。以下、コードです。

  def create
    @comment = Comment.create(comment_params)
    respond_to do |format|
      format.html { redirect_to tweet_path(params[:tweet_id])  }
      format.json
    end
  end

気付く人は気付きますがrender json 〇〇がありません。
これはこの後にjbuilderを用いるためです。
次はjbuilderについて記述します。

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

とりあえずVue.jsのComposition APIで書くためのまとめ

自分含め、これまで Vue.js 2.x でコードを書いていた人に向けて書きます。
あまり深いところや細かいところまでは掘らずに、Vue.js 2.x で書いてきた基本的な記述をComposition APIで書くために、と言う視点で書いてみました。

目次

  • Composition API
  • Vue.js 2.xで慣れ親しんだ書き方からの移行
  • ロジックの抽出と再利用

Composition API

これまでのVue.jsでは data computed methods などのoptionごとにリアクティブなデータや関数などを書いてきたが(Options APIというらしい)、大きなコンポーネントになってくると必要な機能ごとにコードをまとめた方がいい。そのための関数ベースのAPI群。

Vue.jsのv3から利用可能予定。
v2でもプラグインとして利用可能。

現在はRFCとして仕様を揉んでいるところなのでここに記載の内容は変更される可能性があります。
https://vue-composition-api-rfc.netlify.app/

メリット

  • 自由なコードの構成
  • ロジックの抽出と再利用
  • 型推論の強化

これまでのVue.jsのOptions APIを上書きするのものではなく、上記のメリットを受けられるような大規模なプロジェクトで使うのが良い。
Options APIはそれ自体がコードの規約となるので小・中規模なプロジェクトでとても有用と思う。

setup()

setupは新しいOptionで、コンポーネント内でComposition API を使うときのエントリーポイントになります。
setup関数内にリアクティブなデータや算出プロパティ、メソッドなどを定義していきます。

<template>
 ...
</template>

<script>
import { computed, ref, reactive } from 'vue' // 必要なAPIはimportが必要
export default {
  setup() {
    // 
  }
}
</script>

<style>
 ...
</style>

setup関数内で使うcomputed, ref, reactive などの関数(後述)はimportしておく必要があります。

import { computed, ref, reactive } from 'vue'

templateで使うものは return { } が必要

templateで使う値や関数はsetup関数の最後でreturnしておく必要がある。
下の例でいうとreturnされた count などは、いままでのOptions APIで言うところの this.count のような形でぶら下ります。
いちいちこれを書く手間がある一方、templateでどの値/関数が使われているかがわかりやすいというメリットもあります。

<template>
  <div>
    {{ count }} * 2 = {{ double }}
    <button @click="increment">+</button>
  </div>
</template>

<script>
import { computed, ref, reactive } from 'vue'
export default {
  setup() {
    const count = ref(0)
    const double = computed(() => {
      return count.value * 2
    }

    const increment = () {
      count.value++
    }

    // template で出力するデータはここで渡す必要がある
    return { count, double, increment }
  }
}
</script>

Options APIからCompositio APIへの移行

これまで慣れ親しんだOptions APIでの書き方をComposition APIで書くときのまとめです。
datacomputedなどoptionごとに書いてます。

data

ref()、またはreactive()でリアクティブなデータを扱うことができます。

ref()

ref関数は数値や文字列、booleanなどのプリミティブ値を引数にとり、リアクティブなrefオブジェクトを返す
setup関数内でリアクティブな値を使う場合は.valueプロパティを参照します。

<template>
  <div>
    {{ count }}
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  setup() {
    const count = ref(0) // リアクティブな値をもつオブジェクトを返す
    console.log(count.value) // 0

    count.value++
    console.log(count.value) // 1

    const increment = () => {
      count.value++
    }
    return { count } // return する場合はリアクティブなrefオブジェクトごと返す。 .value は不要
  }
}
</script>

reactive()

オブジェクトを引数にとり、リアクティブプロキシーを返します。Vue.js 2.x系のVue.observable()と等しいようです。
(参考:Proxy - MDN web docs)

The reactive conversion is "deep": it affects all nested properties. In the ES2015 Proxy based implementation, the returned proxy is not equal to the original object. It is recommended to work exclusively with the reactive proxy and avoid relying on the original object.

<template>
  <div>
    {{ state.count }}
    {{ state.code }}
  </div>
</template>

<script>
import { reactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      count: 1,
      code: 'ready'
    }

    state.count++ // 2 refオブジェクトと違い.valueを参照しない

    return { state }
  }
}
</script>

computed

算出プロパティはcomputed関数を使います。
引数にgetter関数をとり、イミュータブルなrefオブジェクトを返します。
templateで使う場合はもちろんreturnする必要があります。

import { ref, computed } from 'vue'
export default {
  setup() {
    const count = ref(2) 
    count.value++ // 3

    const double = computed(() => {
      return count.value * 2
    })
    console.log(double.value) // 6
    double.value++ // error

    return { double }
  }
}

methods

Composition APIのなかでmethodsは単純なJavaScriptの関数として宣言します。
template内で利用する場合はリアクティブな値と同様、setup関数の最後にreturnしてあげる必要があります。

<template>
  <div>
    Count is {{ count }} 
    <button @click="increment">+</button>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  const count = ref(1)

  // 単純なJSの関数として宣言する
  const increment = () => {
    count.value++
  }

  return { count, increment }
}
</script>

props

これまで同様、setup関数の外で指定。setup(props)のように引数に指定し使う。setupに渡されたpropsオブジェクトはリアクティブであり、

export default {
  props: {
    id: string
  },
  setup(props) {
    // props.id
  }
}

ただしpropsオブジェクトからプロパティだけを取り出すとリアクティビティは失われます。

export default {
  props: {
    id: Number
  },
  setup({ id }) {
    watchEffect(() => {
    console.log(`id is: ` + id) // リアクティブではなくなる。
    })
  }
}

created(), mounted(), updated()

RFCのリファレンスには以下のような対応表があります。
https://vue-composition-api-rfc.netlify.app/api.html#lifecycle-hooks

  • beforeCreate -> setup()を使う
  • created -> setup()を使う
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

created()

setup関数はpropの初期値が解決された後、beforeCreatedフックとcreatedフックの前に呼ばれます。
そのためsetup自体をbeforeCreated, createdとして扱うのがいいようです。

なお、setupはいままでのcreatedよりも前に解決されるため、

  • setup関数内からはこれまでの data() のプロパティやmethodsの関数にアクセスできない
  • 逆にsetupでreturn された値/関数はcreated() やmethodsの中で this.xxxの表記でアクセスできる。

となります。

mounted(), updated()

created, beforeCreated以外のライフサイクルフック、例えばmounted, updatedなんかは
onXxxxの形でimportした上でsetup関数内でonMounted() onUpdated() のように使います。

import { onMounted, onUpdated } from 'vue'
export default {
  setup() {
    onMounted(() => {
      console.log('mounted.')
    }
    onUpdated(() => {
      console.log('updated.')
    })
  }
}

そのほかのライフサイクルフック

beforeMount(), beforeUpdate(), beforeDestroy(), destroyed(), errorCaptured()などのそのほかのLife Cycle Hookも同様に関数をimportしてsetup内で使います。

import { onBeforeMount, onBeforeUpdate, onBeforeUnmount, onUnmounted, onErrorCaptured   } from 'vue'
export default {
  setup() {
    onBeforeMount(() => { ... }) // = beforeMount()
    onBeforeUpdate(() => { ... }) // = beforeUpdate()
    onBeforeUnmount(() => { ... }) // = beforeDestroy()
    onUnmounted(() => { ... }) // = destroyed()
    onErrorCaptured(() => { ... }) // = errorCaptured()
  }
}

emit, attrs, slots

emit, attrs, slotsは、setup関数の第二引数に渡されるcontextオブジェクトを通してアクセス可能です。

export default {
  setup(props, context) {
    context.attrs
    context.slots
    context.emit
  }
}

ロジックの抽出と再利用

Composition APIを使うメリットの一つにロジックの抽出とその再利用があります。

いくつかのコンポーネント間で利用されるような機能のロジックや、膨大で複雑なコードの機能を切り出して再利用することができ、見通しがよく効率的なコードが書けます。
ロジックを抽出して再利用する際にも、Composition APIのコード構成に対する非常に高い柔軟性が活きてきます。

以下の例はクリックで増えるカウントに対する機能を抽出しています。

// count.vue 
import { ref, computed, onMounted } from 'vue'

export function count() {
  const count = ref(0)

  const update = (e) => {
    count.value++
  }

  const displayCount = computed(() => {
    return status.value = count.value >= 10 ? '10+' : count.value
  })

  onMounted(() => {
    window.addEventListener('click', update)
  })

  return { cont, displayCount }
}

抽出したcount関数を利用します

import { count } from './count'

export default {
  setup() {
    const { count, displayCount } = count()

    return { count, displayCount }
  }
}

終わりに

Compositio APIの基本的な書き方、そしてこれまでのVue.jsに慣れ親しんだ人からの目線での書き方をまとめてみました。

Composition APIにはここで紹介していないAPIがまだあります。
冒頭のメリットでも触れたように、自由なコード構成、型サポートの強化、ロジックの抽出など、特に大規模なプロジェクトでは非常に可能性を感じさせる新機能となることでしょう。

一方、RFCである現在(2020/06)はAPIの追加や破壊的変更がされる可能性があります。
そのため業務での利用はまだ難しいですが、
来るべきVue.js 3.0の正式リリースに備え今から概要を掴んでいくのは良いかなと思います。

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

Nightwatch で E2E テストをしたいので Bing 検索のテストを書いてみた

Vue CLI で e2e テストを有効にすると入ってくる Nightwatch ですが、こんな感じで使うよ~というメモです。

公式サイト

こちらです。よくまとまったドキュメントもあります。

https://nightwatchjs.org/

Bing で検索するテストを書いてみよう

サンプルとしては、ecosia のサイトの検索とか Google の検索とかがあります。
プレーンな API を使った例としては Nightwatch の公式を開いた時点でコード例が上がってます。

image.png

ただ、実際に画面系のテストをするときは Page Object Pattern を少なくとも使わないとテストコード内にページの内部実装に依存したコードが散らばって大変なことになります。なので、Page Object の機能を使って Bing で Microsoft と検索して、検索結果のページのテキストボックスに Microsoft という文字列がちゃんと入っているか確認するというテストを書いてみようと思います。

とりあえず必要最低限だけだと以下のドキュメントを見るといいかも。

Chai の expect とかも使えるみたいなので、興味があったらこっちのドキュメントも見ておくといいかもです。

Chai の expect って可読性のための Chain が

  • to
  • be
  • been
  • is
  • that
  • which
  • and
  • has
  • have
  • with
  • at
  • of
  • same
  • but
  • does
  • still

とあって、個人的には英語圏の人じゃないとフルの恩恵って難しいような気がするんだけどどうだろう?(英語弱者の意見

というわけで Nightwatch で Bing 検索してみようと思います。

プロジェクトの作成

以下のコマンドでプロジェクトの作成と必要な依存関係をインストールします。

$ npm init -y
$ npm install --save-dev nightwatch chromedriver

nightwatch.conf.js を以下のような内容で作ります。chrome を使うということと、テスト用のソースコードは tests/e2e フォルダーだということを設定しています。その他にもカスタムコマンド(今回は使わないのでコメントアウト)、カスタムアサーション(今回は使わないのでコメントアウト)、ページオブジェクトのパスを指定しています。

nightwatch.conf.js
const chrome = require('chromedriver')

module.exports = {
    src_folders: ['tests/e2e'],
    //custom_assertions_path: ['tests/custom-assertings'],
    //custom_commands_path: ['tests/custom-commands'],
    page_objects_path: ['tests/page-objects'],
    webdriver: {
        start_process: true,
        server_path: chrome.path,
        port: 9515,
    },
    test_settings: {
        default: {
            desiredCapabilities: {
                browserName: 'chrome',
            },
        },
    },
}

では、tests/page-objects/bingTop.js に Bing のトップページのページオブジェクトを作っていきましょう。
ページオブジェクトではセクションというものを使って、いい感じにページを領域単位に区切って管理することが出来ます。例えばヘッダーやメニューや入力フォームやレポート出力領域のような感じです。

セクションを使わずにべたっとエレメントを定義することも出来ますが、小さな画面じゃない限りはセクション区切った方が可読性的にはいいと思います。セクションの使い方を示すために、今回の Bing のトップページはセクションいらないくらいシンプルなのですが、あえてセクションを定義しています。

tests/page-objects/bingTop.js
module.exports = {
    url: 'https://bing.com?cc=jp',
    sections: {
        search: {
            selector: '',
            elements: {
                input: {
                    selector: '',
                },
                submit: {
                    selector: '',
                },
            },
        },
    },
};

とりあえず操作項目としては、検索入力エリアと検索ボタンがあればいいので、それを定義しました。selector を指定していないので、Chrome か Chronium Edge で bing を開いて開発者ツールを開いて要素のツリーでセレクターがゲットしたいエレメントで右クリックしてコピー→セレクターでセレクターをゲットします。

image.png

セレクターを入れるとこんな感じです。

tests/page-objects/bingTop.js
module.exports = {
    url: 'https://bing.com?cc=jp',
    sections: {
        search: {
            selector: 'body > div.hp_body > div.hp_cont',
            elements: {
                input: {
                    selector: '#sb_form_q',
                },
                submit: {
                    selector: '#sb_form > label',
                },
            },
        },
    },
};

これでテストコードからは、セレクターを指定せずにセクション名やエレメント名でアクセスできます。ページの構造が変わってもページの仕様が大きく変わらなければページオブジェクトの修正だけで対応できます。

後は、コマンドも定義できて例えば今回の例だと検索ボタンを押すとかいうのを以下のようにコマンドで定義できます。

tests/page-objects/bingTop.js
const bingCommands = {
    wait: function() {
        this.section.search.waitForElementVisible('@input');
    },
    submit: function() {
        this.section.search.waitForElementVisible('@submit')
            .click('@submit')
            .waitForElementNotPresent('@submit');
    },
};

module.exports = {
    commands: [bingCommands],
    url: 'https://bing.com?cc=jp',
    sections: {
        search: {
            selector: 'body > div.hp_body > div.hp_cont',
            elements: {
                input: {
                    selector: '#sb_form_q',
                },
                submit: {
                    selector: '#sb_form > label',
                },
            },
        },
    },
};

同じ要領で検索結果のページのページオブジェクトも tests/page-objects/bingSearchResult.js に以下のように定義しました。

tests/page-objects/bingSearchResult.js
module.exports = {
    sections: {
        header: {
            selector: '#b_header',
            elements: {
                input: {
                    selector: '#sb_form_q',
                },
            },
        },
    },
};

ページが出来たのでテストを書いていきます。tests/bingtest.js に以下のように書きました。

これで、npx nightwatch というコマンドを叩くとブラウザーが起動して画面に文字をうって操作してアサートしてくれます。後は、ものによっては Nightwatch のページオブジェクトのエレメントとかは、日本語にしてもいいかもですね。以下のように。

tests/page-objects/bingSearchResult.js
const bingSearchResultCommands = {
    wait: function() {
        this.section.header.waitForElementVisible('@検索欄');
    }
};

module.exports = {
    commands: [bingSearchResultCommands],
    sections: {
        header: {
            selector: '#b_header',
            elements: {
                '検索欄': {
                    selector: '#sb_form_q',
                },
            },
        },
    },
};
tests/page-objects/bingTop.js
const bingCommands = {
    wait: function() {
        this.section.search.waitForElementVisible('@検索欄');
    },
    submit: function() {
        this.section.search.waitForElementVisible('@submit')
            .click('@submit')
            .waitForElementNotPresent('@submit');
    },
};

module.exports = {
    commands: [bingCommands],
    url: 'https://bing.com?cc=jp',
    sections: {
        search: {
            selector: 'body > div.hp_body > div.hp_cont',
            elements: {
                '検索欄': {
                    selector: '#sb_form_q',
                },
                submit: {
                    selector: '#sb_form > label',
                },
            },
        },
    },
};

こうすると、テストコードは以下のようになります。チームでのルール決めですが日本オンリーならありかも。

tests/e2e/bingtest.js
module.exports = {
    beforeEach: function(browser) {
        browser.page.bingTop()
            .navigate()
            .wait();
    },
    '検索語を入力して検索結果の画面に遷移': function(browser) {
        const bingTop = browser.page.bingTop();
        bingTop.section.search.setValue('@検索欄', 'Microsoft');
        bingTop.submit();

        const bingSearchResult = browser.page.bingSearchResult();
        bingSearchResult.wait();
        bingSearchResult.section.header.assert.value('@検索欄', 'Microsoft');
        browser.end();
    },
    '検索語を入力して検索結果の画面に遷移(日本語版)': function(browser) {
        const bingTop = browser.page.bingTop();
        bingTop.section.search.setValue('@検索欄', 'マイクロソフト');
        bingTop.submit();

        const bingSearchResult = browser.page.bingSearchResult();
        bingSearchResult.wait();
        bingSearchResult.section.header.assert.value('@検索欄', 'マイクロソフト');
        browser.end();
    },
};

動かしてみた結果は以下のような感じです。

e2e.gif

まとめ

Nightwatch を使うと割とさくっと簡単に E2E テストが出来そうですね。
GitHub Actions の Windows マシンに入ってるブラウザーのリストにも Chrome がいるので、GitHub Actions でも気軽に走らせることが出来るかも。

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

【Nuxt.js】Nuxt文法編:v-for

前置き

文法シリーズ第二弾
今回は定番のv-forです!

文法は覚えましょう?
ある程度の構文・単語は
覚える必要があります?
英文法・英単語を覚えないと
文が作れないのと同じです??

❓どんな時に使うか
配列やオブジェクトを
listで描写させる時に使います。
https://note.com/aliz/n/nda7438249ca8

v-for

https://qrunch.net/@rokujiro/entries/ylmWdFAZsaAtMTpq?ref=qrunch

【解説】
・v-for="i in 6"
 forといえば繰り返しですね。
 数字の6までの1つ1つをiとし
 それを繰り返しています。
・:key
 ここだと分かりにくいので
 後半に解説します。

index.vue
<template>
 <div class="page">
   <ul>
     <li
       v-for="i in 6"
       :key="i"
     >
       {{ i }}
     </li>
   </ul>
 </div>
</template>

【表示】
スクリーンショット 2020-06-01 13.21.56.png

配列の繰り返し

【解説】
・v-for="item in items"
 配列のitemsにある
 1つ1つのオブジェクトをitemと名付けます。
 in の代わりに of も使用できます?
・:key (v-bind: key)
 プロパティ= key
 1つ1つの要素がitemなので
 item.messageになります?
 (itemの中のmessageプロパティ)
 ⏬左がプロパティ: 右が値です!
 message: 'Foo',
 message: 'Bar',
 v-forには基本的に一意のkeyを指定します。
 要素を効率よく追跡し、
 パフォーマンス向上のためです。
 一意については
 オブジェクトのv-forで解説しています?
 ?keyによる再利用可能な要素の制御
  https://note.com/aliz/n/n76280a0e3a02/edit
・{{ item.message }}
 itemsの1つ1つの要素をitemとしたので
 itemのmessageプロパティの値を表示
Frame 1.png

index.vue
<template>
 <div class="page">
   <ul>
     <li<img width="665" alt="スクリーンショット 2020-06-01 10.48.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469755/3923d8d8-bbcd-4d8e-309d-5673130b1fea.png">

       v-for="item in items"
       :key="item.message"
     >
       {{ item.message }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     items: [
       { message: 'Foo' },
       { message: 'Bar' },
     ]
   }
 },
}
</script>

【表示】
スクリーンショット 2020-06-01 10.48.48.png

配列番号のサポート

v-forは配列[ ]のインデックス(配列番号)を
第二引数としてサポートしています?
配列番号は0からスタートします!

index.vue
<template>
 <div class="page">
   <ul>
     <li
       v-for="(item, index) in items"
       :key="item.message"
     >
       {{ parentMessage }} - {{ index }} - {{ item.message }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     parentMessage: 'Parent',
     items: [
       { message: 'Foo' },
       { message: 'Bar' },
     ]
   }
 },
}
</script>

【表示】
スクリーンショット 2020-06-01 13.05.07.png

【解説】

v-forで繰り返すのはitem.messageのみ
そのため配列0,1の2つが表示されます。

オブジェクトのv-for

配列だけでなくオブジェクトにも使えます?

index.vue
<template>
 <div class="page">
   <ul>
     <li v-for="value in object">
       {{ value }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     object: {
       title: 'How to do lists in Vue',
       author: 'Jane Doe',
       publishedAt: '2016-04-10'
     }
   }
 },
}
</script>

【表示】

スクリーンショット 2020-06-01 13.31.31.png

?オブジェクトでも配列と同様に
 keyと配列番号が使用できます!

【index.vue: keyを使用】
バラバラのプロパティ名ですが、
これらのkeyをnameと名付けて使用しています?

index.vue
<template>
 <div class="page">
   <ul>
     <li
      v-for="(value, name) in object"
      :key="name"
     >
       {{ name }} : {{ value }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     object: {
       title: 'How to do lists in Vue',
       author: 'Jane Doe',
       publishedAt: '2016-04-10'
     }
   }
 },
}
</script>

【表示】
スクリーンショット 2020-06-01 13.36.37.png

【index.vue: 配列番号indexを使用】

index.vue
<template>
 <div class="page">
   <ul>
     <li
      v-for="(value, name, index) in object"
      :key="name"
     >
       {{ index }}. {{ name }} : {{ value }}
     </li>
   </ul>
 </div>
</template>

<script>
export default {
 data () {
   return {
     object: {
       title: 'How to do lists in Vue',
       author: 'Jane Doe',
       publishedAt: '2016-04-10'
     }
   }
 },
}
</script>

【表示】
スクリーンショット 2020-06-01 13.44.42.png

:keyに使う値

❌非プリミティブ値
 オブジェクトや配列
⭕️プリミティブ値
 文字列や数字

【解説】
・:key="index"
 indexは配列番号のため非プリミティブ値
 配列は追加したり削除したりで
 対の値がズレることがあるので
 予期せぬ挙動になることがあります?

【❌index.vue: NG例】

index.vue
<template>
 <div class="page">
   <ul>
     <li
       v-for="(todo, index) in todos"
       :key="index"
       @click="deleteTodo(index)"
     >
       {{ todo.value }}
     </li>
   </ul>
 </div>
</template>

<script>
import Vue from 'vue'

export default {
 data () {
   return {
     todos: [
       { id: 1, value: '記事を書く' },
       { id: 2, value: '投稿する' },
       { id: 3, value: 'ツイートする' },
     ]
   }
 },
 methods: {
   deleteTodo (index) {
     this.$delete(this.todos, index)
   },
 },
}
</script>

【解説】
・:key="todo.id"
 idは文字列のためプリミティブ値
 todos配列の中のオブジェクト1つ1つがtodo
 そのtodoにあるプロパティidと
 valueを含めてidと名付けています?

【補足】
・$delete
 VueのグローバルAPI
 Vue.deleteを使用しています!
 そのためvueのimportが必要です?
 Nuxtで使う際には$deleteと書きます✍️

【⭕️index.vue: OK例】

index.vue
<template>
 <div class="page">
   <ul>
     <li
       v-for="(todo, index) in todos"
       :key="todo.id"
       @click="deleteTodo(index)"
     >
       {{ todo.value }}
     </li>
   </ul>
 </div>
</template>

<script>
import Vue from 'vue'

export default {
 data () {
   return {
     todos: [
       { id: 1, value: '記事を書く' },
       { id: 2, value: '投稿する' },
       { id: 3, value: 'ツイートする' },
     ]
   }
 },
 methods: {
   deleteTodo (index) {
     this.$delete(this.todos, index)
   },
 },
}
</script>

記事が公開したときにわかる様、
フォローをお願いします??

https://twitter.com/aLizlab

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

【Nuxt.js】Nuxt文法編:v-if, v-else, v-else-if

前置き

今までの記事で
何度も使ってきたv-if!
記事ごとに解説はしていましたが
v-if自体の記事はない??
ということが、ちらほら。。。
そこで文法の記事を投稿していきます??
今後v-forやv-modelなどを
更新していくのでお楽しみに♪

❓どんな時に使うか
表示/非表示の切り替えに使います!
buttonをclickしてモーダルなどの
表示/非表示を切り替えたりします。
https://note.com/aliz/n/n2f0bc857defb

v-if

ディレクティブの式が
真の時のみブロックが描写されます!
単一の要素にv-ifをつけます。

index.vue
<template>
 <div class="page">
   // h1タグの描写をtitleの真偽値で切り替え
   <h1 v-if="title">
    titleがtrueの時のみ表示
   </h1>
 </div>
</template>

<script>
export default {
 data () {
   return {
     // trueなので描写
     title: true,
   }
 },
}
</script>

【表示】

?true?
h1タグが描写されます。
スクリーンショット 2020-05-26 16.50.15.png

?false?
title: falseにした場合
h1タグが描写されません。
スクリーンショット 2020-05-26 16.50.59.png

? ! を使うと…
 真偽値を逆にすることができます!

index.vue
<template>
 <div class="page">
   // titleがfalseですが!で逆にするのでtrueになり描写されます
   <h1 v-if="!title">
    titleがtrueの時のみ表示
   </h1>
 </div>
</template>

<script>
export default {
 data () {
   return {
     title: false,
   }
 },
}
</script>

?複数の要素を
 切り替えたい場合

 →親要素にv-ifをつけてあげます?

index.vue
<template>
 <div class="page">
   <p>親要素ごと描写が切り替わる</p>
   <div v-if="titles">
     <h1>
       Hello Nuxt.js!
     </h1>
     <h2>
       Hello Nuxt.js!
     </h2>
     <h3>
       Hello Nuxt.js!
     </h3>
   </div>
 </div>
</template>

<script>
export default {
 data () {
   return {
     titles: false,
   }
 },
}
</script>

【表示】
スクリーンショット 2020-05-26 17.17.01.png

v-else

v-if="true":v-ifのみ描写
v-if="false":v-elseのみ描写

v-ifとセットで使います!
v-elseはv-ifもしくはv-else-ifの
直後に書きましょう!
それ以外は認識されません??

index.vue
<template>
 <div class="page">
   <h1 v-if="title">
     Hello Nuxt.js!
   </h1>
   // titleがfalseのためv-elseが描写
   <h1 v-else>
     v-ifがfalseの場合に表示
   </h1>
 </div>
</template>

<script>
export default {
 data () {
   return {
     title: false,
   }
 },
}
</script>

?v-ifに直接関数を入れてみましょう
 Math.random()で
 0〜1の数字をランダムで作り
 0.5より上ならv-ifがtrueとなります。
 より、なので0.5は含みません。
 数字は確認したいので
 Mustache構文(二重中括弧)で
 表示させておきます?

index.vue
<template>
 <div class="page">
   {{ Math.random() }}
   <h1 v-if="Math.random() > 0.5">
     Hello Nuxt.js!
   </h1>
   <h1 v-else>
     v-ifがfalseの場合に表示
   </h1>
 </div>
</template>

<script>
export default {
}
</script>

【表示】

?true?
スクリーンショット 2020-05-26 17.45.52.png

?false?
スクリーンショット 2020-05-26 17.44.58.png

v-else-if

複数の条件を指定したい時に使います?
こちらもv-ifもしくはv-else-ifの
直後に書きましょう!

index.vue
<template>
 <div class="page">
   <h1 v-if="type === 'A'">
     A
   </h1>
   <h1 v-else-if="type === 'B'">
     B
   </h1>
   <h1 v-else-if="type === 'C'">
     C
   </h1>
   <p v-else>
     ABCどれにも当てはまりません
   </p>
 </div>
</template>

<script>
export default {
 data () {
   return {
     type: 'E'
   }
 },
}
</script>

keyによる再利用可能な要素の制御

効率的に描写するために
要素を再利用することがよくあります。
例えばこちらのコード。
v-ifとv-elseでinputを分けています?
toggleボタンでinputを切り替えます?

index.vue
<template>
 <div class="page">
   <label v-if="loginType === 'username'">
     Username
     <input placeholder="Enter your username">
   </label>
   <label v-else>
     Email
     <input placeholder="Enter your email address">
   </label>
   <button
     @click="toggle"
   >
     toggle
   </button>
 </div>
</template>

<script>
export default {
 data () {
   return {
     loginType: 'username'
   }
 },
 methods: {
   toggle () {
     if (this.loginType === 'username') {
       this.loginType = 'email'
     } else {
       this.loginType = 'username'
     }
   },
 },
}
</script>

【表示】
toggleを押しても
inputに入力したものが残ってます。
切り替えができていません。
placeholderは変わっていますが、
input自体が再利用されています?
この場合の再利用は
望ましくありません?
input.gif

【key属性】
そこでkey属性の出番!
Vueで使える特別な属性のことです?
これを追加することで
「別物だよ!再利用しないでね!」
と伝えることができます?

index.vue
<template>
 <div class="page">
   <label
     v-if="loginType === 'username'"
   >Username
     <input
       placeholder="Enter your username"
       // inputkey属性を追加
       key="username-input"
     >
   </label>
   <label v-else>Email
     <input
       placeholder="Enter your email address"
       // inputkey属性を追加
       key="email-input"
     >
   </label>
   <button
     @click="toggle"
   >
     toggle
   </button>
 </div>
</template>

<script>
export default {
 data () {
   return {
     loginType: 'username'
   }
 },
 methods: {
   toggle () {
     if (this.loginType === 'username') {
       this.loginType = 'email'
     } else {
       this.loginType = 'username'
     }
   },
 },
}
</script>

【表示】
input2.gif

inputが最初から
描写されていますね✨?
※ inputはkey属性を持ちますが
 labelにはないため
 labelは再描写されています!

記事が公開したときにわかる様、
フォローをお願いします??

https://twitter.com/aLizlab

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

jQuery Dragg&Drop テンパズル

See the Pen zYrOMRa by 岩崎麻美 (@brbhdnra) on CodePen.

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

VSCode の Prettier でファイル毎にフォーマットを変える

プロジェクトのルートに .prettierrc を作成して、下記のように記述
下記の場合は、プロジェクト内の SCSS ファイルはシングルクォートを使って、 JS ファイルはダブルクォートを使いたい場合。

{
  "overrides": [
    {
      "files": "*.scss",
      "options": {
        "singleQuote": true
      }
    },
    {
      "files": "*.js",
      "options": {
        "singleQuote": false
      }
    }
  ]
}

.prettierrc

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

JSのお勉強 その1

自分用に、、結構調べ直したりが多いので 汗

JSの記法

記法名 主な用途 使用例
キャメルケース 変数/関数名 lastName
Pascal記法 クラス・コンストラクター名 LastName
アンダースコア 定数名 LAST_NAME , last_name

マジックナンバー

意味を持たない数字(ぱっと見なんの値かわからないやつ)
消費税計算の 1.1 とかがいきなりコード内に記述されてても、
読み解かないとなんの値かわからない、こういう数字。

テンプレート文字

`こんにちは、${name}さん。`

これで変数を + を使わずに文字列に入れられる。

オブジェクトリテラル記法

let obj = { x:1 , y:2 , z:3 };
obj.x     //結果は1
obj['x']; //結果は1

obj.x、の x は識別子の命名規則(頭に数字使えない)に沿ってるので数字の指定はNG

演算子

演算子 意味 使用例
+ 数値の加算 1 + 1 //2
- 数値の減算 1 - 2 //1
* 数値の乗算 2 * 2 //4
/ 数値の除算 10 / 5 //2
% 除算したあまり 10 % 4 //2
++ *前置加算 a = 3; b = ++a //bは4になる
++ *後置加算 a = 3; b = a++ //bは3になる
-- *前置減算 a = 3; b = --a //bは2になる
-- *後置減算 a = 3; b = a-- //bは3になる

*前置加算では a1 を加算してからbに代入、後置加算では ab に代入してから a+1 してるので結果が異なる。
 後置加算の ab の結果は a4b3 になる。

代入演算子

演算子 意味 使用例
= 変数へ値を代入 x = 1
+= 加算したものを代入 x = 1; x += 2 //3
-= 減算したものを代入 x = 3; x -= 1 //2
*= 乗算したものを代入 x = 2; x *= 2 //4
/= 除算したものを代入 x = 6; x /= 3 //2
%= 除算したあまりを代入 x = 7; x %= 3 //1
**= べき算を代入 x = 7; x **= 3 //xは7の3乗なので、 343 になる
&= ※論理積演算した結果を代入 a = 3; b = a++ //bは 3 になる
|= ※論理和演算した結果を代入 a = 3; b = --a //bは 2 になる
^= ※排他的論理和演算した結果を代入 a = 3; b = a-- //bは 3 になる
<<= ※左の値を右の値だけ左シフトした結果を代入 a = 3; b = a-- //bは 3 になる
>>= ※左の値を右の値だけ右シフトした結果を代入 a = 3; b = a-- //bは 3 になる
>>>= ※左の値を右の値だけ右シフトした結果を代入 a = 3; b = a-- //bは 3 になる

※この辺りはちょっとまだ理解できてない、、難しい。。

定数の再代入

//NG例1
const hoge = 100;
hoge = 200;

//NG例2
const hoge = [100,200,300];
hoge = [500,600,700];

//OK例
const hoge = [100,200,300];
hoge[0] = 500;

イメージとして、const hoge = この右側はhogeがみてる参照先

NG例1はhogeの参照先がかわるのでダメ、
NG例2もhogeの参照先がかわってるのでダメ、OK例では参照先が変わらず、
参照先の値だけを変更してるので、OK。
まー基本的には定数は変更しない。

分割代入

配列/オブジェクトを分解して配下の要素/プロパティ値を個々の変数に分解するための構文。
むつかしい・・・とりあえず下記で。。

配列

今までの配列の値の取り方

var hoge = [10 , 20 , 30 , 40 , 50 , 60 , 70];
var x0 = hoge[0];
var x1 = hoge[1];
var x2 = hoge[2];

分割代入での値の取り方

let hoge = [10 , 20 , 30 , 40 , 50 , 60 , 70];
let [x0 , x1 , x2 , x3 , x4 , x5 , x6] = hoge

console.log(x0) //この結果は 10 になる

分割代入での値の取り方(応用)

let hoge = [10 , 20 , 30 , 40 , 50 , 60 , 70];
let [x0 , x1 , x2 , x3 , ...other] = hoge

console.log(x0) //この結果は 10 になる
console.log(x1) //この結果は 20 になる
console.log(x2) //この結果は 30 になる
console.log(x3) //この結果は [40 , 50 , 60 , 70] になる

オブジェクト

分割代入での値の取り方

let hoge = {name: '名前' , age: '年齢' , address: '住所'};
let {age , name} = hoge

console.log(age) //この結果は 年齢 になる

配列と違って、プロパティ名で紐づけるので、順番が変わっても大丈夫。
また、分割代入で、 [name: '名前' , age: '年齢' , address: '住所'] に対して、
let [age , name] = hoge とすると、address は無視される。

分割代入での値の取り方(入れ子)

let hoge = {name: '名前' , age: '年齢' , address: '住所' ,other: {children: '2人' , dependents: 'いる'}};
let {age , name , other:{children}} = hoge

console.log(other) //この結果は {children: '2人' , dependents: 'いる'} になる
console.log(children) //この結果は 2人 になる

分割代入での値の取り方(変数名を別名に)

let hoge = {name: '名前' , age: '年齢' , address: '住所'};
let {age: nenrei , name: namae} = hoge

console.log(nenrei) //この結果は 年齢 になる
console.log(namae) //この結果は 名前 になる

デフォルト値を設定

let hoge = {name: '名前' , age: '年齢' , address: '住所'};
let {age , name , tel = 000} = hoge

console.log(tel) //この結果は 000 になる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQueryを自分なりにまとめた

初めに

備忘録です。

jQueryの概要

冒頭

Webアプリなど作っている人には有名な「jQuery」、私は名前しか知らなかったです。。。。
jQueryはWebサイトやWebサービスを作成する上で便利便利な物で、デザインのスキルを向上させたいという人は習得するのがマストだそうです。

jQueryの大きな特徴

ブラウザの違いを意識せずに済む
HTMLのDOM操作(HTMLの部品を、DOMと呼びます。)が簡単にできる
Ajax処理が簡単に記述できる

Ajax処理ってなんぞや

Ajaxとは、Asynchronous JavaScript And XML の略です。直訳すると、「非同期(で動作する)JavaScript と XML」です。

jQueryを使う

HTMLファイルとリンクさせる

jQueryを使えるようにします。

Test.html
 <script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script>

HTMLファイルのDOMとリンクさせる(例:テキストボックス)

Test.html
 テキストボックス
<input type="text" id="tBox" class="font-main" />
main.js
// DOMのidをキーにする場合
 $("#tBox);

Buttonとリンクさせる

ボタンを押下するとアラート表示されます
html:Test.html
テキストボックス
<button type="button" id="btn1">表示</button>

main.js
// DOMのidをキーにする場合
 $(function(){

    // 指定ボタンを押下すると処理を開始する
    $("#btn1").on("click", function() {
      alert('アラートのメッセージ');
    });


  });

最後に

jQueryを習得することで、HTMLのDOM操作を簡単に実現できそうです。

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

Javascript 初めてのGSAPアニメーションの使い方 その8 Timeline制御と開始位置制御

前回の記事はこちら
Javascript 初めてのGSAPアニメーションの使い方 その7 TimelineMaxのラベルと同期処理

今回はtimelineの制御について説明します。

以下のhtmlを準備します。※配置と色はCSSで調整しています。

    <div class="row row1">
      <div class="column col1">
        <p>パネル 1 (.circle)</p>
        <div class="circle shape"></div>
      </div>
      <div class="column col2">
        <p>パネル 2 (.square)</p>
        <div class="square shape"></div>
      </div>
      <div class="column col3">
        <p>パネル 3 (.rectangle)</p>
        <div class="rectangle shape"></div>
      </div>
    </div>
    <!--row1 -->

    <div class="row row2">
      <div class="column col4">
        <p>パネル 4 (.triangle)</p>
        <div class="triangleContainer">
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
        </div>
        <!--triangle container -->
      </div>
      <div class="column col5">
        <p>パネル 5 (.oval)</p>
        <div class="oval shape"></div>
      </div>
      <div class="column col6">
        <p>パネル 6 (.pacman)</p>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
      </div>
    </div>
    <!--row1 -->

    <div class="playback_controls_row">
      <a href="#0" class="playbackButton playButton">Play</a>
      <a href="#0" class="playbackButton pauseButton">Pause</a>
      <a href="#0" class="playbackButton resumeButton">Resume</a>
      <a href="#0" class="playbackButton reverseButton">Reverse</a>
      <a href="#0" class="playbackButton slowButton">Slow</a>
      <a href="#0" class="playbackButton fastButton">Fast</a>
      <a href="#0" class="playbackButton seekButton">Seek</a>
      <a href="#0" class="playbackButton progressButton">Progress</a>
    </div>

Image from Gyazo

timeline制御

まずはtimelineを作成してstaggerFromで連続したアニメーションにします。
x軸方向50px、y軸方向50pxの位置、opacity0の状態から
0.2秒の時間差で.shapeクラスのついた要素をアニメーションします。

要素の取得はJqueryで記述します。

const tlShapes = new TimelineMax();

tlShapes.staggerFrom('.shape', 0.5, {x: 50, y:50, opacity:0},0.2);

Image from Gyazo

下のボタンにそれぞれtimeline制御の操作を設定します。

play/pause

$('.playButton').click(function(){
  tlShapes.play();
});

$('.pauseButton').click(function(){
  tlShapes.pause();
});

Image from Gyazo

pauseボタンで一時停止、playボタンで再開の制御を設定できました。

resume/reverse

$('.resumeButton').click(function(){
  tlShapes.resume();
})

$('.reverseButton').click(function(){
  tlShapes.reverse();
})

Image from Gyazo

reverseボタンでアニメーションの逆再生、resumeボタンで再生方向の再生を設定しました。

playの場合はアニメーションは順方向に必ず再生されますが
resumeでは進行中のアニメーションの方向に再生ができます。

reverse再生したものを止めて再びreverse方向に再生したい時に使いましょう。

timeScale

$('.slowButton').click(function(){
  tlShapes.timeScale(0.5);
})
$('.fastButton').click(function(){
  tlShapes.timeScale(1.5);

Image from Gyazo

timeScaleを使うとスピードの速さを比率で制御できます。
slowボタンには0.5倍速、fastボタンには1,5倍速を設定しました。

seek/progress

$('.seekButton').click(function(){
  tlShapes.seek(0.2);
})
$('.progressButton').click(function(){
  tlShapes.progress(0.8);
})

Image from Gyazo

seekは指定した秒の箇所からアニメーションを再生します。
上記では全体の0.2秒後から再生

progressは指定したパーセンテージの箇所からアニメーションを再生します。
上記では全体の80%の位置から再生

開始位置制御

シンプルなTweenMaxではアニメーションの開始位置は
htmlとCSSで書かれた場所になります。

下記では円を左右中央揃えしたポジションが0の位置です。

Image from Gyazo

以下のコードでは上記を起点にしてx軸方向にアニメーションは動きます。

TweenMax.to ('.circle', 1, {x:100});

Image from Gyazo

以下のようにfromToを使用すると第3引数と第4引数で開始位置と終了位置を指定できます。

TweenMax.fromTo('.circle', 1, { x: -100, scale:0}, {x:100, scale:1.5});

Image from Gyazo

開始位置を-100px,終了位置を100pxの方向にアニメーションさせています。
位置以外のプロパティ(scaleなど)も一緒に指定することができます。

staggerにstaggerFromToとすることで開始と終了位置を指定できます。
以下はx軸y軸方向に起点と終点を設定しています。

TweenMax.staggerFromTo('.triangle', 1, { y:200, x:200},{ y:-50, x:-50},0.2)

初期位置
Image from Gyazo

アニメーション

Image from Gyazo

少しわかりにくいですが起点が右下に200pxずれて終点は左上に50pxずれています。

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

Javascript 初めてのGSAPアニメーションの使い方 その7 Timeline制御と開始位置制御

前回の記事はこちら
Javascript 初めてのGSAPアニメーションの使い方 その7 TimelineMaxのラベルと同期処理

今回はtimelineの制御について説明します。

以下のhtmlを準備します。※配置と色はCSSで調整しています。

    <div class="row row1">
      <div class="column col1">
        <p>パネル 1 (.circle)</p>
        <div class="circle shape"></div>
      </div>
      <div class="column col2">
        <p>パネル 2 (.square)</p>
        <div class="square shape"></div>
      </div>
      <div class="column col3">
        <p>パネル 3 (.rectangle)</p>
        <div class="rectangle shape"></div>
      </div>
    </div>
    <!--row1 -->

    <div class="row row2">
      <div class="column col4">
        <p>パネル 4 (.triangle)</p>
        <div class="triangleContainer">
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
        </div>
        <!--triangle container -->
      </div>
      <div class="column col5">
        <p>パネル 5 (.oval)</p>
        <div class="oval shape"></div>
      </div>
      <div class="column col6">
        <p>パネル 6 (.pacman)</p>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
        <div class="pacman shape"></div>
      </div>
    </div>
    <!--row1 -->

    <div class="playback_controls_row">
      <a href="#0" class="playbackButton playButton">Play</a>
      <a href="#0" class="playbackButton pauseButton">Pause</a>
      <a href="#0" class="playbackButton resumeButton">Resume</a>
      <a href="#0" class="playbackButton reverseButton">Reverse</a>
      <a href="#0" class="playbackButton slowButton">Slow</a>
      <a href="#0" class="playbackButton fastButton">Fast</a>
      <a href="#0" class="playbackButton seekButton">Seek</a>
      <a href="#0" class="playbackButton progressButton">Progress</a>
    </div>

Image from Gyazo

timeline制御

まずはtimelineを作成してstaggerFromで連続したアニメーションにします。
x軸方向50px、y軸方向50pxの位置、opacity0の状態から
0.2秒の時間差で.shapeクラスのついた要素をアニメーションします。

要素の取得はJqueryで記述します。

const tlShapes = new TimelineMax();

tlShapes.staggerFrom('.shape', 0.5, {x: 50, y:50, opacity:0},0.2);

Image from Gyazo

下のボタンにそれぞれtimeline制御の操作を設定します。

play/pause

$('.playButton').click(function(){
  tlShapes.play();
});

$('.pauseButton').click(function(){
  tlShapes.pause();
});

Image from Gyazo

pauseボタンで一時停止、playボタンで再開の制御を設定できました。

resume/reverse

$('.resumeButton').click(function(){
  tlShapes.resume();
})

$('.reverseButton').click(function(){
  tlShapes.reverse();
})

Image from Gyazo

reverseボタンでアニメーションの逆再生、resumeボタンで再生方向の再生を設定しました。

playの場合はアニメーションは順方向に必ず再生されますが
resumeでは進行中のアニメーションの方向に再生ができます。

reverse再生したものを止めて再びreverse方向に再生したい時に使いましょう。

timeScale

$('.slowButton').click(function(){
  tlShapes.timeScale(0.5);
})
$('.fastButton').click(function(){
  tlShapes.timeScale(1.5);

Image from Gyazo

timeScaleを使うとスピードの速さを比率で制御できます。
slowボタンには0.5倍速、fastボタンには1,5倍速を設定しました。

seek/progress

$('.seekButton').click(function(){
  tlShapes.seek(0.2);
})
$('.progressButton').click(function(){
  tlShapes.progress(0.8);
})

Image from Gyazo

seekは指定した秒の箇所からアニメーションを再生します。
上記では全体の0.2秒後から再生

progressは指定したパーセンテージの箇所からアニメーションを再生します。
上記では全体の80%の位置から再生

開始位置制御

シンプルなTweenMaxではアニメーションの開始位置は
htmlとCSSで書かれた場所になります。

下記では円を左右中央揃えしたポジションが0の位置です。

Image from Gyazo

以下のコードでは上記を起点にしてx軸方向にアニメーションは動きます。

TweenMax.to ('.circle', 1, {x:100});

Image from Gyazo

以下のようにfromToを使用すると第3引数と第4引数で開始位置と終了位置を指定できます。

TweenMax.fromTo('.circle', 1, { x: -100, scale:0}, {x:100, scale:1.5});

Image from Gyazo

開始位置を-100px,終了位置を100pxの方向にアニメーションさせています。
位置以外のプロパティ(scaleなど)も一緒に指定することができます。

staggerにstaggerFromToとすることで開始と終了位置を指定できます。
以下はx軸y軸方向に起点と終点を設定しています。

TweenMax.staggerFromTo('.triangle', 1, { y:200, x:200},{ y:-50, x:-50},0.2)

初期位置
Image from Gyazo

アニメーション

Image from Gyazo

少しわかりにくいですが起点が右下に200pxずれて終点は左上に50pxずれています。

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

QRコードベースのオンライン入退室管理システム

Introduction

今回の騒動で,研究室内の入室人数を非接触で可視化するシステムが必要になったため,
Googleアカウントを持っていれば誰でもwebアプリが無料で作れちゃう,Google Apps Script(GAS)を使って簡単なwebアプリケーションを作成しました

サンプルはこちらをクリックして確認してください

入退室管理システム.png

特徴

  • QRコードを読み込むだけで,入退室処理が可能
  • 部屋内の人数をオンラインで管理することができる

プログラミング言語

フロント;HTML, CSS, JavaScript … CSSはBootstrapを使用しています
バック;JavaScript
データベース;Google Spread Sheet

ソースコード

非エンジニアの方にとりあえず作って貰う,という状況を念頭に置いて本エントリを作成いたしました.そのため,大まかな作成方法の流れのみ説明しソースコードやアルゴリズムはここで触れません.

本体はGithubで配布しています.MITライセンスなので,こちらからソースコードをコピペして自由に利用してください.

Main

目次

  1. Google Spread Sheetを作成する
  2. GASでwebアプリを作成し公開する
  3. 編集したwebアプリを更新する
  4. 作成したURLを元に,QRコードを生成する

フォルダ構成

ちなみに,最終的には以下のようなフォルダ構成になります.
なんとなく,把握しておいてもらえるとありがたいです.

sample //project名
├ main.gs //JavaScriptでバックの関数を定義
├ index.html //人数を動的に表示
├ in.html //入室処理を行います
├ out.html //退室処理を行います
└ error.html

それでは,さっそくwebアプリを作っていきましょう!!

(1)Google Spread Sheetを作成する

資料進行の都合上,先にデータベースを使うGoogle Spread Sheetを作成し,URLを取得します

今回のGASでは,Google Spread Sheetを疑似的なデータベースとして使用します.

まず,Google Driveを開き,「新規作成」をクリックします
図1.png

新規作成から,「Google スプレッドシート」を選択し名前を「sample_DB」(名前はなんでもよいです)に変更してください.続いて,下記のようなSpreadSheetのURLにおける ×××××××× の部分をコピペで取得し,どこかにメモしておいてください. URLは図の赤字の線で引いてある部分に記載されています.

https://docs.google.com/spreadsheets/d/××××××××/edit 

スクリーンショット (190)_LI.jpg

続いて,"A1セル"に「現在の入室人数」,”B1セル”に「条件設定用」と入力してください

そして,"A2セル"に以下の関数を入力し、"B2セル"に"0"を入力してください

=IF(B2<=0,0,B2)

(少しエンジニア寄りの話になりますので,飛ばしてもらって構いません)
今回のwebアプリケーションではソースコードを見て貰えれば分かるように,A2セルの数値をGETしてindex.htmlに動的に数値を表示し,また入退室処理時にはA2セルからGETした数値に対して演算処理した値をB2セルに格納します.同時に,A2セルを上記のIF関数でB2セルが負になっても「0以上」になるような設定にしてあげることで,「人数」が「負」になることを回避しています.

(おそらくもっと賢いやり方があるかと思いますが,ご容赦ください…笑)

(2)GASでwebアプリを作成し公開する

(1)と同様にGoogle Driveを開き,新規作成をクリック
図1.png

新規作成から,「その他 → Google Apps Scripts」を選択.もし,まだGoogle DriveにGASがインストールされていなかった場合は,こちらを参考にして追加してください
図2.png

GASが無事起動できれば,以下のような画面に移ります
図3.png

早速,プロジェクトの名前を変更してみましょう.「無題のプロジェクト」をクリックして,名前を「sample」に変更してください(名前はhogeやfoo等,なんでもよいです).変更が終われば,「OK」をクリックしてください
図4.png

続いて,プロジェクトに必要なフォルダを作成します.「ファイル → New → HTMLファイル」の順でクリックして…
図5.png

「index.html」をダイアログに入力して,作成してみてください,次のような画面に移れば,成功です.
図6.png

今回のフォルダ構成は以下の通りなので,同様に,「in.html」,「out.html」,「error.html」を作成してください.上手くいけば,次のような画面になります.

sample //project名
├ main.gs //JavaScriptでバックの関数を定義
├ index.html //人数を動的に表示
├ in.html //入室処理を行います
├ out.html //退室処理を行います
└ error.html

図7.png

これで,必要なファイルの構築は完了しました,続いて,Githubにあげたソースコード(こちら)をそれぞれのファイルにコピペしてください.

(✳︎)ペーストする時には、もともとsampleプロジェクトの「.gs」や「.html」にあったカードは全部消してから、新しく貼り付けてください

まずはじめに,「main.gs」ファイルをコピペします.
(1) Google Spread Sheetを作成するでスプレッドシートのURLから抽出した番号を,ソースコードの5, 14, 23行目にある以下のコードの「××××××××××××」部分にペーストしてください

SpreadsheetApp.openById('××××××××××××')

続いて,「index.html」「in.html」「out.html」「error.html」ファイルをコピペしてください
コピペしたhtmlのソースコードには,以下のようなソースコードがあると思います

href="https://script.google.com/××××××××××××/exec?p=○○" 
//○○にはindexやin,out,errorなどが記載されています

htmlファイル内にある「××××××××」は,GASで作成したwebアプリの公開URLです

すなわち,webアプリを公開しURLを取得する必要があります.webアプリを公開していきましょう.

まず,「公開 → ウェブアプリケーションとして導入…」をクリックします
スクリーンショット (187).png

すると,ダイアログが出てくると思いますが,「Who has access to the app:」を「Anyone, even anonymous」に変更し「更新」してください.ここを,「Only myself」にすれば自分だけがアクセス可能なwebアプリになります.他の設定項目はこちらを参照してください.

スクリーンショット (194)_LI.jpg

webアプリの公開が成功すれば,以下のようなダイアログが出てきます.
(✳︎)もし、「承認が〜ウンタラカンタラ」のようなダイアログがでてきましたら、こちらを参考に承認してください

スクリーンショット (188).png

生成されたURLをコピペして,先ほど説明したように以下のソースコード「××××××××」に該当する文字列を切り出し、貼り付けてください.

href="https://script.google.com/××××××××××××/exec?p=○○" 
//○○にはindexやin,out,errorなどが記載されています

これで完成です!!!

(3)編集したwebアプリを更新する

適時,HTMLを編集して,各研究室の名前にあわせてください

たとえば,「index.html」の25行目

        <div class="text-center"><h6>@○棟△号室</h6></div>

の「@〇棟△号室」を適当な名前(例;山田研@28棟283号室)等に変えれば,各々の部屋で管理することができます.

そして,編集した後の注意点ですが,「公開 → ウェブアプリケーションとして導入…」をクリックしてダイアログを開いたら,Project versionを必ず「New」に変更してから「更新」をクリックしてください
ここが,GASのよく分からないところなのですが,「New」にしないと修正点は反映されません…

スクリーンショット (193) - コピー_LI.jpg

(4)作成したURLを元に,QRコードを生成する

QRコードは各種無料ツールを使用して,作成してみてください.

QRコードを生み出したデンソー公式の「クルクルManager」がお勧めです
スクリーンショット (198).png

生成するURLは以下の通りです

  • 入口に貼るQRコードのURL
https://script.google.com/××××××××/exec?p=in
//「××××××××」は,GASで作成したwebアプリの公開URL
  • 出口に貼るQRコードのURL
https://script.google.com/××××××××/exec?p=out
//「××××××××」は,GASで作成したwebアプリの公開URL

あとがき

今回がQiita初投稿でした.見にくいところがあれば,ぜひご指摘ください.
GASも初めて触れてみましたが,意外と疑似的には簡単なwebアプリができるのですね
ちょっと感動しました:relaxed:

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

30分刻みの時刻を動的生成

追記

コメントにカッコイイ書き方乗っていますので、ぜひ


もっとカッコよくかけないものかね

array = Array.prototype.concat.apply(
  [],
  [...Array(24).keys()].map(v => {
    return [
      String(v).padStart(2, '0') + ':' + '00',
      String(v).padStart(2, '0') + ':' + '30'
    ];
  })
);
console.log(array);
実行結果
[
  '00:00', '00:30', '01:00', '01:30',
  '02:00', '02:30', '03:00', '03:30',
  '04:00', '04:30', '05:00', '05:30',
  '06:00', '06:30', '07:00', '07:30',
  '08:00', '08:30', '09:00', '09:30',
  '10:00', '10:30', '11:00', '11:30',
  '12:00', '12:30', '13:00', '13:30',
  '14:00', '14:30', '15:00', '15:30',
  '16:00', '16:30', '17:00', '17:30',
  '18:00', '18:30', '19:00', '19:30',
  '20:00', '20:30', '21:00', '21:30',
  '22:00', '22:30', '23:00', '23:30'
]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQuery1系をWebpackerで流用した話

背景

新規開発プロジェクトにてデザインの一部を他アプリから流用したいリクエストがありました。
そのアプリはjquery1系で動いていたため、そのまま流用しても後述の環境では動かず・・・。
Chromeの開発者ツールによるデバッグで「$ is not defined」が頻発したりとだいぶハマったので備忘録として残します。

局所的にjqueryを利用するため、アプリ内におけるグローバル化は実施しておりません。

参考

以下の回答が大変参考になりました!

Webpackでjqueryなどが認識されない

環境

Rails: 6.0.2
Webpacker: 4.2.2
jQuery: 3.5.1
パッケージマネージャ: yarn

流用したコード(抜粋)

test.js

'use strict';

(function($){
  // 各処理
  var hoge = (function () {
    var $fuga = $('viewのclassまたはid属性')
    return{
      piyo: function () {
        // 取得したclass,id属性に対してjqueryで実現したい処理
      }
     }
  })();
  $(window).on('load', function(){
    // ページが読み込まれた時に実行するモジュール化された関数たち
    hoge.piyo();
  });

})(jQuery);

CDNによるjquery読み込み

test.html

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

修正したコード(抜粋)

test.js

'use strict';

import $ from 'jquery';

$(function(){
  // 各処理
  var hoge = (function () {
    var $fuga = $('viewのclassまたはid属性')
    return{
      piyo: function () {
        // 取得したclass,id属性に対してjqueryで実現したい処理
      }
     }
  })();

  $(window).on('load', function(){
    // ページが読み込まれた時に実行するモジュール化された関数たち
    hoge.piyo();
  });

});

webpackerによるjavascript読み込み

[webpacker管理対象ディレクトリ]/packs/app.js

import '../[任意のディレクトリ]/test.js';

test.html.slim(要件に従いslimを採用しております)

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

new Promise(function(onFulfilled, onRejected)) のonFullfilledとonRejectedってなにもの??

JSの勉強会をしていたときに
new Promise(function(onFulfilled, onRejected)) のonFullfilledとonRejectedってなにもの??
となったので自分なりにまとめてみることにした

まず、なにが疑問なのか

Promiseのインスタンス生成のとき
非同期処理の内容を記述した関数を引数に指定する
こんな感じ

new Promise(
  function(onFulfilled, onRejected) {
    /* 非同期の処理を記述 */
  }
)

これのonFulfilled, onRejectedが何なのか・どういう関数なのかという話

onFulfilledとonRejected

onFulfilledとonRejectedの説明書きはされていて、以下のように書かれている

onFulfilled
処理が正常に終了し結果が得られた場合に実行させるもの
これが実行される時はプロミスはfulfilled(成功)の状態

onRejected
処理の結果エラーが起こった場合に実行させるもの
これが実行される時はプロミスはrejected(失敗)の状態

ふむふむ、そうね、成功のときはonFulfilled、失敗のときはonRejectedという認識でした
この説明はわかっていそう

ひとまず、Promiseについて書かれているコレを読んでみた
JavaScript Promiseの本

Promiseのリファレンスも読んでみて、なんだかわかったような、さらに深みにハマっているような、、、そんな気持ちになってしまった。。。

Promiseの処理の例を書いてみた

function dispSmile(str) {
  return new Promise(
    function(onFulfilled, onRejected) {
      if(str != 'OK') {
        onRejected(new Error()); // 失敗の状態を返す
      }
      else onFulfilled(console.log(☺︎)); // 成功の状態を返す
      // onFulfilledまたはonRejectedのどちらかを返さなければこのPromiseは終了しない
    }
  )
}

引数の値が'OK'のときはFulfilledの状態を返し、onFulfilled()の引数の値は次の処理に渡す値になってる
引数の値が'OK'以外はRejectedの状態を返し、onRejected()でエラーを返すようになっている
なので結果はこんな感じ

// 実行結果
dispSmile('OK'); // ☺︎
dispSmile('No'); // Error

例えば以下のようなPromiseを返すと、状態は呼び出されてもPending(初期状態)のままになる

function dispSmile(str) {
  return new Promise(function(onFulfilled, onRejected) {})
}

onFulfilledとonRejectedは、関数で、状態によって実行される処理、ということかな・・・

うーん、まだスッキリという感じではないけれど
Promiseの状態の持ち方は以前より理解できた気がするのはよかった

もう少し勉強してみて理解を深めていきたい

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

JavaScriptの文字列操作(サンプルでマスター)

はじめに

JavaScriptの基本的な文字列操作をまとめました。

テンプレートリテラル

基本は文字列を`テキスト`のように、バッククオートで囲むだけです。
エスケープ無しの改行や変数埋め込みができます。

テンプレートリテラルで改行
var a = `Java
Script`;

console.log(a);
// -> Java
// -> Script
テンプレートリテラル内に変数埋め込み
var num = 10;
var str = `りんごを${num}個とみかんを${num - 5}個ください`; // ${}内に計算式をいれてもOK
console.log(str); // -> りんごを10個とみかんを5個ください

エスケープはシングルクオートやダブルクオートのように、\でOKです。

テンプレートリテラル内のエスケープ
var a = `Java\\Script`;
console.log(a); // -> Java\Script

連結(結合)

+ 演算子で連結

+演算子で連結
var a = 'Java';
var b = 'Script';
var result = a + b ;
console.log(result); // -> JavaScript


`文字列` テンプレートリテラルで連結

テンプレートリテラルで文字列を連結
var a = '6';
var b = '2';
var c = '';
var result = `今日は${a}${b}日、${c}曜日です`;
console.log(result); // -> 今日は6月2日、火曜日です


対象の配列.join('つなぎ目の文字列');

.joinで配列の文字列を連結
var a = ['Hello', 'World'];
var result = a.join('');
console.log(result); // -> HelloWorld


対象の配列.join('');

.joinでつなぎ目の文字列を指定
// つなぎ目の文字列を指定 -> 「--」 
var a = ['Hello', 'World'];
var result = a.join('--');
console.log(result); // -> Hello--World

文字列と数値を連結すると、文字列になります。

切り出し

対象の文字列.slice( 開始位置, 終了位置 );

.slice
var str = '本日も、よろしくお願いします!';

var result = str.slice(0, 3);
console.log(result); // -> 本日も

// 開始位置を後ろから数えて切り出し -> 開始位置:後ろから7文字目, 文字数:文字列の最後まで
var result2 = str.slice(-7);
console.log(result2); // -> お願いします!


対象の文字列.substr( 開始位置, 切り出す文字数 );

.substr
var str = '本日も、よろしくお願いします!';

var result1 = str.substr(0, 3);
console.log(result1); // -> 本日も

// 開始位置を後ろから数えて切り出し -> 開始位置:後ろから7文字目, 切り出す文字数:3文字(省略すると文字列の最後まで)
var result2 = str.substr(-7, 3);
console.log(result2); // -> お願い


対象の文字列.substring( 開始位置, 終了位置 );

.substring
var str = '本日も、よろしくお願いします!';

var result1 = str.substring(0, 3);
console.log(result1); // -> 本日も

// 開始位置 > 終了位置 のとき -> 開始位置:8文字目, 終了位置:4文字目
// 終了位置と開始位置は逆になる
var result2 = str.substring(8, 4);
console.log(result2); // -> よろしく

// 開始位置から文字列の先頭まで -> 開始位置:8文字目, 終了位置:文字列の先頭
var result3 = str.substring(8, -1);
console.log(result3); // -> 本日も、よろしく

分割

対象の文字列.split( 区切り文字 );

.split
var str = '2018, 2019, 2020';
var result = str.split(','); // 「,」 カンマ区切りで分割して配列に格納
console.log(result); // -> ["2018", " 2019", " 2020"]

置換・削除

対象の文字列.replace( 置換前の文字列, 置換後の文字列 );

.replaceで置換
var str = '夕食はラーメンとカレーです';
var result = str.replace('カレー', '餃子');
console.log(result); // -> 夕食はラーメンと餃子です


対象の文字列.replace( 削除したい文字列, '' );

.replaceで削除
var str = '夕食はラーメンとカレーです';
var result = str.replace('とカレー', '');
console.log(result); // -> 夕食はラーメンです

検索

対象の文字列を含むかどうかのチェック。

対象の文字列.indexOf( 検索したい文字列 );

.indexOfで検索(前方一致)
var str = 'おはようございまーす';
var result1 = str.indexOf('ございまー');
var result2 = str.indexOf('!');

console.log(result1); // -> 4
console.log(result2); // -> -1

検索したい文字列を含む  :インデックスを返す(0文字目から数えて何文字目か?)
検索したい文字列を含まない:「-1」を返す

を利用して、次のように条件分岐できます。

.indexOfの戻り値を利用した条件分岐
if (str.indexOf('おはよう') != -1) {
  //strに「おはよう」を含む場合の処理
}


対象の文字列.lastIndexOf( 検索したい文字列 );

.lastIndexOfで検索(後方一致)
var str = 'おはようございまーす';
var result1 = str.lastIndexOf('ございまー');
var result2 = str.lastIndexOf('!');

console.log(result1); // -> 4
console.log(result2); // -> -1

検索したい文字列を含む  :インデックスを返す(0文字目から数えて何文字目か?)
検索したい文字列を含まない:「-1」を返す

※ 対象の文字列の最後から検索しますが、インデックスは文字列の先頭から数えた値です。

トリム

対象の文字列の前後にある空白を削除する

対象の文字列.trim();

.trim
var str = '  おはようございまーす      ';
var result = str.trim();
console.log(result); // -> おはようございまーす

大文字・小文字に変換

対象の文字列.toUpperCase();

.toUpperCase
var str = 'Chrome';
var result = str.toUpperCase();
console.log(result); // -> CHROME


対象の文字列.toUpperCase();

.toLowerCase
var str = 'MSIE';
var result = str.toLowerCase();
console.log(result); // -> msie

文字列を数値にする

Number( 文字列 );

Number
var str = '123';
var result = Number(str);
console.log(result); // -> 123

// typeofで型判定
console.log(typeof result) // -> number

数値を文字列にする

String( 数値 );

String
var num = 123;
var result = String(num);
console.log(result); // -> 123

// typeofで型判定
console.log(typeof result) // -> string

文字数をカウント

対象の文字列.length;

.length
var str = 'あいうえお';
var result = str.length;
console.log(result); // -> 5

参考

https://qiita.com/kura07/items/c9fa858870ad56dfec12
http://catprogram.hatenablog.com/entry/2013/05/13/231457

おわりに

よく使うものをまとめてみました。(たぶん追記します。。)
正規表現をマスターすると.match.searchも便利に使えます。(長くなるので別の機会にまとめます)

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

JavaScriptの文字列操作(サンプル集でマスター)

はじめに

JavaScriptの基本的な文字列操作をまとめました。

テンプレートリテラル

基本は文字列を`テキスト`のように、バッククオートで囲むだけです。
エスケープ無しの改行や変数埋め込みができます。

テンプレートリテラルで改行
var a = `Java
Script`;

console.log(a);
// -> Java
// -> Script
テンプレートリテラル内に変数埋め込み
var num = 10;
var str = `りんごを${num}個とみかんを${num - 5}個ください`; // ${}内に計算式をいれてもOK
console.log(str); // -> りんごを10個とみかんを5個ください

エスケープはシングルクオートやダブルクオートのように、\でOKです。

テンプレートリテラル内のエスケープ
var a = `Java\\Script`;
console.log(a); // -> Java\Script

連結(結合)

+ 演算子で連結

+演算子で連結
var a = 'Java';
var b = 'Script';
var result = a + b ;
console.log(result); // -> JavaScript


`文字列` テンプレートリテラルで連結

テンプレートリテラルで文字列を連結
var a = '6';
var b = '2';
var c = '';
var result = `今日は${a}${b}日、${c}曜日です`;
console.log(result); // -> 今日は6月2日、火曜日です


対象の配列.join('つなぎ目の文字列');

.joinで配列の文字列を連結
var a = ['Hello', 'World'];
var result = a.join('');
console.log(result); // -> HelloWorld


対象の配列.join('');

.joinでつなぎ目の文字列を指定
// つなぎ目の文字列を指定 -> 「--」 
var a = ['Hello', 'World'];
var result = a.join('--');
console.log(result); // -> Hello--World

文字列と数値を連結すると、文字列になります。

切り出し

対象の文字列.slice( 開始位置, 終了位置 );

.slice
var str = '本日も、よろしくお願いします!';

var result = str.slice(0, 3);
console.log(result); // -> 本日も

// 開始位置を後ろから数えて切り出し -> 開始位置:後ろから7文字目, 文字数:文字列の最後まで
var result2 = str.slice(-7);
console.log(result2); // -> お願いします!


対象の文字列.substr( 開始位置, 切り出す文字数 );

.substr
var str = '本日も、よろしくお願いします!';

var result1 = str.substr(0, 3);
console.log(result1); // -> 本日も

// 開始位置を後ろから数えて切り出し -> 開始位置:後ろから7文字目, 切り出す文字数:3文字(省略すると文字列の最後まで)
var result2 = str.substr(-7, 3);
console.log(result2); // -> お願い


対象の文字列.substring( 開始位置, 終了位置 );

.substring
var str = '本日も、よろしくお願いします!';

var result1 = str.substring(0, 3);
console.log(result1); // -> 本日も

// 開始位置 > 終了位置 のとき -> 開始位置:8文字目, 終了位置:4文字目
// 終了位置と開始位置は逆になる
var result2 = str.substring(8, 4);
console.log(result2); // -> よろしく

// 開始位置から文字列の先頭まで -> 開始位置:8文字目, 終了位置:文字列の先頭
var result3 = str.substring(8, -1);
console.log(result3); // -> 本日も、よろしく

分割

対象の文字列.split( 区切り文字 );

.split
var str = '2018, 2019, 2020';
var result = str.split(','); // 「,」 カンマ区切りで分割して配列に格納
console.log(result); // -> ["2018", " 2019", " 2020"]

置換・削除

対象の文字列.replace( 置換前の文字列, 置換後の文字列 );

.replaceで置換
var str = '夕食はラーメンとカレーです';
var result = str.replace('カレー', '餃子');
console.log(result); // -> 夕食はラーメンと餃子です


対象の文字列.replace( 削除したい文字列, '' );

.replaceで削除
var str = '夕食はラーメンとカレーです';
var result = str.replace('とカレー', '');
console.log(result); // -> 夕食はラーメンです

検索

対象の文字列を含むかどうかのチェック。

対象の文字列.indexOf( 検索したい文字列 );

.indexOfで検索(前方一致)
var str = 'おはようございまーす';
var result1 = str.indexOf('ございまー');
var result2 = str.indexOf('!');

console.log(result1); // -> 4
console.log(result2); // -> -1

検索したい文字列を含む  :インデックスを返す(0文字目から数えて何文字目か?)
検索したい文字列を含まない:「-1」を返す

を利用して、次のように条件分岐できます。

.indexOfの戻り値を利用した条件分岐
if (str.indexOf('おはよう') != -1) {
  //strに「おはよう」を含む場合の処理
}


対象の文字列.lastIndexOf( 検索したい文字列 );

.lastIndexOfで検索(後方一致)
var str = 'おはようございまーす';
var result1 = str.lastIndexOf('ございまー');
var result2 = str.lastIndexOf('!');

console.log(result1); // -> 4
console.log(result2); // -> -1

検索したい文字列を含む  :インデックスを返す(0文字目から数えて何文字目か?)
検索したい文字列を含まない:「-1」を返す

※ 対象の文字列の最後から検索しますが、インデックスは文字列の先頭から数えた値です。

トリム

対象の文字列の前後にある空白を削除する

対象の文字列.trim();

.trim
var str = '  おはようございまーす      ';
var result = str.trim();
console.log(result); // -> おはようございまーす

大文字・小文字に変換

対象の文字列.toUpperCase();

.toUpperCase
var str = 'Chrome';
var result = str.toUpperCase();
console.log(result); // -> CHROME


対象の文字列.toUpperCase();

.toLowerCase
var str = 'MSIE';
var result = str.toLowerCase();
console.log(result); // -> msie

文字列を数値にする

Number( 文字列 );

Number
var str = '123';
var result = Number(str);
console.log(result); // -> 123

// typeofで型判定
console.log(typeof result) // -> number

数値を文字列にする

String( 数値 );

String
var num = 123;
var result = String(num);
console.log(result); // -> 123

// typeofで型判定
console.log(typeof result) // -> string

文字数をカウント

対象の文字列.length;

.length
var str = 'あいうえお';
var result = str.length;
console.log(result); // -> 5

参考

https://qiita.com/kura07/items/c9fa858870ad56dfec12
http://catprogram.hatenablog.com/entry/2013/05/13/231457

おわりに

よく使うものをまとめてみました。(たぶん追記します。。)
正規表現をマスターすると.match.searchも便利に使えます。(長くなるので別の機会にまとめます)

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

Javascript 初めてのGSAPアニメーションの使い方 その7 TimelineMaxのラベルと同期処理

前回の記事はこちら

Javascript 初めてのGSAPアニメーションの使い方 その6 TweenMax.setとTimelineMaxプロパティ

今回はTimelineMaxのラベルと同期処理について説明します。

htmlは以下です。CSSで配置と色は調整しています。

    <div class="row row1">
      <div class="column col1">
        <p>パネル 1 (.circle)</p>
        <div class="circle shape"></div>
      </div>
      <div class="column col2">
        <p>パネル 2 (.square)</p>
        <div class="square shape"></div>
      </div>
      <div class="column col3">
        <p>パネル 3 (.rectangle)</p>
        <div class="rectangle shape"></div>
      </div>
    </div>

Image from Gyazo

丸(.circle)→正方形(.square)→長方形(.rectangle)の順にアニメーションを設定します。
それぞれx軸方向へ100px動かします。

const tlshapes = new TimelineMax();
tlshapes.to('.circle', 1, { x: 100 })
.to('.square', 1, { x: 100})
.to('.rectangle', 1, { x: 100});

Image from Gyazo

このとき個々の要素の動く順番やタイミングをコントロールするために
任意の名前でラベルが設定できます。

circleRectangleラベルで、丸が動くタイミングで長方形を一緒に動かします。

const tlshapes = new TimelineMax();
tlshapes.to('.circle', 1, { x: 100 }, 'circleRectangle')
.to('.square', 1, { x: 100})
.to('.rectangle', 1, { x: 100},'circleRectangle');

Image from Gyazo

次にsquareRectangleラベルで正方形と長方形を一緒に動かします。

const tlshapes = new TimelineMax();
tlshapes.to('.circle', 1, { x: 100 })
.to('.square', 1, { x: 100},'squareRectangle')
.to('.rectangle', 1, { x: 100},'squareRectangle');

Image from Gyazo

ラベルの中に秒数を設定すると同期処理の中で時間差をつけることができます。

const tlshapes = new TimelineMax();
tlshapes.to('.circle', 1, { x: 100 })
.to('.square', 1, { x: 100},'squareRectangle')
.to('.rectangle', 1, { x: 100},'squareRectangle+=0.25');

Image from Gyazo

正方形の動き出しから0.25秒後に長方形が動き出します。

マイナス数値を入れることで動き出しを早めることもできます。

const tlshapes = new TimelineMax();
tlshapes.to('.circle', 1, { x: 100 })
.to('.square', 1, { x: 100},'squareRectangle')
.to('.rectangle', 1, { x: 100},'squareRectangle-=0.25');

Image from Gyazo

長方形は正方形と同期していますが0.25秒早く動き出します。

ラベルはつけずに秒数のみ調整することも可能です。

const tlshapes = new TimelineMax();
tlshapes.to('.circle', 1, { x: 100 })
.to('.square', 1, { x: 100}, '-=0.5')
.to('.rectangle', 1, { x: 100}, '-=0.5');

Image from Gyazo

正方形と長方形の動き出しを-0.5秒することで滑らかに動かしています。

数値のみを指定するとアニメーションの中で絶対値の秒数を指定できます。

const tlshapes = new TimelineMax();
tlshapes.to('.circle', 1, { x: 100 })
.to('.square', 1, { x: 100},3)
.to('.rectangle', 1, { x: 100},3);

Image from Gyazo

アニメーション開始から3秒経過後に正方形と長方形が動き出します。

次回はTimeline制御と開始位置制御です

Javascript 初めてのGSAPアニメーションの使い方 その8 Timeline制御と開始位置制御

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

Javascript 初めてのGSAPアニメーションの使い方 その6 TweenMax.setとTimelineMaxプロパティ

前回の記事はこちら
Javascript 初めてのGSAPアニメーションの使い方 その5 staggerTo/staggerFrom

今回はTweenMax.setとTimelineMaxのプロパティーを
組み合わせたアニメーションについて説明します。

TweenMax.set

今回使用するhtmlは以下です。
※CSSで配置調整と色付けしています。

      <div class="column col5">
        <p>パネル 5 (.oval)</p>
        <div class="oval shape"></div>
      </div>

Image from Gyazo

jsシートに以下を記述します

TweenMax.set('.oval', { x: -100});

実行結果
Image from Gyazo

TweenMax.setを使用することによってアニメーションの初期位置をずらすことができます。
※この段階ではアニメーションはしません。
CSSのtransform: translate();と同じ意味です。

TimelineMaxのプロパティ

ここからTimelineMaxを使用してアニメーションを設定しています。
まずは繰り返しを3回にしてみます。

TweenMax.set('.oval', { x: -100});

const tlAnimation = new TimelineMax({ repeat:3 }); //繰り返しを3回に設定
tlAnimation.to('.oval', 0.5, { x: 100 })
.to('.oval', 0.5, { x: -100});

実行結果
Image from Gyazo

タイムラインでx軸方向に100px,動かしてから-100px方向に動かす処理を入れています。
ここで気をつけるのは基準位置は.setで取得したx:-100にはならず
htmlで設定した位置になるという点です。

そのため見た目には最初の動き幅は200px動きますが実際に指定するのは左右100pxです。

回数制限なく繰り返す場合は数値に-1を入れます。なんとなくrepeat:trueにしたくなりますが
それでは無制限にならないので注意しましょう。

TweenMax.set('.oval', { x: -100});

const tlAnimation = new TimelineMax({ repeat:-1 });
tlAnimation.to('.oval', .5, { x: 100 })
.to('.oval', .5, { x: -100});

実行結果
Image from Gyazo

プロパティにrepeatDelayを入れるとリピート間隔の指定(秒)になります。

TweenMax.set('.oval', { x: -100});

const tlAnimation = new TimelineMax({ repeat:-1, repeatDelay:2 }); //2秒の間隔を開けて繰り返し(無制限)
tlAnimation.to('.oval', .5, { x: 100 })
.to('.oval', .5, { x: -100});

Image from Gyazo

プロパティにyoyo:trueを入れると名前の通りヨーヨーの動きになります。

TweenMax.set('.oval', { x: -100});
const tlAnimation = new TimelineMax({ repeat:-1, yoyo: true });
tlAnimation.to('.oval', 1, { x: 100 })

Image from Gyazo

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

PlayCanvasでTypeScriptを使うためのメモ(Webpack編)

概要

前回の記事でwebpackを使わず、無理やりコードを追加することでモジュール問題を解消しましたが、
今回は、そもそもああいったモジュール問題を解決するためにあるWebpackを利用したgulpタスクを作ります。

Webpackの設定

ポイントとしては、PlayCanvasライブラリは実行時に別の形で(たぶんScriptタグで)読み込まれるため、externalで生成されるJavaScriptコードに組み込まないように設定しておく必要があります(そうしないと競合するのでたぶん動かない)

webpack.config.js
const path = require("path");
const glob = require("glob");

module.exports = {
  mode: "development",
  //mode: 'production',
  devtool: "",
  entry: glob.sync("./src/*.ts"),
  output: {
    filename: "output.js",
    path: path.resolve(__dirname, "dist/"),
  },
  externals: [
    {
      playcanvas: 'pc'
    }
  ],
  module: {
    rules: [
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        loader: "ts-loader"
      },
    ]
  },
  resolve: {
    extensions: [".ts", ".js"]
  },
};

gulpfile.js

WebPack未使用版との違いは、TypeScriptのトランスパイルをWebpack経由にしていることです。その関係で前回はgulpfile.js上で直接TypeScriptのオプションを設定していましたが、今回はtsconfig.jsonで設定をしておく必要があります。

gulpfile.js
const gulp = require("gulp");
const playcanvas = require("gulp-playcanvas");
const pcOptions = require("./playcanvas.json");
const webpackStream = require('webpack-stream');
const webpack = require('webpack');
const webpackConfig = require('./webpack.config.js');

gulp.task("ts", () => {
  return webpackStream(webpackConfig, webpack)
  .pipe(gulp.dest('./dist/'))
  .pipe(playcanvas(pcOptions));
});

gulp.task("js", () => {
  return gulp
    .src(["src/**/*.js", "!src/**/_*.js", "!./node_modules/**"])
    .pipe(gulp.dest("dist/"))
    .pipe(playcanvas(pcOptions));
});

gulp.task("watch", function() {
  gulp.watch(["src/**/*.ts", "!src/**/_*.ts", "!./node_modules/**"], gulp.task("ts"));
  gulp.watch(["src/**/*.js", "!src/**/_*.js", "!./node_modules/**"], gulp.task("js"));
});

gulp.task("default", gulp.parallel("watch"));

感想

これで、トランスパイルに成功するとoutput.jsという単一のJavascriptファイルが生成され、PlayCanvasにアップロードされます。

眺めるとそこそこの量のコードがWebpackによって追加されるので、小規模なものだと気になるかもしれませんね…

規模によって使い分けるといいのではないでしょうか。

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

PlayCanvasでTypeScriptを使うためのメモ(Webpack未使用編)

概要

前回の記事(準備編)でアセットをアップロードするための設定までやりましたが、今回はgulpを使ってトランスパイルからアップロードまで一括で行えるようにgulpタスクを作ります。

なぜgulpを使うのかというと、前回の記事にも書いた通り、PlayCanvas用のgulpタスクが公開されているからです。

gulpfile.js

後述の問題のため、through2というgulp用のプラグインを使い、トランスパイル後のコードの先頭に2行ほどコードを書き加えるという泥臭い処理を行っています…(´・ω・`)

gulpfile.js
const gulp = require("gulp");
const playcanvas = require("gulp-playcanvas");
const pcOptions = require("./playcanvas.json");
const typescript = require("gulp-typescript");
const through2 = require('through2');

gulp.task("ts", () => {
  return gulp
    .src(["src/**/*.ts", "!src/**/_*.ts", "!./node_modules/**"])
    .pipe(typescript({target: "ES5", module: "commonjs"}))         //ES5
    //.pipe(typescript({target: "ES2015", module: "commonjs"}))    //ES2015(ES6)
    .pipe(through2.obj((file, enc, callback) => {
      if (file.isBuffer()) {
        let contents = String(file.contents);
        contents = "var exports = {};\nvar require = function(mod){};\n" + contents;
        //contents = "/*jshint esversion: 6, asi: true, laxbreak: true*/\n" + contents;   //ES2015(ES6)
        file.contents = Buffer.from(contents);
      }
      callback(null, file);
    }))
    .pipe(gulp.dest("dist/"))
    .pipe(playcanvas(pcOptions));
});

gulp.task("js", () => {
  return gulp
    .src(["src/**/*.js", "!src/**/_*.js"])
    .pipe(gulp.dest("dist/"))
    .pipe(playcanvas(pcOptions));
});

gulp.task("watch", function() {
  gulp.watch(["src/**/*.ts", "!src/**/_*.ts"], gulp.task("ts"));
  gulp.watch(["src/**/*.js", "!src/**/_*.js"], gulp.task("js"));
});

gulp.task("default", gulp.parallel("watch"));

モジュール問題

トランスパイルしたコードの先頭に付け足す理由はモジュール機構の問題になります、ざっくり挙げると

  • トランスパイルしただけのJavascriptコードそのままではモジュール機構の関係でPlayCanvas上ではエラーになります。
  • ブラウザ環境ではES2015(ES6)形式のモジュールしかサポートされていません(IEの場合はモジュール自体対応していません)
  • ES2015形式でimportすると、Playcanvasの実行時にエラーが出ます(importはトップレベルに記載する必要があるのですがランタイム上でスクリプトは下位へ移動されるから?)
  • AMDやUMDはブラウザ上でも動くモジュール形式だそうですが、非同期だからなのかPlayCanvasエディタでアトリビュートを読み込めないので意味がありません

これをいろいろどうにかする方法を探りましたが、最も簡単で無難だったのが、commonjs形式でトランスパイルした上で

var exports = {};
var require = function(mod){};

という何もしないモジュール関係の変数と関数を定義するコードを付け足すことでした。
もっといい方法があればだれか教えてください…

そのためのWebpack

そもそもこういったモジュール機構の問題を解決するためにWebpackがあるので、
次回はWebpackを使ってあんなことをしなくてもPlaycanvasで使えるコードを生成するためのタスクを作ります。

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

Javascript 初めてのGSAPアニメーションの使い方 その5 staggerTo/staggerFrom

前回の記事はこちら
Javascript 初めてのGSAPアニメーションの使い方 その4 easing設定

今回はstaggerFrom/staggerToについて説明します。

staggerは繰り返す複数の要素に対して連続したアニメーションを設定できます。

staggerFrom

htmlは以下のように三角形を連続して表示させています。

      <div class="column col4">
        <p>パネル 4 (.triangle)</p>
        <div class="triangleContainer">
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
          <div class="triangle shape"></div>
        </div>
        <!--triangle container -->
      </div>

Image from Gyazo

ここにstaggerを使用してアニメーションを設定します。

TweenMax.staggerFrom('.triangle', 1, { x: 200, y:200, opacity:0 }, 0.1)

三角形(.triangle)に対して
・各要素のアニメーションの時間は1秒
・x方向へ200px,y方向へ200pxから
・透明度0から徐々に
・各要素のずれは0.1秒ずつ
という設定にします。

実行結果
Image from Gyazo

staggerTo

続いてstaggerToの動きを見てみましょう

TweenMax.staggerTo('.triangle', 1, { x: 200, y:200 }, 0.1)

三角形(.triangle)に対して
・各要素のアニメーションの時間は1秒
・x方向へ200px,y方向へ200px
・透明度0から徐々に
・各要素のずれは0.1秒ずつ
という設定にします。

実行結果
Image from Gyazo

このように初期位置から指定位置へ向かっていく動きとなります。

プロパティとしてeasingを設定することもできます。
easingの記事はこちら

TweenMax.staggerFrom('.triangle', 1, { x: 200, y:200, ease:Back.easeOut }, 0.1)

Image from Gyazo

次回はTweenMax.setとTimelineMax プロパティです。
Javascript 初めてのGSAPアニメーションの使い方 その6 TweenMax.setとTimelineMaxプロパティ

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

React Redux基礎の基礎/小さなカウンターアプリを作る流れ

この記事で書くこと

UdemyのReact、Redux講座の基礎編を学んでおり、本当にReduxがややこしいので一旦基礎の基礎をまとめました。
環境構築のあたりや細かい用語の説明や調査は省き、ミニマムにコードの流れをまとめただけの記事です。
ほぼ頭の整理のためなのでいろいろ認識違ってるかもしれないです。。w。

最低限わかっておくべきprops、stateについてはこちらが分かりやすかったです。
+と-ボタンで数字が増える、減るというだけの処理を書いてます。

流れ

【1】まず、アクションのタイプを書く。incrementかdecrementか。これをReducerへ渡す。

action/index.js
//あとでレデューサーでも使うので、constしておく。
export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT'

//それぞれをアクションクリエイターとよぶよ
export const increment = () => ({
        type:INCREMENT
})

export const decrement = () => ({
        type:DECREMENT
})

【2】アクションからReducerへINCREMENT、DECREMENTを渡し、typeによりstateを返す。

reducers/count.js
import { INCREMENT,DECREMENT } from '../actions'

//初期状態のオブジェクトを入れる変数名をinitialStateとする。
const initialState = { value:0 }//初期のカウント

//関数として定義。引数はstate、acitonとして2つ持つ
export default(state =initialState, action) => {
    //actionのtype(INCREMENTかDECREMENTか)はaction.typeという処理で拾える
    switch(action.type){
        case INCREMENT://INCREMENTを拾った場合は値をプラス1する。
            return { value:state.value + 1}
        case DECREMENT://同様に、DECREMENTの場合はマイナス。
            return { value:state.value - 1}
        default: //初期状態は0のまま
            return state
    }
}

【3】count.jsを同じReducerの中のindex.jsに渡し、combineReducersでまとめる(今回は1つだが、複数のreducersをまとめるのに良い)

reducers/index.js
//すべてのreducsersをここでまとめてしまうcombineReducers を取り込む。
import { combineReducers } from 'redux'
//count.jsから取得
import count from './count'

//storeで使うので、Reducerで決めた数値をエクスポートする。
export default combineReducers({ count })
//なお、今回は受け渡す値は「count」一つだが
//いろいろな状態を管理したい場合は
//export default combineReducers({ foo,bar,baz }) みたいな感じ


【4】ReducserからComponent/App.jsへ。ここでconnect関数を使い、

component/App.js
import React, { Component } from 'react';
//コネクト関数を追加
import { connect } from 'react-redux'
import { increment, decrement } from '../actions'


class App extends Component {
  //counterには、レデューサーのカウンター内のオブジェクトの値の値を渡す。propsで受け取ってる。
  render(){
    const props = this.props
      return(
      <React.Fragment>
        <div>counter:{props.value}</div>
        <button onClick={props.increment}> +1</button>
        <button onClick={props.decrement}> -1</button>
      </React.Fragment>
    )
  }
}

//mapStateToPropsは、stateの情報から、componentに必要な情報を受け渡す
const mapStateToProps = state => ({ value:state.count.value})
//アクションが実行された時に、該当のアクションを渡し、状態遷移をさせるのがディスパッチ
const mapDispatchToProps = dispatch => ({
  increment: () => dispatch(increment()),
  decrement: () => dispatch(decrement())
})
//connect関数でstoreと描写側がつながるらしい。
export default connect(mapStateToProps,mapDispatchToProps)(App)


【5】これらをまとめるトップにあるファイルがこれ。

index.js
import React from "react";
import ReactDOM from 'react-dom';
//createStoreはstoreを利用するためのもの
import { createStore } from 'redux'
//Providerはすべての環境でstoreを使うためのもの
import { Provider } from 'react-redux'
import './index.css';
import reducer from './reducers';
import App from './components/App';
import registerServiceWorker from './registerServiceWorker';

//ここで作られるstoreはアプリ内の唯一のものになる
const store = createStore(reducer)

//既存のコンポーネントを、providerコンポートネントで囲み、store属性に、
//上のconstしたstoreを当てはめる。これで、バケツリレーをせずにproviderが使える
//この中の<App />が【4】でrenderされてるもの。
ReactDOM.render(
<Provider store={store}>
    <App />
</Provider>,
 document.getElementById('root')
);

registerServiceWorker();//?

実際の表示。※【4】でrenderされてるコンテンツのみ!

スクリーンショット 2020-06-02 1.42.39.png

+1と-1をクリックすると数字が増えるよ。

むずい

書き始めたときは流れに沿って書けば整理できそう!と思ったけど、まとめてみると全然1つの流れ、というわけではない。し、あまりよくわかってないです。。w

とにかく、
action、action creatorでアクションを作り
ビュー側のクリックとかでそのアクションをディスパッチする。
レデューサーでそのアクションを元に実際にstate(値)を変える。
したらビュー側が変わる、というくらいは分かりました!ほんまにわかってるんかこれ!

qiitaでも色々あったので、もう少し整理するためにいろんな記事読んでみようと思います。。
外部サイトですがこれも図になってて分かりやすかった。

ちなみに

これやってます!まだ基礎編を学んだだけなのでこっからしんどそう!
「フロントエンドエンジニアのためのReact・Redux実践入門」
https://www.udemy.com/course/react-application-development/learn/
ちなみに、gitもpushとclone、commitくらいしかしてなかった(しかもSource tree)のでめっちゃ練習になってます、、、w

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

PlayCanvasでTypeScriptを使うためのメモ(コーディング編)

概要

せっかくTypeScriptでPlayCanvasをコーディングできるようになっても、
従来のPlayCanvasサンプルによくみられるプロトタイプベースのコーディングをしていたのでは意味がないので
(最近まで古いJavaScriptしかサポートしていなかったので致し方ないのですが…)

サンプルとして同じコードをクラスベースに書き換えたものを載せておきます。
TypeScriptではなく、ES2015(ES6)で作る際の参考にもなるかもしれません。

コードは新規登録したときに作成された「My First Project」のfollow.jsというコードをクラスベースに書き換えてみます。

プロトタイプベース

var Follow = pc.createScript('follow');

Follow.attributes.add('target', {
    type: 'entity',
    title: 'Target',
    description: 'The Entity to follow'
});

Follow.attributes.add('distance', {
    type: 'number',
    default: 4,
    title: 'Distance',
    description: 'How far from the Entity should the follower be'
});

// initialize code called once per entity
Follow.prototype.initialize = function() {
    this.vec = new pc.Vec3();
};

// update code called every frame
Follow.prototype.update = function(dt) {
    if (!this.target) return;

    // get the position of the target entity
    var pos = this.target.getPosition();

    // calculate the desired position for this entity
    pos.x += 0.75 * this.distance;
    pos.y += 1.0 * this.distance;
    pos.z += 0.75 * this.distance;

    // smoothly interpolate towards the target position
    this.vec.lerp(this.vec, pos, 0.1);

    // set the position for this entity
    this.entity.setPosition(this.vec); 
};

クラスベース

上記のプロトタイプベースのコードをクラスベースに書き換えるとこうなります。
コメントで書き換えるポイントを載せてあります。

import 'playcanvas';   //PlayCanvasライブラリ

//クラスベースの場合はpc.ScriptTypeを継承する。
class Follow extends pc.ScriptType {

    public vec!: pc.Vec3;

    //TypeScript上ではアトリビュートは下記のattributes.addとは別に、フィールドとして別途設定しておく必要があります。
    //DRY原則に反していて面倒ですが、typeとクラス名を合わせれば正しくコード補完が効くので便利です。
    //(PlayCanvadのwebエディタではアトリビュートのコード補完は未対応のようです)
    //トランスパイル後にはこのフィールドは消える模様
    public target!: pc.Entity;
    public distance!: number;

    // update等の各種メソッドはprototype.メソッド名に関数を代入するのではなく、pc.ScriptTypeクラスのメソッドをオーバーライドする。

    // initialize code called once per entity
    public initialize() {
        this.vec = new pc.Vec3();
    }

    // update code called every frame

    public update(dt: number) {    //updateメソッドの引数であるdtはnumber型です。
        // called each tick

        if (!this.target) {
            return;
        }
        // get the position of the target entity
        var pos = this.target.getPosition();
        // calculate the desired position for this entity
        pos.x += 0.75 * this.distance;
        pos.y += 1.0 * this.distance;
        pos.z += 0.75 * this.distance;
        // smoothly interpolate towards the target position
        this.vec.lerp(this.vec, pos, 0.1);
        // set the position for this entity
        this.entity.setPosition(this.vec);
    }
}

//クラスベースで作る場合はpc.createScriptではなく、pc.registerScriptを使う。
//またコードの先頭ではなく、クラス定義の後に呼び出す。
pc.registerScript(Follow, "follow");

//アトリビュートはクラス定義かつpc.registerScriptの後に追加する
Follow.attributes.add('target', {
    type: 'entity',
    title: 'Target',
    description: 'The Entity to follow'
});

Follow.attributes.add('distance', {
    type: 'number',
    default: 4,
    title: 'Distance',
    description: 'How far from the Entity should the follower be'
});

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

[JavaScript] 正規表現の Firefox 限定問題の回避方法 (flags s)

概要

  • JavaScript の正規表現で flags の一部にブラウザ依存があり、それの回避方法を説明します。

Firefox で生じる問題

  • 以下のコードは Google Chrome と Safari では動作しますが、Firefox では動作しません。
const str = `aaa
bbb
ccc`

str.match(/.{11}/s)
  • 正規表現の説明。
    1. 正規表現中の . は、ディフォルトでは「改行以外の任意の一文字」にマッチします。
    2. /.{11}/ は「11文字の長さの文字列」にマッチします。
    3. /.{11}/s の最後の s は正規表現の flags で、「正規表現中の . を、改行文字にもマッチさせる」と云う指定です。
    4. aaa ... ccc は、アルファベット9文字と aaabbb の後ろの改行を含めて11文字あります。

原因

  • flags の s は比較的新しい仕様で、Firefox (v76.0.1) が未対応のため。

    (2020/06/01 現在)

  • Firefox の例外

const str = `aaa
bbb
ccc`

str.match(/.{11}/s)
// SyntaxError: invalid regular expression flag s

回避策

  1. flags に m を指定する。(複数行指定)
  2. . の代わりに [^] を使う。
const str = `aaa
bbb
ccc`

str.match(/[^]{11}/m)
  • Q.E.D.

  • [^] の意味が知りたい方は、最寄りの StewEucen までお尋ね下さいな。

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

Javascript 初めてのGSAPアニメーションの使い方 その4

前回の記事はこちら

Javascript 初めてのGSAPアニメーションの使い方 その3

easingの設定

今回はeasingのプロパティを設定していきます。
htmlは前回と同じです(CSSにて色付けしています)

    <div class="row row1">
      <div class="column col1">
        <p>パネル 1 (.circle)</p>
        <div class="circle shape"></div>
      </div>
      <div class="column col2">
        <p>パネル 2 (.square)</p>
        <div class="square shape"></div>
      </div>
      <div class="column col3">
        <p>パネル 3 (.rectangle)</p>
        <div class="rectangle shape"></div>
      </div>
    </div>

前回のjsシートで作成したTimelineMaxにeasingを設定していきます。

Liniar.ease

まずはLinear.easeNoneを動かしてみます。

const tlAnimation = new TimelineMax();

tlAnimation.to('.circle', 1, { x: 100, ease: Linear.easeNone })
.to('.square', 1, { x:100, ease: Linear.easeNone })
.to('.rectangle', 1, { x:100, ease: Linear.easeNone });

Linear.easeNoneは名前の通り無機質な一定の動きでeasingします。

Image from Gyazo

Power2.easeOut

次はPower2.easeOutの動きです。

tlAnimation.to('.circle', 1, { x: 100, ease: Power2.easeOut })
.to('.square', 1, { x:100, ease: Power2.easeOut })
.to('.rectangle', 1, { x:100, ease: Power2.easeOut });

Image from Gyazo

easeOutは動き出しが早く終了に向けてゆっくりに変化します。
power2の数字を大きくすると全体のスピードが変わります。

Power2.easeIn

tlAnimation.to('.circle', 1, { x: 100, ease: Power2.easeIn })
.to('.square', 1, { x:100, ease: Power2.easeIn })
.to('.rectangle', 1, { x:100, ease: Power2.easeIn });

Image from Gyazo

easeInは動き出しが遅く終了に向けて加速します

Power4.easeOut

Powerの数字を最大の4に変えてみます。
(0〜4)で設定可能です。

tlAnimation.to('.circle', 1, { x: 100, ease: Power4.easeOut })
.to('.square', 1, { x:100, ease: Power4.easeOut })
.to('.rectangle', 1, { x:100, ease: Power4.easeOut });

Image from Gyazo

easeOutの動きは同じですが全体のスピードが早くなりました。

Back.easeOut

tlAnimation.to('.circle', 1, { x: 100, ease: Back.easeOut })
.to('.square', 1, { x:100, ease: Back.easeOut })
.to('.rectangle', 1, { x:100, ease: Back.easeOut });

Image from Gyazo

Backを指定するとアニメーションは少しオーバーしてから戻る動きになります。

Elastic.easeOut

tlAnimation.to('.circle', 1, { x: 100, ease: Elastic.easeOut })
.to('.square', 1, { x:100, ease: Elastic.easeOut })
.to('.rectangle', 1, { x:100, ease: Elastic.easeOut });

Image from Gyazo

Elasticはガタッと落ちてきたようなアニメーションを加えることができます

Bounce.easeOut

tlAnimation.to('.circle', 1, { x: 100, ease: Bounce.easeOut })
.to('.square', 1, { x:100, ease: Bounce.easeOut })
.to('.rectangle', 1, { x:100, ease: Bounce.easeOut });

Image from Gyazo

Bounceを設定すると名前の通り重力のあるバウンドのような動きをします。

GSAPのeasingドキュメントはこちらを確認ください

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

Javascript 初めてのGSAPアニメーションの使い方 その4 easing設定

前回の記事はこちら

Javascript 初めてのGSAPアニメーションの使い方 その3 Timelinemax

easingの設定

今回はeasingのプロパティを設定していきます。
htmlは前回と同じです(CSSにて色付けしています)

    <div class="row row1">
      <div class="column col1">
        <p>パネル 1 (.circle)</p>
        <div class="circle shape"></div>
      </div>
      <div class="column col2">
        <p>パネル 2 (.square)</p>
        <div class="square shape"></div>
      </div>
      <div class="column col3">
        <p>パネル 3 (.rectangle)</p>
        <div class="rectangle shape"></div>
      </div>
    </div>

前回のjsシートで作成したTimelineMaxにeasingを設定していきます。

Liniar.ease

まずはLinear.easeNoneを動かしてみます。

const tlAnimation = new TimelineMax();

tlAnimation.to('.circle', 1, { x: 100, ease: Linear.easeNone })
.to('.square', 1, { x:100, ease: Linear.easeNone })
.to('.rectangle', 1, { x:100, ease: Linear.easeNone });

Linear.easeNoneは名前の通り無機質な一定の動きでeasingします。

Image from Gyazo

Power2.easeOut

次はPower2.easeOutの動きです。

tlAnimation.to('.circle', 1, { x: 100, ease: Power2.easeOut })
.to('.square', 1, { x:100, ease: Power2.easeOut })
.to('.rectangle', 1, { x:100, ease: Power2.easeOut });

Image from Gyazo

easeOutは動き出しが早く終了に向けてゆっくりに変化します。
power2の数字を大きくすると全体のスピードが変わります。

Power2.easeIn

tlAnimation.to('.circle', 1, { x: 100, ease: Power2.easeIn })
.to('.square', 1, { x:100, ease: Power2.easeIn })
.to('.rectangle', 1, { x:100, ease: Power2.easeIn });

Image from Gyazo

easeInは動き出しが遅く終了に向けて加速します

Power4.easeOut

Powerの数字を最大の4に変えてみます。
(0〜4)で設定可能です。

tlAnimation.to('.circle', 1, { x: 100, ease: Power4.easeOut })
.to('.square', 1, { x:100, ease: Power4.easeOut })
.to('.rectangle', 1, { x:100, ease: Power4.easeOut });

Image from Gyazo

easeOutの動きは同じですが全体のスピードが早くなりました。

Back.easeOut

tlAnimation.to('.circle', 1, { x: 100, ease: Back.easeOut })
.to('.square', 1, { x:100, ease: Back.easeOut })
.to('.rectangle', 1, { x:100, ease: Back.easeOut });

Image from Gyazo

Backを指定するとアニメーションは少しオーバーしてから戻る動きになります。

Elastic.easeOut

tlAnimation.to('.circle', 1, { x: 100, ease: Elastic.easeOut })
.to('.square', 1, { x:100, ease: Elastic.easeOut })
.to('.rectangle', 1, { x:100, ease: Elastic.easeOut });

Image from Gyazo

Elasticはガタッと落ちてきたようなアニメーションを加えることができます

Bounce.easeOut

tlAnimation.to('.circle', 1, { x: 100, ease: Bounce.easeOut })
.to('.square', 1, { x:100, ease: Bounce.easeOut })
.to('.rectangle', 1, { x:100, ease: Bounce.easeOut });

Image from Gyazo

Bounceを設定すると名前の通り重力のあるバウンドのような動きをします。

GSAPのeasingドキュメントはこちらを確認ください

次回はstaggerTo/staggerFromです。

Javascript 初めてのGSAPアニメーションの使い方 その5 staggerTo/staggerFrom

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

Javascript 初めてのGSAPアニメーションの使い方 その3

前回の記事はこちら

Javascript 初めてのGSAPアニメーションの使い方 その2

Timelinemax

    <div class="row row1">
      <div class="column col1">
        <p>パネル 1 (.circle)</p>
        <div class="circle shape"></div>
      </div>
      <div class="column col2">
        <p>パネル 2 (.square)</p>
        <div class="square shape"></div>
      </div>
      <div class="column col3">
        <p>パネル 3 (.rectangle)</p>
        <div class="rectangle shape"></div>
      </div>
    </div>

Image from Gyazo

上記のようなhtmlをTweenMax.toで動かしてみます。

TweenMax.to('.circle', 1, {x: 100});
TweenMax.to('.square', 1, {x: 100, delay: 1});
TweenMax.to('.rectangle', 1, {x: 100, delay: 2});

Image from Gyazo

パネル1→3に向かってアニメーションをコントロールしていますが
秒数指定では要素が増えるほど記述が煩雑になります。

そこでTimelineMaxを使用してアニメーションの順番をコントロールします。

const tlAnimation = new TimelineMax(); //tlAnimationというタイムラインを作成

tlAnimation.to('.circle', 2, { x: 100 })
.to('.square', 0.5, { x:100 })
.to('.rectangle', 1, { x:100 });

上記のようにタイムラインを作成して取得した要素を.チェーンでつなぐことによってdelayの指定をすることなくアニメーションの順番を指定できます。

順番を指定しているのでアニメーションの時間にも影響を受けません。

実行結果
Image from Gyazo

次回はeasingの設定です。

Javascript 初めてのGSAPアニメーションの使い方 その4

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

Javascript 初めてのGSAPアニメーションの使い方 その3 Timelinemax

前回の記事はこちら

Javascript 初めてのGSAPアニメーションの使い方 その2 Tween.from/Jqueryとの併用

Timelinemax

    <div class="row row1">
      <div class="column col1">
        <p>パネル 1 (.circle)</p>
        <div class="circle shape"></div>
      </div>
      <div class="column col2">
        <p>パネル 2 (.square)</p>
        <div class="square shape"></div>
      </div>
      <div class="column col3">
        <p>パネル 3 (.rectangle)</p>
        <div class="rectangle shape"></div>
      </div>
    </div>

Image from Gyazo

上記のようなhtmlをTweenMax.toで動かしてみます。

TweenMax.to('.circle', 1, {x: 100});
TweenMax.to('.square', 1, {x: 100, delay: 1});
TweenMax.to('.rectangle', 1, {x: 100, delay: 2});

Image from Gyazo

パネル1→3に向かってアニメーションをコントロールしていますが
秒数指定では要素が増えるほど記述が煩雑になります。

そこでTimelineMaxを使用してアニメーションの順番をコントロールします。

const tlAnimation = new TimelineMax(); //tlAnimationというタイムラインを作成

tlAnimation.to('.circle', 2, { x: 100 })
.to('.square', 0.5, { x:100 })
.to('.rectangle', 1, { x:100 });

上記のようにタイムラインを作成して取得した要素を.チェーンでつなぐことによってdelayの指定をすることなくアニメーションの順番を指定できます。

順番を指定しているのでアニメーションの時間にも影響を受けません。

実行結果
Image from Gyazo

次回はeasingの設定です。

Javascript 初めてのGSAPアニメーションの使い方 その4 easing設定

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