20210116のJavaScriptに関する記事は20件です。

Zoom Web SDK を試す

この記事は、Zoom Web SDK を試してみた手順のメモです。

●Web - Client SDKs - Zoom Software Development Kit (Zoom SDK) - Zoom Developer - Technical Documentation and Reference
 https://marketplace.zoom.us/docs/sdk/native-sdks/web

Zoom Web SDK の概要

公式ページの記載を引用して見てみます。

The Web SDK enables the development of video applications powered by Zoom’s core framework inside an HTML5 web client through a highly optimized WebAssembly module.
As an extension of the Zoom browser client, this SDK is intended for implementations where the end user has a low-bandwidth environment, is behind a network firewall, or has restrictions on their machine which would prevent them from installing the Zoom Desktop or Mobile Clients.

引用元: 公式ドキュメントより

説明はいろいろと書いていますが、この SDK を使うと、ブラウザ上で Zoomクライアントを立ち上げることができるもののようです。

Zoom Web SDK をとりあえず試す

公式ドキュメントの「Quick install」を見ると、以下手順でサクッと試せるようです。

パッケージのインストール

まずは、任意のディレクトリでパッケージのインストールを行います。
この手順を試す時、最初は Node.js のバージョンを 12 にしていて途中の手順でエラーが出ました。その後、バージョンを 14 にして試してエラーが出なくなったので、補足しておきます。

npm i @zoomus/websdk

2つのキーの取得について

公式ドキュメントによると Web SDK を使う際に API Key と Secret を取得する必要があるようです。

事前準備についての記述.png

Zoom のApp Marketplaceにアクセスしてログインをし、画面右上の「Develop」のメニューから「Build App」を選択します。

Zoomマーケットプレイス.png

その後の手順については以下の記事などを参照して、JWT の「API Key」と「API Secret」の 2つを取得してください。以下は、Zoom API についての記事ですが、取得するキーは今回の Zoom SDK を用いる場合と同じでした。

●Zoom APIをPostmanで試す | ヤマムギ
 https://www.yamamanx.com/zoom-api-postman/

サンプルアプリのソースの取得

そして、公式ドキュメントの Sample Apps の部分に書かれた以下の手順を進めていきます。

サンプルアプリ.png

まずは最初の2つのコマンドを実行して、ソースを取得し、取得したソースのサブフォルダへと移動します。

git clone https://github.com/zoom/sample-app-web.git --branch master --depth 1
cd sample-app-web/Local

サンプルアプリのソースの書きかえ(キーの設定)

ここで、取得したソースの中の一部を書きかえます。具体的には、先ほど準備した 2つのキーを設定します。

取得したソースの「Local」フォルダ内のさらに下に「js」フォルダがあり、その中に「index.js」というファイルがあります。ソースの取得元の情報でいうと、以下の URL の「13行目」と「19行目」です。

●sample-app-web/index.js at master · zoom/sample-app-web
 https://github.com/zoom/sample-app-web/blob/master/Local/js/index.js

具体的には以下の部分で、const API_KEY = "YOUR_API_KEY"; の部分と const API_SECRET = "YOUR_API_SECRET"; の部分の式の右側に、先ほど取得しておいたキーを設定します。

キーの設定.png

それが完了したら、以下のコマンドを順次実行していきます。

npm install
npm run start

ブラウザでミーティング設定画面を開く

上記のコマンドを入力すると、以下のような文字などが出力されます。

コマンドの実行結果.png

ローカルでポート番号「9999」でアクセスできる状態になったようです。
「0.0.0.0」や「localhost」にポート番号 9999 を指定して、ブラウザでアクセスしてみてください。

そうすると、以下の画面が表示されるので「Meeting Number」と「Meeting Password」のそれぞれに、参加したい Zoomミーティングの ID とパスワードを入力してください。
また、言語設定が「English」となっている部分は「Japanese 日本語」という選択肢があるので、プルダウンから選択して変更します。

Zoom_WebSDKの表示.png

その後、画面のメニュー右端にある「Join」ボタンを押しましょう。
そうすると、以下のように自分が用意したブラウザ上のアプリから Zoom ミーティングに参加できます。

この時、「Meeting Number」の数字の中に半角スペースが入るとうまくいかないようなので、ご注意ください。うまく接続できたら、別タブが開き、以下のように Zoomミーティングへ参加した際の画面が表示されます。

ブラウザでZoom参加の画面へ.png

Zoomミーティングに入った後は、ブラウザ上の UI で画面共有の操作なども行うことができます。

おわりに

今回の内容は単にブラウザ上でZoomミーティング へ参加しただけで、これだけだと、Zoom 標準のブラウザからのミーティング参加機能と同じですが、ソースコードに手を入れれば拡張とかもできるかと思ってます。

詳細は確認できてないので、引き続き情報をチェックしてみようと思います。

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

Zoom Client SDK の Web SDK を試す

この記事は、Zoom Client SDK の Web SDK を試してみた手順のメモです。

●Introduction - Client SDKs
 https://marketplace.zoom.us/docs/sdk/native-sdks/introduction
●Web - Client SDKs - Zoom Software Development Kit (Zoom SDK) - Zoom Developer - Technical Documentation and Reference
 https://marketplace.zoom.us/docs/sdk/native-sdks/web

Zoom Client SDK の Web SDK の概要

公式ページの記載を引用して見てみます。

The Web SDK enables the development of video applications powered by Zoom’s core framework inside an HTML5 web client through a highly optimized WebAssembly module.
As an extension of the Zoom browser client, this SDK is intended for implementations where the end user has a low-bandwidth environment, is behind a network firewall, or has restrictions on their machine which would prevent them from installing the Zoom Desktop or Mobile Clients.

引用元: 公式ドキュメントより

説明はいろいろと書いていますが、この SDK を使うと、ブラウザ上で Zoomクライアントを立ち上げることができるもののようです。

Web SDK をとりあえず試す

公式ドキュメントの「Quick install」を見ると、以下手順でサクッと試せるようです。

パッケージのインストール

まずは、任意のディレクトリでパッケージのインストールを行います。
この手順を試す時、最初は Node.js のバージョンを 12 にしていて途中の手順でエラーが出ました。その後、バージョンを 14 にして試してエラーが出なくなったので、補足しておきます。

npm i @zoomus/websdk

2つのキーの取得について

公式ドキュメントによると Web SDK を使う際に API Key と Secret を取得する必要があるようです。

事前準備についての記述.png

Zoom のApp Marketplaceにアクセスしてログインをし、画面右上の「Develop」のメニューから「Build App」を選択します。

Zoomマーケットプレイス.png

その後の手順については以下の記事などを参照して、JWT の「API Key」と「API Secret」の 2つを取得してください。以下は、Zoom API についての記事ですが、取得するキーは今回の Zoom SDK を用いる場合と同じでした。

●Zoom APIをPostmanで試す | ヤマムギ
 https://www.yamamanx.com/zoom-api-postman/

サンプルアプリのソースの取得

そして、公式ドキュメントの Sample Apps の部分に書かれた以下の手順を進めていきます。

サンプルアプリ.png

まずは最初の2つのコマンドを実行して、ソースを取得し、取得したソースのサブフォルダへと移動します。

git clone https://github.com/zoom/sample-app-web.git --branch master --depth 1
cd sample-app-web/Local

サンプルアプリのソースの書きかえ(キーの設定)

ここで、取得したソースの中の一部を書きかえます。具体的には、先ほど準備した 2つのキーを設定します。

取得したソースの「Local」フォルダ内のさらに下に「js」フォルダがあり、その中に「index.js」というファイルがあります。ソースの取得元の情報でいうと、以下の URL の「13行目」と「19行目」です。

●sample-app-web/index.js at master · zoom/sample-app-web
 https://github.com/zoom/sample-app-web/blob/master/Local/js/index.js

具体的には以下の部分で、const API_KEY = "YOUR_API_KEY"; の部分と const API_SECRET = "YOUR_API_SECRET"; の部分の式の右側に、先ほど取得しておいたキーを設定します。

キーの設定.png

それが完了したら、以下のコマンドを順次実行していきます。

npm install
npm run start

ブラウザでミーティング設定画面を開く

上記のコマンドを入力すると、以下のような文字などが出力されます。

コマンドの実行結果.png

ローカルでポート番号「9999」でアクセスできる状態になったようです。
「0.0.0.0」や「localhost」にポート番号 9999 を指定して、ブラウザでアクセスしてみてください。

そうすると、以下の画面が表示されるので「Meeting Number」と「Meeting Password」のそれぞれに、参加したい Zoomミーティングの ID とパスワードを入力してください。
また、言語設定が「English」となっている部分は「Japanese 日本語」という選択肢があるので、プルダウンから選択して変更します。

Zoom_WebSDKの表示.png

その後、画面のメニュー右端にある「Join」ボタンを押しましょう。
そうすると、以下のように自分が用意したブラウザ上のアプリから Zoom ミーティングに参加できます。

この時、「Meeting Number」の数字の中に半角スペースが入るとうまくいかないようなので、ご注意ください。うまく接続できたら、別タブが開き、以下のように Zoomミーティングへ参加した際の画面が表示されます。

ブラウザでZoom参加の画面へ.png

Zoomミーティングに入った後は、ブラウザ上の UI で画面共有の操作なども行うことができます。

おわりに

今回の内容は単にブラウザ上でZoomミーティング へ参加しただけで、これだけだと、Zoom 標準のブラウザからのミーティング参加機能と同じですが、ソースコードに手を入れれば何らか拡張とかもできるかと思ってます。

詳細は確認できてないので、引き続き情報をチェックしてみようと思います。また、今回使った「Client SDK」以外の SDK(例えば、「Fully Customizable SDK」)も見てみようと思います。

●Introduction - Client SDKs
 https://marketplace.zoom.us/docs/sdk/native-sdks/introduction
●Introduction - Fully Customizable SDKs
 https://marketplace.zoom.us/docs/sdk/custom/introduction

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

TensorFlow.js version of PoseNetで手旗信号読取りサイト

はじめに

初記事です。
以前Coral USB Acceleratorを購入したので、Raspberry Pi 4でPoseNetを動かして手旗信号を読み取るようなものを作って自己満足してました。
ボーイスカウトの指導者などをやっているので、スカウトたちにもやらせてみたいな、などと思ってたら、
Real-time Human Pose Estimation in the Browser with TensorFlow.js
ってのがあるんですね。すごい。

というわけでタイトルの通り、読取りサイトを作ってみたので、その備忘録です。ベンチャー/ローバースカウトが興味を持ってくれるの期待して…
snap.png

参考にしたもの

とはいえWebサイトやJavaScriptなどはほぼ触ったことないので各所を参考に。

PoseNet

何はともあれ姿勢推定してくれるPoseNetを、ということで、

Github - tensorflow/tfjs-models

で公開されているdemosからcamera.jsを丸パク…

実装の詳細説明は割愛しますが、poseDetectionFrame()内でPoseNetから17関節の座標を取得して骨人間とかを描画しているところに手旗信号の姿勢を判定するロジックを入れます。

ポーズを判定する

判定といってもどうすれば良いのか、ということでCoral Acceleratorで実装したときに参考にさせていただいた

カメラモジュールから動画撮影してポーズを判定する

から、肘と肩の内角を計算して、その角度の状態からポーズを判定します。

手旗信号

オールドスカウトなので手旗信号は一応マスターしているつもりですが、再確認&参考に下記サイト。

ポーラスター ~ボーイスカウト指導者応援サイト

ボーイスカウト関連のかわいいイラストなどを提供してくれているサイトなのですが、手旗信号関連の参考イラストもたくさん。

基本的には数字の1~14までを表すポーズ(原画)があり、それを組み合わせてカタカナを表現するのが手旗信号です。

手旗信号の姿勢を判定する

さて本題の手旗信号認識です。

各関節の内角を計算する

まず1~14の数字を表す原画を判定するのですが、関節の角度を求めてその状態からどういうポーズをしているかを判定します。
ので3つの関節の座標から真ん中の関節の内角を求める関数。

function calculateInternalAngle(keypoints, point0, point1, point2)
{
    var a = {x:keypoints[point1].position.x-keypoints[point0].position.x, y:keypoints[point1].position.y-keypoints[point0].position.y};
    var b = {x:keypoints[point2].position.x-keypoints[point0].position.x, y:keypoints[point2].position.y-keypoints[point0].position.y};

    var dot = a.x * b.x + a.y * b.y;

    var absA = Math.sqrt(a.x*a.x + a.y*a.y);
    var absB = Math.sqrt(b.x*b.x + b.y*b.y);

    var cosTheta = dot/(absA*absB);
    var theta = Math.acos(cosTheta) * 180 / Math.PI;

    return theta;
}

手旗信号はほぼ腕の状態で判定するので、両肩と両肘の角度を求めておきます。

function getAngles(keypoints, minConfidence) {
    var angles = [];

    // left elbow
    deg = calculateInternalAngle(keypoints, LEFTELBOW, LEFTWRIST, LEFTSHOULDER, minConfidence);
    angles.push(deg);

    :
    // 省略
}

内角なので腕を上げている場合の角度か、下げている場合の角度かを求めておきます。

function get_positions(keypoints, minConfidence) {
    positions = [];
    if((keypoints[LEFTELBOW].score > minConfidence) || (keypoints[LEFTWRIST].score > minConfidence)){
        if((keypoints[LEFTELBOW].score > minConfidence) && (keypoints[LEFTELBOW].position.y < keypoints[LEFTSHOULDER].position.y) ||
        (keypoints[LEFTWRIST].score > minConfidence) && (keypoints[LEFTWRIST].position.y < keypoints[LEFTSHOULDER].position.y)){
            positions.push(UP);
        }else{
            positions.push(DOWN);
        }

    :
    // 省略
}

腕の状態から原画を判定

原画の一覧はポーラスターさんを参考に。
「2」は右上げと左上げの2種類、「11」は降り下げるジェスチャーなので上の状態と下の状態の2種類。
基本姿勢(原姿)を「0」として全17姿勢を判定。

function judge_genkaku(keypoints, minConfidence){
    angles = getAngles(keypoints, minConfidence);
    positions = get_positions(keypoints, minConfidence);

    if (150 < angles[ANG_LELBOW] &&
        150 < angles[ANG_RELBOW] &&
        (80 < angles[ANG_LSHOULDER] && angles[ANG_LSHOULDER] < 130) &&
        (80 < angles[ANG_RSHOULDER] && angles[ANG_RSHOULDER] < 130) &&
        positions[LEFTHAND_UPDOWN] == DOWN &&
        positions[RIGHTHAND_UPDOWN] == DOWN)
        return 0;

    :
    // 省略
}

角度は微妙な調整が必要だったので、コテコテのコードですが?
判定した数値を配列にとっておきます。

カナ(五十音)を判定

手旗信号は原画を続けて打って、基本姿勢(原姿)に戻るとカナ1つを打ち終えたと判断します。原画の形を組み合わせるとカタカナの形になります。
こちらもポーラスターさんの一覧を参考に。

function judge_kana(text, genkakus) {
    strGen = genkakus.toString();

    if (strGen == [9, 3])
        return text + '';
    else if (strGen == [3, 2])
        return text + '';

    :
    // 省略
}

という具合で原画の組み合わせをカナにします。

以上、手旗信号判定の簡単な流れでした。

最後に

ソースコードはこちらです。
Github

読取りサイトはこちら
https://hiroshi32yoshida.github.io/posenet_flag_sign_js/

うまく読み取るためのコツは注意点としてサイトに書いてありますが、
・背景がゴチャゴチャしていないこと
・服装と背景のコントラストがあること
・上半身の関節が隠れていないこと(手旗を持ってると判定できないことがあります)
などです。6とか9とか11が読取り渋いですが、条件良ければ上手く読み取ってくれます。
練習程度にはなると思います。

ただ角度からの判定が微妙なことが多いので、ml5.jsの機械学習で原画をトレーニングして判定するVerも作成してみました。機械学習とかよくわかっていませんが。
次回記事で紹介します。

以上です。

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

Luminousが上手く動かない件

ウェブサイトの復旧作業の中で、ギャラリーのページをカスタマイズする作業の中で、jQuery不要なLightboxスクリプトLuminousを知りました。

jQueryを使わないので、小規模のHTMLを手入力で作ったようなウェブサイトのギャラリーページのカスタマイズに最適だと思いました。

でも、下記のコードを設置しても、動かないです。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/luminous-lightbox@2.3.2/dist/luminous-basic.min.css">
<script src="https://cdn.jsdelivr.net/npm/luminous-lightbox@2.3.2/dist/luminous.min.js"></script>
<script>
new LuminousGallery(document.querySelectorAll('.luminous'));
</script>

複数の画像に対応するため、このコードを使いました。

このコードは、こちらの記事で見つけました。
jQuery不要なLightbox代替スクリプトLuminousが本当に軽量

こちらの記事を読んで、原因が分かりました。
Luminousを適用したいaタグの直下に、このコードを設置する必要があります。
理由は、他のCSSの影響を受けないように、最後にLuminousのCSSを適用させるためです。

luminous がうまく動かない

設置例

<div>
 <ul>

        <li><a class="luminous" href="./img/1_big.jpg"><img src="./img/1.jpg" alt="image" /></a></li>
        <li><a class="luminous" href="./img/2_big.jpg"><img src="./img/2.jpg" alt="image" /></a></li>

    </ul>


    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/luminous-lightbox@2.3.2/dist/luminous-basic.min.css" />
    <script src="https://cdn.jsdelivr.net/npm/luminous-lightbox@2.3.2/dist/luminous.min.js"></script>
    <script>new LuminousGallery(document.querySelectorAll('.luminous'));</script>

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

vuetifyで「[Vue warn]: The .native modifier for v-on is only valid on components but it was used on <div>.」

起きたこと

私の場合はカレンダー・コンポーネントを使った時に発生しました。

起動はしますが、ブラウザ上でwarningが出ます。

[Vue warn]: The .native modifier for v-on is only valid on components but it was used on <div>.

解決方法

その1 vuetifyのバージョンをあげる

このwarningについてはGitHubでissueがあがっていて、解決済です。(v-iconでも発生するようです)

[Bug Report] warning with VueJS 2.6.11 · Issue #9999 · vuetifyjs/vuetify
[Bug Report] Button wrapper missing for faSvg clickable v-icons · Issue #10623 · vuetifyjs/vuetify

修正のPRは↓で、v2.3.14でリリース済です。

fix(VIcon): render a button element around clickable component icons by KaelWD · Pull Request #12148 · vuetifyjs/vuetify

というわけで、v2.3.14に上げれば発生しなくなります。

その2 warningを無視するコードを入れる

様々な事情でバージョンをあげるのが難しい場合、無理やり無視する事も可能です。(warningなので動作に影響は無いようです)

該当のコードはこちら

Nuxtの場合は、pluginとして書いてあげるとわかりやすいです。

ignore-warning.ts
const ignoreWarnMessage = 'The .native modifier for v-on is only valid on components but it was used on <div>.';
Vue.config.warnHandler = function (msg, vm, trace) {
  // `trace` is the component hierarchy trace
  if (msg === ignoreWarnMessage) {
    msg = null;
    vm = null;
    trace = null;
  }

該当のwarningメッセージを握りつぶします。

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

[JavaScript] たのしいタグ付きテンプレート文字列

これはなに?

タグ付きテンプレート文字列で色々遊んでみる記事です。

タグ付きテンプレート文字列とは?

文字列と言っていますが、文字列と言うよりは、関数の呼び出しかたです。

例えば、ある関数があったとしまして

function myFunc(){}

普通はこのように呼び出しますが、

myFunc();

このように呼び出すことを、タグ付きテンプレート文字列と言います。

myFunc``

ここで使用した関数を特別にタグ関数と言います。

タグ関数に渡される引数

タグ関数に渡される引数は、文字列をパースした結果になります。

第1引数は配列です。式以外の部分文字列が入っています。
第2引数以降は可変長です。式を評価した結果になります。

実際にどのような引数が渡されるのか確認するために、このような関数を定義しました。

function myTag(strings, ...keys) {
  console.log('strings = ', strings);
  console.log('keys = ', keys);
}

呼び出してみます。

myTag`abc`;
/* 結果 :
strings = (1)["abc"]
keys = (0)[]
*/
myTag`Java${100}Script`;
/* 結果 :
strings = (2)["Java" ,"Script"]
keys = (1)[100]
*/
myTag`pl ${1 + 1} mu ${2 * 2} di ${3 / 3}`;
/* 結果 :
strings = (4)["pl " ," mu " ," di " ,""]
keys = (3)[2 ,4 ,1] 
*/

組み込み関数をタグ関数として使ってみる

こんなこともできますが、意味はないですねww

parseInt`abc`
// 結果 : NaN

parseInt`010`
// 結果 : 10

encodeURI`\n`
// 結果 : '%0A'

色々な値を返すタグ関数

タグ関数はただの関数なので、Promiseを返したり、関数を返したりもできますね。

意味なくPromise返してみたり。

function myTag(strings) {
  return new Promise((resolv) => {
    resolv(strings.join('|');
  })
}
myTag`a${0}${1}b`.then(console.log);
// 結果 : 'a||b'

関数を返してみたり。

function myTag(strings) {
  return function(d) {
    return strings.join(d);
  }
}
console.log(myTag`a${0}${1}b`('---'));
// 結果 : 'a------b'

おまけ : TypeScriptでタグ関数を定義する

引数をTemplateStringsArrayにします。

function myTag(strings: TemplateStringsArray, ...keys: Array<any>): any {
  // (snip)
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IEでconsole.logは使用できない。

index.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>JavaScriptドリル</title>
</head>
<body>
  <script src="script.js"></script>
</body>
</html>
script.js
const a = 10;
const b = 20;
sum = a + b;
console.log('定数aは,'+ a +',定数bは,'+ b + ',合計は,' + sum);

IE

image.png

Chrome

image.png

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

JavaScriptの関数とオブジェクトにおける最低限の知識

本記事の目的

この記事は自身のJavaScriptに対する知識の定着を図るため、また、同じような初学者が理解するきっかけになればと思い作成しています。間違い等ございましたらお手数ですがご連絡いただけますと幸いです。

JavaScriptの概要とメリット

プログラミング言語の一つ。クライアントサイドに用いられる。
例えば某サイトで服を購入しようと考えた際に服→メンズ→サイズ...と選択する際にマウスのポインタを選択肢上に移動させると次々に新しい選択項目が表示されるようなものをイメージしてもらうとわかりやすいかと思う。
上記の例で話すのであれば、ページの移動なしで画面の表示を変更することができるサーバーとの通信回数を減らすことができるというメリットがある。

コンソールパネルを用いてJavaScriptを実行する

JavaScriptのコードを実行し、その結果を表示するためにはデベロッパーツールを使用する。一般的なブラウザに付属しており誰でも使用することができる。
ブラウザ上で右クリック→検証を押す、もしくはcommand + option + cキーで表示可能。
また、デベロッパーツールは検証ツールと言われ、以下のような作業ができる。

  • 表示しているサイトのHTMLの要素の確認・編集
  • 表示しているサイトのCSSの確認・編集
  • JavaScriptの実行

JavaScriptにおける変数について

var,const,letの三種類を用いて変数を定義する。
それぞれの違いは以下の通り。
var・・・再定義と再代入が可能。
const・・・再定義と再代入ともに不可能。
let・・・再代入は不可能だが、再定義は可能。

varの使用(検証ツールのconsoleより)

'var'
var sample = "Hello"
console.log(sample)
Hello(←が表示される)

'再定義'
var sample = "再定義"
console.log(sample)
再定義(←が表示される)

'再代入'
sample = "再代入"
console.log(sample)
再定義(←が表示される)

constの使用(検証ツールのconsoleより)

'const'
var sample = "Hello"
console.log(sample)
Hello(←が表示される)

'再定義'
var sample = "再定義"
console.log(sample)
エラーになる

'再代入'
sample = "再代入"
console.log(sample)
エラーになる

letの使用(検証ツールのconsoleより)

'let'
let sample = 'Hello'
console.log(sample)
Hello(←が表示される)

'再定義'
let sample = "再定義"
console.log(sample)
エラーになる
※2021年1月16日現時点で、Google chromeだとエラーにならない。バージョンアップにて変更した模様?
要注意です!

'再代入'
let sample = "再定義"
console.log(sample)
再定義(←が表示される)

オブジェクト

プロパティを複数集めたものの集合体のこと。また、プロパティとはキーとバリューによって構成されている。

オブジェクトの値を取得するためにはオブジェクト名.プロパティ名のように記述することで取得することができる。

'例'
const sample = { color: 'blue'}
console.log(sample.color)
blue(←が表示される)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者】#1 Reactの基礎とMaterial-UI使って綺麗に作ってみる

Reactでかっこいいページを作りたい!

  • 国内ではVue.jsの人気も高いですが、Reactの方が海外では流行っている
  • React Server Componentsなるものが出てきた
  • 下火気味?だけどReact Nativeをもしやる事になっても、少しは対応しやすくなるかも

ということで、
バックエンドが専門の私ですが、JavaScriptのスキルアップかねてReactをはじめました。

デザインはMaterial UIに任せよう

デザイナーでもフロントエンジニアでもない私は、CSS Frameworkに頼ります!
情報量の多さから、Material UIを使うのが良いと判断して勉強開始。

ReactもMaterial UIも英語読めないと対処しにくいところあるので、
誰かの助けになればいいなと思って作りながらメモしながら書いてます。

2020-15.png

トピック

対象:

  • react初心者
  • CSS苦手な人へ
  • Reactやってみたいけど、デザインが…
  • とりあえず見た目が及第点欲しい人の基礎の基礎

やること

  • インストール、react-create-appから始める 
  • ヘッダー作成
  • グリッド(レスポンシブ)
  • Reactでのアイコン付け方
  • カード、ボタン、画像を使って見た目を整える

環境作る

前提:nodejs入れてない人はインストールしてください。nodejsと調べて、LTSという方をダウンロードしてインストール進めればOKなはずです。

Reactのファイル作りたいところに移動してから、以下でアプリ作ります。
名前はmaterial-reactで作ってみます。

$ npx create-react-app material-react
$ cd material-react/node_modules

サーバーを起動。

$ npm start

reactのマークがくるくる回ってたらOK

スクリーンショット 2021-01-15 1.06.38.png

Material-UI使えるようにする

公式サイトの通りにインストールしていく。

$ npm install @material-ui/core

必須ではないけど、以下をやっていきます。
index.htmlにフォントを追記。titleタグ付近にでも。

index.html
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />

'Roboto'つかうと書いてあるので、フォントの優先度をあげます。

index.css
body {
  margin: 0;
  font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

アイコンを使うので、インストール。

$ npm install @material-ui/icons

デフォルトのページいらないので変更

軽く説明すると、

  • index.htmlの<div id="root"></div>この要素をJSでいじりまくってタグとか生成して作っていく感じ
  • App.jsがJSファイルの親玉?先祖?みたいな感じ
  • componentsフォルダ作って、その中に部品となるもの作り、App.jsにコンポーネントを埋め込んで作っていくが一般的。例えば、サイドバー用のJS、ヘッダー用のJSとか。

image.png

では、デフォルトページを修正。
初期状態では、コンポーネントが存在しないで、App.jsに全てが書いてあります。
全部をApp.jsに作ることもできますが、
Reactは機能ごとにコンポーネントに分けて作るのが普通です。

では、Reactマークのくるくるさせるための記述と、
デザインが、App.jsとApp.cssに書かれていますので、
Material-UI使うために、白紙のページにします。

白紙ページにする

  1. App.css中身全部消す
  2. App.jsの項目削除して真っ白なページにする
App.js
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      真っ白
    </div>
  );
}

export default App;

Reactマークが消えて、真っ白とだけ表示されてればOK

グリッド、レスポンシブ

Bootstrapと同じですね。
画面を横方向を12分割にしてその要素は、12個ののうち、何個分の横幅を使うの?
ってやつです。

flexbox使うより簡単にレスポンシブ対応できるので、専門でない人には便利です!

スクリーンショット 2021-01-15 17.06.04.png

https://material-ui.com/components/grid/#grid
※いまいちわからない方は↑ページ開いて、ブラウザのウィンドウを小さくしたり、大きくしたりしてみてください。

material-react/src/App.js
import './App.css';
import { Grid } from '@material-ui/core';

function App() {
  return (
    <Grid container direction="column">
      <Grid item>
        item1item1item1item1item1item1item1item1item1item1item1item1
      </Grid>
      <Grid item container>
        <Grid sm={2} />
        <Grid xs={12} sm={8}>
          item2item2item2item2item2item2item2item2item2item2item2item2
          item2item2item2item2item2item2item2item2item2item2item2item2
          item2item2item2item2item2item2item2item2item2item2item2item2
          item2item2item2item2item2item2item2item2item2item2item2item2
          item2item2item2item2item2item2item2item2item2item2item2item2
        </Grid>
        <Grid sm={2} />
      </Grid>
    </Grid>
  );
}

export default App;

<Grid container>という親の中に<Grid item>という子を入れていきます。
<Grid item container>分割された子の要素の中をさらに分割するため、container入れてます。

<Grid sm={2} />これが二つありますが、これが左右の余白です。
xsが超小さい画面の時には余白なし。小さい画面以上の時は、 2/12の空白を入れてます。
2/12が二つなので、本体は残りの8/12。

なので<Grid xs={8}>

すると、こんな感じ。
スクリーンショット 2021-01-15 2.02.49.png

ブラウザの画面を手動で大きくしたり、小さくしたりして変化するか試してください。

ヘッダーを作る

srcのなかにコンポーネントフォルダを作ります。
その中にHeader.js作成

$ mkdir src/components
material-react/src/components/Header.js
import React from 'react'

function Header() {
    return (
        <div>
            ヘッダー
        </div>
    )
}

export default Header

ヘッダーコンポーネントをApp.jsに追加

作ったヘッダーコンポーネントを親玉のApp.jsの中につっこみます。

material-react/src/App.js
import Header from './components/Header';

・・・

    <Grid container direction="column">
      <Grid item>
        <Header />
      </Grid>
...

単純に「ヘッダー」という文字が出るだけですが、
ヘッダーコンポーネント(ヘッダーの部品)が読み込まれてます。

スクリーンショット 2021-01-15 3.05.16.png

これだとしょぼいので、公式サイトから使用例をパクって改造します。

https://material-ui.com/components/app-bar/
https://material-ui.com/ja/api/app-bar/

基本的なものだけで構成してみました。

material-react/src/components/Header.js
import { AppBar, Toolbar, Typography } from '@material-ui/core'
import React from 'react'

function Header() {
    return (
        <AppBar position="static">
            <Toolbar>
                <Typography>ヘッダー</Typography>
            </Toolbar>
        </AppBar>
    )
}

export default Header

ヘッダーなのでposition="static"にしてます。
これ指定しないと、ヘッダーに要素が重なってしまいます。
スクリーンショット 2021-01-15 3.14.36.png

アイコンをつける

https://material-ui.com/components/material-icons/

検索して良いアイコンを探す。
好きなアイコンをクリックすると下の画像みたいな表示が出る。

今回はこのシルエットみたいなアイコンを試しに貼り付けてみます。
スクリーンショット 2021-01-15 3.17.25.png

importのところをコピーしてHeader.jsに貼り付け。iconを使えるようにします。

Header.js
import AccountCircleOutlinedIcon from '@material-ui/icons/AccountCircleOutlined';

        <AppBar position="static">
            <Toolbar>
                <Typography>ヘッダー</Typography>
                <AccountCircleOutlinedIcon />
            </Toolbar>
        </AppBar>

スクリーンショット 2021-01-15 3.19.26.png
アイコンが出ました。

ただ、普通、こういうのって、左端にありますよね。
ということで、CSSをいじって、調整します。

Material-UIでCSSを追加

makeStyleを使ってみましょう。
Material-UIで作るときにCSSを書くとき使うもののようです。
ファイル一つにたくさんのCSS書くより、コンポーネントに分かれてるし、そこにCSS書けばよくない?
ってことで使われるらしいです。(?)
当然、JSで書くので計算とか、条件とか、Scssっぽく?便利にデザインを適用もできますね。

Header.js
import React from "react";
import { AppBar, Toolbar, makeStyles, Typography } from "@material-ui/core";
import AcUnitRoundedIcon from "@material-ui/icons/AcUnitRounded";

const useStyles = makeStyles(() => ({
  typographyStyles: {
    flex: 1
  }
}));

const Header = () => {
  const classes = useStyles();
  return (
    <AppBar position="static">
      <Toolbar>
        <Typography className={classes.typographyStyles}>
          Anthony sistilli
        </Typography>
        <AcUnitRoundedIcon />
      </Toolbar>
    </AppBar>
  );
};

export default Header;

classNameはHTMLのclassのことです。reactの中ではclassNameと書きます。

で、クラスをmakeStyleで生成するということで↓追加。

const useStyles = makeStyles(() => ({
  typographyStyles: {
    flex: 1
  }
}));

useStyles作って

const classes = useStyles();でclasses作って、
classNameにオブジェクトのキー名typographyStyles
を設定しているので

classes.typographyStylesで、ここの要素にflex: 1を適用するということです。

スクリーンショット 2021-01-15 3.35.59.png

コンテンツ部分のコンポーネントを作る

$ touch src/components/Content.js

Content.js作成。App.js
ヘッダーの時と同じようにインポートと、<Content />とを入れる。

App.js
import Content from './components/Content';

...

    <Grid container direction="column">
      <Grid item>
        <Header />
      </Grid>
      <Grid item container>
        <Grid sm={2} />
        <Grid xs={12} sm={8}>
          <Content />
        </Grid>
        <Grid sm={2} />
      </Grid>
    </Grid>

コンテンツ部分は
「カード」
を使います。

カードの部分もコンポーネントにしたいのでBodyCard.jsを作ります。

$ touch src/components/BodyCard.js

Contentコンポーネントの中にBodyCardコンポーネントが入れ子になってます。
App.jsからみるとBodyCardコンポーネントは孫に当たります。

では、Outlined Cardというのを公式ページからパクってきて、

https://material-ui.com/ja/components/cards/

src/components/BodyCard.js
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';

const useStyles = makeStyles({
    bullet: {
      display: 'inline-block',
      margin: '0 2px',
      transform: 'scale(0.8)',
    },
    title: {
      fontSize: 14,
    },
    pos: {
      marginBottom: 12,
    },
});


function BodyCard() {
    const classes = useStyles();
    const bull = <span className={classes.bullet}></span>;
    return (
        <Card variant="outlined">
            <CardContent>
            <Typography className={classes.title} color="textSecondary" gutterBottom>
                Word of the Day
            </Typography>
            <Typography variant="h5" component="h2">
                be{bull}nev{bull}o{bull}lent
            </Typography>
            <Typography className={classes.pos} color="textSecondary">
                adjective
            </Typography>
            <Typography variant="body2" component="p">
                well meaning and kindly.
                <br />
                {'"a benevolent smile"'}
            </Typography>
            </CardContent>
            <CardActions>
            <Button size="small">Learn More</Button>
            </CardActions>
        </Card>
    );
}

export default BodyCard

カードを一つだけ表示させてみます。

Content.js
import React from 'react'
import BodyCard from './BodyCard'

function Content() {
    return (
        <BodyCard />
    )
}

export default Content

一つだけは表示できてますね。
スクリーンショット 2021-01-15 4.08.39.png

3個表示すると

Content.js
import { Grid } from '@material-ui/core'
import React from 'react'
import BodyCard from './BodyCard'

function Content() {
    return (
        <Grid container>
            <Grid item xs={4}> 
                <BodyCard />
            </Grid>
            <Grid item xs={4}> 
                <BodyCard />
            </Grid>
            <Grid item xs={4}> 
                <BodyCard />
            </Grid>
        </Grid>
    )
}

export default Content

スクリーンショット 2021-01-15 4.12.40.png

スペースを入れて見た目調整

くっついていて見た目良くないので、調整します。

Material-UI公式ページのgridの項目で
spacingの項目で確認してからやるといいかもしれません。

スクリーンショット 2021-01-15 4.21.16.png

spacing={2}足す。もっと開けたいときは数字を大きくするといいです。

Content.js
return (
        <Grid container spacing={2}>
            <Grid item xs={4}> 
                <BodyCard />
            </Grid>
            <Grid item xs={4}> 
                <BodyCard />
            </Grid>
            <Grid item xs={4}> 
                <BodyCard />
            </Grid>
        </Grid>
    )

あとは、好みで調整してみてください。
画面のサイズによってどのような横幅にするかをxs, sm md, lg, xlで決められます。
Bootstrapと同じ感じ。

今回は、スマホなど画面が非常に小さくなると、カードが一つずつ。smサイズで1行に3個並べるようにしました。

スクリーンショット 2021-01-15 4.27.29.png

↓xsサイズにすると

スクリーンショット 2021-01-15 4.31.30.png

カードの見た目改造 カードのヘッダー、クリックできるアイコン

カードの中のヘッダーとクリックできるアイコンを用意します。
今回は⭐️アイコンつけます。

Card要素の一番初めにCardHeaderを追加して、
⭐️アイコンを調べてインポート(StarBorderOutlinedIcon)。
該当箇所に貼り付けます。

BodyCard.js
import CardHeader from '@material-ui/core/CardHeader';
import Avatar from '@material-ui/core/Avatar';
import IconButton from '@material-ui/core/IconButton';
import StarBorderOutlinedIcon from '@material-ui/icons/StarBorderOutlined';

        <Card variant="outlined">
            <CardHeader
                avatar={
                <Avatar aria-label="recipe" className={classes.avatar}>
                    R
                </Avatar>
                }
                action={
                <IconButton aria-label="settings">
                    <StarBorderOutlinedIcon />
                </IconButton>
                }
                title="Shrimp and Chorizo Paella"
                subheader="September 14, 2016"
            />

IconButtonがあるとホバーした時に違い出て、押せる感じになってます。
スクリーンショット 2021-01-15 15.16.25.png

アバターと画像は適当な画像をランダムで取得できるサービスあったのでテストで使います。

https://joeschmoe.io/
https://picsum.photos

BodyCard.js
import { CardMedia } from '@material-ui/core';

.
.
.

function BodyCard(props) {
    const { avatarUrl, title, subheader, text, imageUrl } = props;
    const classes = useStyles();
    const bull = <span className={classes.bullet}></span>;
    return (
        <Card variant="outlined">
            <CardHeader
                avatar={<Avatar src={avatarUrl} />}
                action={
                <IconButton aria-label="settings">
                    <StarBorderOutlinedIcon />
                </IconButton>
                }
                title={title}
                subheader={subheader}
            />
            <CardMedia style={{ height: "150px" }} image={imageUrl} />
            <CardContent>
            <Typography variant="body2" component="p">
                {text}
            </Typography>
            </CardContent>
            <CardActions>
            <Button size="small">詳細をみる</Button>
            </CardActions>
        </Card>
    );
}
Content.js
.
.
.

function Content() {
    return (
        <Grid container spacing={2}>
            <Grid item xs={12} sm={4}> 
                <BodyCard 
                title="タイトル1" 
                subheader="サブヘッダー1" 
                avatarUrl="https://joeschmoe.io/api/v1/random" 
                imageUrl="https://picsum.photos/150"
                text="カードの説明1" />
            </Grid>
            <Grid item xs={12} sm={4}> 
                <BodyCard />
            </Grid>
            <Grid item xs={12} sm={4}> 
                <BodyCard />
            </Grid>
            <Grid item xs={12} sm={4}> 
                <BodyCard />
            </Grid>
        </Grid>
    )
}

今は、propsを一個めのカードにしか与えてないので、このようになってます。

スクリーンショット 2021-01-15 15.48.56.png

では、残りのカードにも表示されるようにして、

Content.js
import { Grid } from '@material-ui/core'
import React from 'react'
import BodyCard from './BodyCard'

const cardContents = [
    {
        title: "タイトル1",
        subheader: "サブヘッダー1",
        avatarUrl: "https://joeschmoe.io/api/v1/random",
        imageUrl: "https://picsum.photos/150"
    },
    {
        title: "タイトル2",
        subheader: "サブヘッダー2",
        avatarUrl: "https://joeschmoe.io/api/v1/random",
        imageUrl: "https://picsum.photos/150"
    },
    {
        title: "タイトル3",
        subheader: "サブヘッダー3",
        avatarUrl: "https://joeschmoe.io/api/v1/random",
        imageUrl: "https://picsum.photos/150"
    },
    {
        title: "タイトル4",
        subheader: "サブヘッダー4",
        avatarUrl: "https://joeschmoe.io/api/v1/random",
        imageUrl: "https://picsum.photos/150"
    },
]

function Content() {
    const getCardContent = getObj => {
        return (
            <Grid item xs={12} sm={4}>
                <BodyCard {...getObj} />
            </Grid>
        );
    };
    return (
        <Grid container spacing={2}>
            {cardContents.map(contentObj => getCardContent(contentObj))}
        </Grid>
    )
}

export default Content

同じ画像になりますができました。
次回はデータをAPIで取得して表示する方法について触れます。

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

yarnを最新化する

はじめに

yarnを最新化する時に手間取ったので、備忘録としてメモを残す。

結論としては、yarnのアンインストール->インストールだけでは不十分で、local/bin/配下のyarn,yarnpkgを削除する必要がある。

開発環境

  • macOS Big Sur(バージョン 11.1)

最新化する手順

  1. /usr/local/bin配下のyarn,yarnpkgを削除する

  2. /usr/local/lib/node_modules/yarn/bin配下のyarn,yarnpkgを最新化する

  3. $ npm uninstall --global yarn
    $ npm install --global yarn
    

あと書き

npm uninstallは/usr/local/lib/node_modules/yarn/bin配下のファイルを削除するのであって、/usr/local/binを更新してくれるわけではなさそう。

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

クレジットカードの公開鍵の書き方

javascriptファイルへ、クレジット決済会社の公開鍵を書く方法

① クレジット決済会社のサイトから、公開鍵を取得する
② .jsファイルへ記述する
例)
javascript/〜.js

const pay = () => {
~.setPublicKey("pk_test_******************");

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

繰り返し利用可能なドロワーをJavaScriptで書く

See the Pen 繰り返し利用可能なドロワーをJavaScriptだけで書く by 熊瀬川直也 (@momonoki1990) on CodePen.

使い方

  • ドロワーの開閉ボタンに data-drawer-btn="true" data-drawer-target="{ドロワーのid}" をセットする
  • ドロワー本体に id class="drawer" data-drawer="true" をセットする。
  • ドロワーを閉じるボタンに class="drawer" data-drawer-close-btn="true" data-drawer-target="{ドロワーのid}" をセットする

ベタがき

drawer.html
<div id="main">
  <div class="my-drawer1">
    <button class="drawer-btn" data-drawer-btn="true" data-drawer-target="drawer1">
      開閉ボタン1
    </button>
    <div class="drawer" id="drawer1" data-drawer="true">
      <div>
        <button class="drawer-close-btn" data-drawer-close-btn="true" data-drawer-target="drawer1">x</button>
      </div>
      <div class="drawer-menu">
        <div class="drawer-title">
          ドロワー1です
        </div>
      </div>
    </div>
  </div>
  <div class="my-drawer2">
    <button class="drawer-btn" data-drawer-btn="true" data-drawer-target="drawer2">
      開閉ボタン2
    </button>
    <div class="drawer" id="drawer2" data-drawer="true">
      <div>
        <button class="drawer-close-btn" data-drawer-close-btn="true" data-drawer-target="drawer2">x</button>
      </div>
      <div class="drawer-menu">
        <div class="drawer-title">
          ドロワー2です
        </div>
      </div>
    </div>
  </div>
  <div class="my-drawer3">
    <button class="drawer-btn" data-drawer-btn="true" data-drawer-target="drawer3">
      開閉ボタン3
    </button>
    <div class="drawer" id="drawer3" data-drawer="true">
      <div>
        <button class="drawer-close-btn" data-drawer-close-btn="true" data-drawer-target="drawer3">x</button>
      </div>
      <div class="drawer-menu">
        <div class="drawer-title">
          ドロワー3です
        </div>
      </div>
    </div>
  </div>
</div>

<style>
  .drawer {
    position: fixed;
    top: 0;
    right: 0;
    padding: 1rem;
    background-color: red;
    width: 300px;
    height: 100%;
    z-index: 100;
    transition: all 0.2s;
    transform: translate(340px);
  }

  .drawer-open {
    transform: translate(0px);
  }

  .drawer-close-btn {
    position: absolute;
    top: 0;
    left: 0;
  }

  .fade-layer {
    position: absolute;
    top: 0px;
    left: 0px;

    width: 100%;
    height: 100%;

    background-color: #000000;
    opacity: 0.5;
    z-index: 99;
  }

  .d-none {
    display: none;
  }
</style>

<script type="text/javascript">

  // フェードレイヤーを生成しておく
  window.addEventListener('DOMContentLoaded', () => {
    const main = document.getElementById('main');
    const fadeLayer = document.createElement('div');
    fadeLayer.setAttribute('id', 'fade-layer');
    fadeLayer.classList.add('fade-layer', 'd-none');
    main.prepend(fadeLayer);
  })



  // フェードレイヤーの表示・非表示切り替え
  toggleFadeLayer = () => {
    const fadeLayer = document.getElementById('fade-layer');
    fadeLayer.classList.toggle('d-none');
  }



  // ドロワーの表示・非表示切り替え
  const toggleDrawer = (event) => {
    const target = event.target;
    const drawerTarget = target.getAttribute('data-drawer-target');
    const drawer = document.getElementById(drawerTarget);
    drawer.classList.toggle('drawer-open');
  }



  // ドロワーの開閉ボタンにクリックイベントを仕込む
  const drawers = document.querySelectorAll('[data-drawer-btn="true"]');
  drawers.forEach((drawer) => {
    drawer.addEventListener("click", (event) => {
      toggleDrawer(event);
      toggleFadeLayer()
    })
  })



  // ドロワーを閉じるボタンにクリックイベントを仕込む
  const drawerCloseBtns = document.querySelectorAll('[data-drawer-close-btn="true"]');
  drawerCloseBtns.forEach(closeBtn => {
    closeBtn.addEventListener('click', (event) => {
      toggleDrawer(event);
      toggleFadeLayer();
    })
  })



  // フェードレイヤーにクリックイベントを仕込む
  // フェードレイヤーがクリックされたらドロワーを閉じて、フェードレイヤーを非表示にする
  window.addEventListener('DOMContentLoaded', () => {
    const fadeLayer = document.getElementById('fade-layer');
    fadeLayer.addEventListener('click', () => {
    const openingDrawer = document.getElementsByClassName('drawer-open')[0];
    openingDrawer.classList.remove('drawer-open');
    fadeLayer.classList.add('d-none');
    })
  })

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

JavaScriptのオブジェクト比較をできるだけ短いコードで行う

はじめに

Reactを書いていて、initialState(初期ステート)とstate(現在のステート)の比較を行いたいことがあった。
しかし、意外とJavaScriptではオブジェクトの比較が難しかったので、できるだけ短いヘルパー関数を作ったので共有したい。

結論

helper.js
/* オブジェクトをソート済み配列に変換する */
const objToSortedArray = obj => Object.entries(obj).sort()

/* ソート済み配列を文字列に変換して比較する */
const isEqualOneDimentionalArray = (obj1, obj2) =>
  JSON.stringify(objToSortedArray(obj1)) === JSON.stringify(objToSortedArray(obj2))

/* 再帰処理を行い、ネストされたオブジェクトまで比較する */
export const isEqual = (obj1, obj2) => isEqualOneDimentionalArray(obj1, obj2)
  && objToSortedArray(obj1).map(([key, val]) => typeof val === "object" ? isEqual(val, obj2[key]) : true)
howToUse.js
import {isEqual} from "./helper.js"

const initialState = {
  tmpConditions: {
    target: "makeup",
    personalColor: false,
    faceType: false,
    items: []
  }
}

const state = {
  tmpConditions: {
    target: "makeup",
    personalColor: true,
    faceType: false,
    items: [1, 23]
  }
}

console.log(isEqual(initialState, state)) // false

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

javascriptでif関数やswitch関数を作る(別記事のものを参考にしました)

最初に前提として書いておくと、javascriptでifやswitchは文となっています。この記事では、そんな二つの文法を返り値を返せるようにiff関数やswitchf関数として実装するというものです。
https://qiita.com/Yametaro/items/17f5a0434afa9b88c3b1
の感想で書いていたものになります。このページに掲載するプログラムは元記事の物を参考にしています。

if関数改めiff関数

 これに関しては、元記事の中でも元記事の感想欄でも指摘されてましたが、通常は三項演算子を使ってください。
 元記事の感想欄で失敗したので、省略はできないとします。
 感想欄でも書いたものになりますが、thenとelseの順番が逆でもいいバージョンを作ってみました。

const iff = cond=>{
  const met = cond2=>func=>{
    const revmet = revfunc => {
      return cond2? func():revfunc();
    }
    return (cond==cond2)?{else:revmet}:{then:revmet}
  };
  return {then:met(cond),else:met(!cond)};
}
//以下サンプル
a=true;
document.write(iff(a).then(()=>"that is right<br>").else(()=>"that is wrong<br>"));
document.write(iff(a).else(()=>"that is wrong<br>").then(()=>"that is right<br>"));

いい実装かはわかりませんが、仕組みを説明します。最後のreturn {then:met(cond),else:met(!cond)};というところを見てください。thenを先にした時とelseを先にした時で、引き渡している条件が逆転しています。これが肝となっています。cond(指定した条件式)とcond2を整理したいと思います。

thenが先かelseが先か cond cond2
then true true
else true false
then false false
else false true

まず、最後のcond2に注目してください。cond2がtrueになるときはthenが先で条件式がtrueの時と、elseが先で条件式がfalseの時です。つまり先に指定された方の関数の返り値を返すときです。次にcond2がfalseになるときはelseが先で条件式がtrueの時と、thenが先で条件式がtrueの時です。つまり後に指定した方の関数返り値を返すときです。つまり、条件式だったり、thenとelseどっちが先かによらず先に指定した関数か、後に指定した関数かどっちの関数の返り値を返せばいいのか一緒になっています。なので、revmet関数は単にcond2によって分岐しているだけになっています。また、thenが後に来てもelseが後に来ても同じrevmetでいいというわけです。
次に、condとcond2を比較すると、両方trueの時と、両方falseの時にthenを先に指定しています。なのでcond==cond2という式で、thenを含むオブジェクトを返すかelseを含むオブジェクトを返すかを分岐しています。
よって上記の簡単な実装で適切な返り値となっています。なかなか説明が難しかったのでうまく説明できた自信ないですけどわかりましたか?
 ちなみにelseifいりますか?といってもelse-thenの順番で書く人はelseifはどこに書くんでしょうか?

switch関数改めswitchF関数

 作ろうかと思ったんですけど、なかなかうまくいかないので完成したら投稿します。方針としては、最初はcase関数の引数を可変長にしようと思ったんですけど複数のcase関数で同じ値を書かれたときの処理をどうするのか問題になりそうだったので、ttatsfさんのcase関数(やめ太郎さんバージョンだとthen関数)に関数を指定しなかったときはその後初めて指定されたときに指定した関数を指定するという風にしようと考えています。

他には

 他にはなんか式にしたら便利そうな文ってありましたっけ?あったら関数の実装に挑戦するので教えてください。

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

ドラッグ&ドロップされた画像や音声ファイルをインラインimgやaudioタグに変換するツールをつくってみた

HTMLに画像を埋め込みたい場合に、下記のように、URLではなく直接データをBase64エンコードして埋め込むことができます。
<img src="data:image/png;base64,iVBORw0KGgoAAAAN...">

今回は、このタグを簡単に作成できるツールを作ってみました。

インラインimgタグ生成ツール

MIME type image/pngimage/jpeg 1 だけ受け付けるようにしてます。あと、とりあえず1MB以下の制限をつけてます。
【追記】音声ファイル(audio/wavとmp3ファイル(audio/mpeg)のみ)も対応してみました。

See the Pen ImageAndAudioFileBase64Encoder(Under Construction) by kob58im (@kob58im) on CodePen.

参考サイト

参考サイト(画像関連)

参考サイト(Audio関連)


  1. 一度描画してから再エンコーディングする処理となっているので、画質が劣化する可能性があります。音声ファイルの処理のほうを真似れば改善できそうですが、今のところ対応予定はありません。(単純に面倒くさい・・・) 

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

ドラッグ&ドロップされた画像をインラインimgタグに変換するツールをつくってみた

HTMLに画像を埋め込みたい場合に、下記のように、URLではなく直接データをBase64エンコードして埋め込むことができます。
<img src="data:image/png;base64,iVBORw0KGgoAAAAN...">

今回は、このタグを簡単に作成できるツールを作ってみました。

インラインimgタグ生成ツール

MIME type image/pngimage/jpeg だけ受け付けるようにしてます。あと、とりあえず1MB以下の制限をつけてます。

See the Pen ImageFileToBase64(Under Construction) by kob58im (@kob58im) on CodePen.

参考サイト

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

初心者限定!!Vueで横幅(width)の変化を検知する方法

今回は、タイトルにも書いてる通りwindowの横幅の変化を検知したいと思います!!!

長ったらしい話はせずにもうコードを書いていきますね!

まず、初めにdataオブジェクトでcurrentWidthというプロパティを宣言し、横幅が変化するたびに呼び出す関数を設定していきます。

App.vue
<script>
export default {
  data() {
    return {
      currentWidth: window.innerWidth,
    }
  },
  methods: {
    calculateWindowWidth() {
      // 横幅を取得する関数
      this.windowWidth = window.innerWidth
    }
  }
}
</script>

初期値として、window.innerWidthを設定しておきます。

次に、DOM要素と紐づけられた後に行う関数mounted、DOM要素が消去される前の関数beforeDestroyを用いて、横幅の変更を検知します。

App.vue
<script>
export default {
  data() {
    return {
      currentWidth: window.innerWidth,
    }
  },
  mounted() {
    // 横幅の変更を検知
    window.addEventListener('resize', this.calculateWindowWidth)
  },
  beforeDestroy() {
    // 横幅の変更を検知
    window.addEventListener('resize', this.calculateWindowWidth)
  },
  methods: {
    calculateWindowWidth() {
      // 横幅を取得する関数
      this.currentWidth = window.innerWidth
    }
  }
}
</script>

いかがだったでしょうか?

僕は、最初にcomputedで行ってしまい横幅の変更を検知してくれませんでした。

最初はcomputed便利だな~って思ってたんですけど、最近使い方が難しいことに気づきました。

なんとなく出来たので良しとします。

以上、「初心者限定!!Vueで横幅(width)の変化を検知する方法」でした!

良かったら、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!

Thank you for reading

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

ぷい!Zdogで3Dモルカーを実装する

はじめに

モルカーがかわいくてブラウザ上でつついて遊びたいなと思い、てきとうに検索してたらZdogという3Dエンジンを見つけたので使ってみました。

モルカーとは

百聞は一見に如かず!
1話約3分なので見ましょう!
モルカー1話:バンダイチャンネル
モルカー2話:YouTube

作ったもの

ブラウザ上で動かせる3Dモルカー
デモ

実装

今回はhtmlファイル一つだけでささっとつくりました
入門的なのはこちらの記事が良かったです
かわいい3Dエンジン「Zdog」の紹介

html

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Zdog Tutorial</title>
    <script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
</head>

  <body>
    <canvas width="500" height="500" class="zdog-canvas"></canvas>
    <script>
     //ここに3Dの実装をしていく
    </script>
  </body>
</html>

<script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
インストールするのめんどくさかったのでCDN使ってます

JavaScript

全部は長いのでGitHub見てください

  <script>
        const mainColor = "#f2ebd1"
        const pink = "pink"
        const brown = "#705444"

        const illo = new Zdog.Illustration({
            element: '.zdog-canvas',
            dragRotate: true
        })
(省略)
        let anchor = new Zdog.Anchor({
            addTo: illo,
        });
        let body = new Zdog.Shape({
            addTo: anchor,
            stroke: 200,
            color: mainColor,
            path: [{ x: 0, y: 0 }
                , {
                arc: [
                    { x: 0, y: 0, z: 0 },
                    { x: 0, y: 0, z: -90 },
                ]
            },],
        })
  • まずconst illo = new Zdog.Illustration({})で親インスタンスを生成します
  • 次に親となるオブジェクトを決めます。今回はanchorをトップにしてその下にbodyがあります。以降bodyに対してその他のオブジェクトを紐づけていきます
  • Shapeはそのままだと球体ですが、pathを使うと直線や曲線を作ることができます。

arc

arcは楕円曲線をつくれます

new Zdog.Shape({
  addTo: illo,
  path: [
    { x: -60, y: -60 },   // start
    { arc: [
      { x:  20, y: -60 }, // corner
      { x:  20, y:  20 }, // end point
    ]},
    { arc: [ // start next arc from last end point
      { x:  20, y:  60 }, // corner
      { x:  60, y:  60 }, // end point
    ]},
  ],
  closed: false,
  stroke: 20,
  color: '#636'
});

arc.png

赤の点(-60,-60)が始点となり、arcで指定されてる座標(20,-60),(20,20)/(20,60),(60,60)で作られる四角形の中に納まるよう曲線が描画されます。
yは下が+上が-です。
ここではモルカーの体の描画に使いたかったので、直線になるように指定してます。
他には、マズルを作るところでも使用してます。



   let rightNose = new Zdog.Shape({
            addTo: body,
            path: [
                { x: 10, y: 70, z: 65 },
                {
                    bezier: [
                        { x: 15, y: 60, z: 65 },
                        { x: 10, y: 75, z: 60 },
                        { x: 0, y: 75, z: 60 },
                        { x: 0, y: 85, z: 55 },
                    ]
                },
                { x: 0, y: 85, z: 55 },
            ],
            closed: false,
            stroke: 5,
            color: brown
        });
  • 鼻… ωのところのコードです
  • ここではベジェ曲線を使用してます bezier.png ※z軸は省略
    座標いまいちですが…
    始点(10,70)と終点(0,85)をωのガイドラインとして(0,75)から終点までを鼻の下、始点から(0,75)までが鼻になるようにしてます。
    左右あわせてωになるようにしてます。

参考

Zdog公式
かわいい3Dエンジン「Zdog」の紹介

最後に

3Dのものを作るのは初めてでしたが、わりと直感的に書けるので実装しやすかったです。
細かい描画の実装は難しいのでポテトちゃんとかは作れなさそうです…

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

ニコニコ動画のページからタイトルetc.を取り出す

chrome拡張機能を作るときに必要になったので。

タイトル

title
var row_data = document.getElementById('js-initial-watch-data').getAttribute("data-api-data");
var title = JSON.parse(row_data).video.title;

タグをリストで

tags
var row_data = document.getElementById('js-initial-watch-data').getAttribute("data-api-data");
var tags = JSON.parse(row_data).tags;
for(var i=0; i<tags.length; i++){
  console.log(tags[i].name);
}

あとがき

ニコニコ動画はjs-initial-watch-dataというIDの要素のdata-api-data属性にjson形式で様々なデータがくっついています。
このデータには今回取り出したような動画タイトルやタグのほか、コメントも含まれています。

質問、誤りなどあったらコメントください。

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

【JavaScript】クリックしたときにポップアップを表示する

イメージ図

ポップアップ.png

HTML

<button id="click-btn">クリック!</button>
<div id="popup-wrapper">
  <div id="popup-inside">
    <div id="close">x</div>
      <div id="message">
      <h2>あの人気のマンガが...</h2>
      <p>今なら80%オフ!</p>
      <a href="#">ゲットする</a>
    </div>
  </div>
</div>

CSS

#click-btn {
  display: block;
  margin: 20px auto;
  background-color: purple;
  color: white;
  border: 0;
  padding: 6px 10px;
}

#popup-wrapper {
  background-color: rgba(0, 0, 0, .5);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: none;
}

#popup-inside {
  text-align: center;
  width: 100%;
  max-width: 300px;
  background: white;
  margin: 10% auto;
  padding: 20px;
  position: relative;
}

#message a {
  background: purple;
  color: white;
  text-decoration: none;
  padding: 6px 10px;
}

#close {
  position: absolute;
  top: 0;
  right: 5px;
  cursor: pointer;
}

JavaScript

const clickBtn = document.getElementById('click-btn');
const popupWrapper = document.getElementById('popup-wrapper');
const close = document.getElementById('close');

// ボタンをクリックしたときにポップアップを表示させる
clickBtn.addEventListener('click', () => {
  popupWrapper.style.display = "block";
});

// ポップアップの外側又は「x」のマークをクリックしたときポップアップを閉じる
popupWrapper.addEventListener('click', e => {
  if (e.target.id === popupWrapper.id || e.target.id === close.id) {
    popupWrapper.style.display = 'none';
  }
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む