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

vscodeで関数名と()の間のスペースがなくなる問題.

前提

利用したエディタ

Visual Stdio Code
みんな大好き.

利用したコーディングスタイル

JavaScript Standard Style

理由は簡単で,けっこうルールが細かく決められていて,個人的に好みだったからです.
(逆にゆるいのが嫌い)

問題となったルール

上記コーディングスタイルの中に,このようなルールがあります.
簡単に言うと,関数宣言の括弧の前にはスペースを入れましょうということです.

function name (arg) { ... }   // ✓ ok
function name(arg) { ... }    // ✗ avoid

run(function () { ... })      // ✓ ok
run(function() { ... })       // ✗ avoid

どのような問題が発生したか

ソースを保存したときに,勝手に関数名と()の間の空白が消されました.
つまり,上記の例のaviod状態になったということです.

手で空白を追記して,保存したら消えて...
という無限ループに陥っていました.

解決方法

Visual Studio Codesetting.jsonを開いて,
javascript.format.insertSpaceBeforeFunctionParenthesis
にチェックを入れただけです.

これだけです.

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

Deno で "node".split("").sort().join("")

Deno 公式の Twitter アカウントが投稿していたコード。

Deno on Twitter: ""node".split("").sort().join("")" / Twitter

"node".split("").sort().join("")

結果を出力してみる。

deno.ts
console.log("node".split("").sort().join(""))

node が deno になった。

$ deno -q run deno.ts
deno

これが Deno という名前の由来かどうかは探してみたが見つからなかった。

Deno - Wikipedia

DenoはJSConf EU 2018でのライアン・ダールによる講演「Node.jsに関する10の反省点」で発表された。ライアン・ダールはこの講演において、後悔しているNode.jsの初期設計での決定について言及し、APIの設計でpromiseを使用しないという選択をしたこと、古いGYPビルドシステムを使用するようにしたこと、node_modulesとpackage.jsonの採用、拡張子を除外したこと、index.jsによる魔法のようなモジュールの依存関係の解決、V8のサンドボックス環境の破壊を挙げている。最終的に、彼はDenoのプロトタイプ版を発表し、Protocol Buffersのようなシリアライズツールを使用したメッセージの受け渡しを通じてシステムコールバインディングを実現し、アクセス制御用のコマンドラインフラグを提供することを目指した。

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

kintone Google Directions API を用いた2地点間距離の自動入力

目的

住所が保存してあるフィールドA,B地点間の距離を自動で算出して経理が交通費を処理する
という要望があり、自動化を図る。
ここでは、A,B地点間の距離を算出するまでを紹介します。
(ただGoogle先生にお願いするだけです)

Google Directions APIトークンの発行

発行できているものとします。
調べたら他にも記事が出てきますので、そちらを参考に。
使用環境が大型の場合、リクエスト数が有料枠に届ことが考えられますので、
そちらは自己責任でよろしくお願いします。

フィールドを用意

image.png

フィールド フィールドコード フィールドタイプ
距離 距離 数値
A地点 A地点 文字列一行
B地点 B地点 文字列一行

とします

ソースコード

route.js
(function() {
  'use strict';
  kintone.events.on('app.record.create.submit', function(event) {
      const record = event.record;

      const api_key = 'XXXXXXXX'; //API key

      const origin = record['A地点'].value ? record['A地点'].value : 0; //A地点
      const destination = record['B地点'].value ? record['B地点'].value : 0; //B地点

      if(origin === 0 | destination === 0){//何も入力されていなければリターン
         event.error = 'エラー';
         return;
      }

      const param = 'origin=' + origin + '&destination=' + destination + '&avoid=highways';

      return kintone.proxy('https://maps.googleapis.com/maps/api/directions/json?' + param + '&key=' + api_key , 'GET', {}, {}).then(function(resp) {

        const response = JSON.parse(resp[0]);
        const routes = response.routes[0];

        //距離、分数
        const distance = routes.legs[0].distance.value;
        const duration = routes.legs[0].duration.text;

        //小数第一の位で切り上げ
        const cul = Math.round((distance / 1000)*10)/10;

        record['距離'].value = String(cul); 

        return event;

      }, function(error) {
          // error
      });


    });
})();

解説

保存時イベント

イベントのタイミングはA,B地点両方入力された後と想定し、保存時イベントに設定。
外部のWebAPIを使用するのでPromiseを使用した同期的な処理が必要となる。
(Google Directions APIの結果を待って処理をするということ)

両地点の宣言とエラー設定

三項演算子を使用

const origin = record['A地点'].value ? record['A地点'].value : 0; //A地点
const destination = record['B地点'].value ? record['B地点'].value : 0; //B地点

レコードに値があればその値を宣言し、なければ0を代入する。

if(origin === 0 | destination === 0){//何も入力されていなければリターン
    event.error = 'エラー';
    return;
}

どちらかで0が代入されていた場合、エラー処理を入れてイベントを返す。
例えばエラー詳細としてフィールド下部にエラー文字を表示させる場合は、

record['A地点'].error = '必須';

これでフィールドにエラーを表示させることができます。

リクエストヘッダ

'https://maps.googleapis.com/maps/api/directions/json?' + param + '&key=' + api_key

公式ドキュメントを参照。

パラメータ

リクエストURLに動的に変化する値(ここではふぁいーるどからの値)を代入したいので、
origin,destinationは事前に宣言して文字列としてparamに代入します。
注意:origin,destinationの文字列は空白を挟むとエラーを返します。

const param = 'origin=' + origin + '&destination=' + destination + '&avoid=highways';

最後の &avoid=highways は高速道路を使わないルート。

外部APIの実行

return kintone.proxy('https://maps.googleapis.com/maps/api/directions/json?' + param + '&key=' + api_key , 'GET', {}, {}).then(function(resp) {

        const response = JSON.parse(resp[0]);
        const routes = response.routes[0];

        //距離、分数
        const distance = routes.legs[0].distance.value;
        const duration = routes.legs[0].duration.text;

        //小数第一の位で切り上げ
        const cul = Math.round((distance / 1000)*10)/10;

        record['距離'].value = String(cul); 

        return event;

      }, function(error) {
          // error
      });

JSON形式で値が返ってくるため、パースをして、欲しいオブジェクトを参照。
distanceは距離、durationは予測分数(デフォルトで車)。

必要に応じてその他処理をイベントを返す前に記述すると交通費までを算出できるかと思います。

距離フィールドにグレーアウトをかけたい場合は、
app.record.create.show
app.record.edit.show
の二つのイベントで下記のコードを記述すると入力時にユーザーは触れないフィールドになります。

const record = event.record;
record['任意のフィールドコード'].disabled = true;
return event;

最終的なコード

コピペしていじれば使えるよってやつです。

サンプル
route.js
(function() {
  'use strict';
  kintone.events.on('app.record.create.submit', function(event) {
      let record = event.record;

      let api_key = 'XXXXXX'; //API key

      let origin = record['A地点'].value ? record['A地点'].value : 0; //A地点
      let destination = record['B地点'].value ? record['B地点'].value : 0; //B地点

      if(origin === 0 | destination === 0){//何も入力されていなければリターン
        return;  
      }

      let param = 'origin=' + origin + '&destination=' + destination + '&avoid=highways';

      return kintone.proxy('https://maps.googleapis.com/maps/api/directions/json?' + param + '&key=' + api_key , 'GET', {}, {}).then(function(resp) {

        const response = JSON.parse(resp[0]);
        let routes = response.routes[0];

        //距離、分数
        let distance = routes.legs[0].distance.value;
        let duration = routes.legs[0].duration.text;

        window.alert(duration);

        //小数第一の位で切り上げ
        let cul = Math.round((distance / 1000)*10)/10;

        record['距離'].value = String(cul); 

        return event;

      }, function(error) {
          // error
      });


    });

    const ev_input = [
      'app.record.create.show',
      'app.record.edit.show'
      ];

    kintone.events.on(ev_input, function(event) {

      const record = event.record;

      record['距離'].disabled = true;

      return event;

    });

})();



後述

Qiitaで初投稿はおろか、自分のコードを世に公開することが初めてで、下手な日本語で説明させていただきました。
kintone,jsを触り始めて一年経ち、いろいろな経験をさせていただいています。

活発に活動をしていきたいと思っていますのでコードの訂正、語句の訂正などご教授よろしくお願いします。
反応があれば、もっとこのAPIで遊んでみようかと思います。

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

vue.jsとmoment.jsを使って、TODOに日付を表示する

vue.jsとmoment.jsを使って、TODOに日付を表示する

TODOリストに日付を追加する方法が分からなかったので、メモ。

moment.js導入手順

$ yarn add moment

で、vuecliのプロジェクトにmoment.jsをインストールします。

import moment from 'moment';

で、インストールしたmoment.jsを読み込みます。

TODOに日付を表示する

下記のように、TODOを追加するシンプルなコードを書きます。

<template>
  <div id="app">
    <h2>todos</h2>
    <p>
      <input v-model="newtodo" v-on:keyup.enter="addtodo" />
    </p>
    <div v-for="todo in todos" :key="todo.id">
      <p>
        <span class="todo">{{ todo }}</span>
      </p>
    </div>
  </div>
</template>

<script>
import moment from 'moment';
export default {

  data() {
    return {
      todos: [],
      newtodo: '',
    };
  },
  methods: {
    addtodo() {
      if (!this.newtodo) {
        return;
      }
      this.todos.push(this.newtodo);
      this.newtodo = '';
    },
  },
};
</script>

image.png

addtodoメソッドで、TODOと現在の時刻を取得して、todos[]にpushします。

    addtodo() {
      if (!this.newtodo) {
        return;
      }
      const params = {
        text: this.newtodo,
        created_at: new Date(),
      };
      this.todos.push(params);
      this.newtodo = '';
    },

parmasに、入力したtodoと、created_atには、new Date()で、現在の日付を代入したもの を代入します。
this.todos.pushで、配列todos[]にparamsの値をpushします。

とすることにより、テンプレート側で{{todo}}で参照できます。

 <div v-for="todo in todos" :key="todo.id">
      <p>
        <span class="todo">{{ todo }}</span>
      </p>
    </div>

参照した結果がこちらです。
image.png

{{todo.text}}とすれば todosの中のtextが取れるし、{{todo.created_at}}とすれば日付が取れます。

これだけだと、moment.jsの出番がないので、活用します。

export default {
  filters: {
    moment: function(date) {
      console.log(date);
      return moment(date).format('YYYY/MM/DD HH:mm');
    },
  },

で、momentというフィルターを作成します。文字列をDate関数よりも簡単に扱えるmoment関数に変換するものです。

テンプレート上で、{{ todo.created_at | moment }}とすることによって、momentのフィルターを通しました。
moment(date).format('YYYY/MM/DD HH:mm');の部分で、YYYY/MM/DD HH:mmの形式に変換することができました。
変換した結果が下記になります。
image.png

使い方忘れそうなのでメモしました。

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

Deno 1.0.0 で Hello World

概要

  • Deno 1.0.0 をインストールして Hello World などを実行する

Deno とは

Deno は Rust で書かれた JavaScript と TypeScript の実行環境。

Deno

Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust.

Deno は単独で TypeScript を実行できる。

https://deno.land/v1

Deno supports TypeScript without additional tooling. The runtime is designed with TypeScript in mind.

Deno のインストール

The Deno Manual - Installation にインストール方法が載っている。

macOS には Homebrew でインストールできる。

$ brew install deno
$ deno --version
deno 1.0.0
v8 8.4.300
typescript 3.9.2

Linux にはインストール用のシェルスクリプトを実行してインストールできる。

$ curl -fsSL https://deno.land/x/install/install.sh | sh -s v1.0.0

Hello World

以下の内容の hello.ts ファイルを作成。

hello.ts
const message: string = "Hello, world"
console.log(message)

deno run で実行。
コンパイルされて実行される。

$ deno run hello.ts
Compile file:///Users/foo/bar/hello.ts
Hello, world

Compile の行 (diagnostic output) は標準エラー出力に出力される。

2回目の実行では diagnostic output は出力されない。
コンパイル済みのコードがキャッシュされているため。

$ deno run hello.ts
Hello, world

コンパイル済みのコードはキャッシュ用のディレクトリに保存されている。
deno info コマンドで調べることが可能。

$ deno info
DENO_DIR location: "/Users/foo/Library/Caches/deno"
Remote modules cache: "/Users/foo/Library/Caches/deno/deps"
TypeScript compiler cache: "/Users/foo/Library/Caches/deno/gen"
$ ls -1 /Users/foo/Library/Caches/deno/gen/file/Users/foo/bar
hello.ts.js
hello.ts.js.map
hello.ts.meta

コンパイル時に diagnostic output を出力したくない場合は --quiet または -q オプションを指定する。

$ deno --quiet run hello.ts
Hello, world

HTTP 越しのコードを実行

Deno のサイトにサンプルコードが置いてある。

$ curl https://deno.land/std/examples/welcome.ts
console.log("Welcome to Deno ?");

deno run コマンドで URL を指定して直接実行できる。

$ deno run https://deno.land/std/examples/welcome.ts
Download https://deno.land/std/examples/welcome.ts
Warning Implicitly using master branch https://deno.land/std/examples/welcome.ts
Compile https://deno.land/std/examples/welcome.ts
Welcome to Deno ?

ファイルを読む

以下の内容の cat.ts ファイルを作成。
引数で指定したファイルを読み込んで標準出力へ出力するプログラムになっている。

cat.ts
const filenames = Deno.args;
for (const filename of filenames) {
  const file = await Deno.open(filename);
  await Deno.copy(file, Deno.stdout);
  file.close();
}

ファイルを読み込むには --allow-read フラグを指定する。

$ deno -q run --allow-read cat.ts cat.ts cat.ts
const filenames = Deno.args;
for (const filename of filenames) {
  const file = await Deno.open(filename);
  await Deno.copy(file, Deno.stdout);
  file.close();
}

const filenames = Deno.args;
for (const filename of filenames) {
  const file = await Deno.open(filename);
  await Deno.copy(file, Deno.stdout);
  file.close();
}

HTTP リクエストを投げる

以下の内容の curl.ts ファイルを作成。

curl.ts
const url = Deno.args[0]
const res = await fetch(url)
const body = new Uint8Array(await res.arrayBuffer())
await Deno.stdout.write(body)

ネットワークアクセスをする場合は --allow-net フラグで通信を許可するドメインを指定する。

$ deno -q run --allow-net=niwasawa.github.io curl.ts https://niwasawa.github.io/
<!DOCTYPE html>
<html lang="ja">
<head>
(以下略)

標準モジュールを使う

標準モジュールは std - deno.land にある。

以下の内容の sha256.ts ファイルを作成。

sha256.ts
import { Message, Sha256 } from "https://deno.land/std/hash/sha256.ts"
const message: Message = "Hello, world."
const sha256: Sha256 = new Sha256()
sha256.update(message)
const hash: string = sha256.hex()
console.log(hash)

deno run コマンドで実行する。

$ deno -q run sha256.ts 
f8c3bf62a9aa3e6fc1619c250e48abe7519373d3edf41be62eb5dc45199af2ef

サードパーティのモジュールを使う

サードパーティのモジュールは Deno Third Party Modules にある。

以下の内容の md5.ts ファイルを作成。

md5.ts
import { md5 } from "https://deno.land/x/md5/mod.ts"
const hash: string = md5("Hello, world.")
console.log(hash)

deno run コマンドで実行する。

$ deno -q run md5.ts 
080aef839b95facf73ec599375e92d47

外部モジュールを使う

以下の内容の echo.ts ファイルを作成。

echo.ts
export const echo = (s: string): string => { return s }

以下の内容の aaa.ts ファイルを作成。

aaa.ts
import { echo } from "./echo.ts"
console.log(echo("Hello, world"))

deno run コマンドで実行する。

$ deno -q run aaa.ts 
Hello, world

Web サーバを起動する

標準モジュールである std/http/server.ts を使って Web サーバを起動できる。

以下の内容の webserver.ts ファイルを作成。

webserver.ts
import { serve, Server, ServerRequest, Response } from "https://deno.land/std/http/server.ts"
const server: Server = serve({ port: 8000 })
for await (const req: ServerRequest of server) {
  const res: Response = {
    body: "<html><body>Hello World</body></html>\n"
  }
  req.respond(res)
}

deno run コマンドで実行して Web サーバを起動する。
ネットワークへのアクセスを許可するため --allow-net フラグを指定する。

$ deno -q run --allow-net webserver.ts

別のコンソールから curl 等でアクセスすると HTTP レスポンスが返ってくる。

$ curl http://localhost:8000/
<html><body>Hello World</body></html>

Deno の Help

deno -- help でヘルプが表示される。

$ deno --help
deno 1.0.0
A secure JavaScript and TypeScript runtime

Docs: https://deno.land/std/manual.md
Modules: https://deno.land/std/ https://deno.land/x/
Bugs: https://github.com/denoland/deno/issues

To start the REPL:
  deno

To execute a script:
  deno run https://deno.land/std/examples/welcome.ts

To evaluate code in the shell:
  deno eval "console.log(30933 + 404)"

USAGE:
    deno [OPTIONS] [SUBCOMMAND]

OPTIONS:
    -h, --help                     
            Prints help information

    -L, --log-level <log-level>    
            Set log level [possible values: debug, info]

    -q, --quiet                    
            Suppress diagnostic output
            By default, subcommands print human-readable diagnostic messages to stderr.
            If the flag is set, restrict these messages to errors.
    -V, --version                  
            Prints version information


SUBCOMMANDS:
    bundle         Bundle module and dependencies into single file
    cache          Cache the dependencies
    completions    Generate shell completions
    doc            Show documentation for a module
    eval           Eval script
    fmt            Format source files
    help           Prints this message or the help of the given subcommand(s)
    info           Show info about cache or info related to source file
    install        Install script as an executable
    repl           Read Eval Print Loop
    run            Run a program given a filename or url to the module
    test           Run tests
    types          Print runtime TypeScript declarations
    upgrade        Upgrade deno executable to given version

ENVIRONMENT VARIABLES:
    DENO_DIR             Set deno's base directory (defaults to $HOME/.deno)
    DENO_INSTALL_ROOT    Set deno install's output directory
                         (defaults to $HOME/.deno/bin)
    NO_COLOR             Set to disable color
    HTTP_PROXY           Proxy address for HTTP requests
                         (module downloads, fetch)
    HTTPS_PROXY          Same but for HTTPS

参考資料

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

puppeteer の世界に一歩はいってみた

Install

npm init -y
npm install --save puppeteer
  • node.js がインストールされていれば良く、Chrome をインストールしておく必要は無い

Basic

example (Google のトップページのスクリーンショットを撮るだけ)

const puppeteer = require('puppeteer');
(async() => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://google.com');
    await page.screenshot({
        path: 'ss.png',
        fullPage: true
    });
    await browser.close();
})();

ヘッドフルモードとスローモーション再生

const browser = await puppeteer.launch({
    headless: false,
    slowMo: 500 // ms
})

シークレットモード

const context = await browser.createIncognitoBrowserContext();
const page = await context.newPage();

DOM 指定

待機

waitFor 系のメソッドを使用する。

ただし、「クリック後、画面遷移を待つ」ような場合は、その二つの動作を並列に実行しておくこと。

await Promise.all([
    page.waitForNavigation(),
    page.click('.button'),
]);

上記ふたつを await で並べてしまうと、タイミングによってはクリック後ずっと待ち状態に入ってしまうため。

example (Google で「puppeteer」と検索した結果のスクリーンショットを撮る)

const puppeteer = require('puppeteer');
(async() => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://google.com');

    await page.type('.gLFyf', 'puppeteer')

    await Promise.all([
    page.waitForNavigation(),
    page.click('.gNO89b'),
    ]);

    await page.screenshot({
        path: 'ss.png',
        fullPage: true
    });
    await browser.close();
})();

memo

  • Puppeteer は Chrome に特化している。Selenium は WebDriver を挟むことで、他ブラウザにも対応できる

参考

  • Web+DB Press Vol.109 『[速習] Puppeteer』
    • ここまでの話以上に、テストでの実践やスクレイピングサンプルもあり、オススメな記事
  • https://github.com/puppeteer/puppeteer

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

TypeScriptでinput要素の型を拡張する

最近、やっと実務でTypeScriptを使う機会がありました。戸惑ったところでしたので記事にしておきます。

環境

$ npx create-react-app react-ts-app --typescript

普通のやーつ

エラー

input[type="text"]にautofocusをつけたら下記のように怒られました。

Property 'autofocus' does not exist on type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'.  TS2322

無くても問題はありませんが、ユーザビリティを考えると諦めきれなかったので拡張します。

1. 拡張を記述するファイルを追加

ディレクトリ

├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json

src配下にファイルを追加します。フォルダ名もファイル名も多分なんでもいいです?

├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
    ├── type
        ├── react.ts
├── tsconfig.json

2. react.tsに拡張を記述

react.ts
import 'react';

declare module 'react' {
  interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
    autocomplete?: boolean;
    autofocus?: boolean; // こいつらも一緒に追加
    tabindex?: boolean; 
  }
}

TypeScriptの型はデフォルトで./node_modules/@types/react/index.d.tsに記述してあります。
interface InputHTMLAttributes<T> extends HTMLAttributes<T>の部分はこちらのファイルを参照しました。

./node_modules/@types/react/index.d.ts
├── node_modules
    ├── @types
        ├──react
           ├──index.d.ts
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json

あとは普通のinterface、型宣言です。当たり前ですが、autocomplete等にもHTMLで有効な型が決まってるのでそれに合わせてください。
参考 : MDN 入力欄 (フォーム入力) 要素

declare

アンビエント宣言というらしいです。恥ずかしながら初めて知りました。
参考 : TypeScriptの翻訳機が知らないような機能を使う時は、アンビエント宣言をしておく

3. tsconfig.jsonにreact.tsを読むように追記

tsconfig.json
{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./src/type"]
  },
  "include": ["src"]
}

いらない部分は端折ってます。"./node_modules/@types"を書かないとnodo_modules配下の型設定を読み込まないので書いてください。そのあとに先ほど作成したreact.tsのディレクトリ"./src/type"を書きます。

4. npm start

$ npm start

サーバーを立てるとエラーが消え、autofocusもコンパイルできました。npm startをしたまま操作していた方は、一度落として再実行してください。

注意点

  • create-react-appはコンパイルする対象が"./src"配下になっているので、プロジェクトのルートに新規ディレクトリを作成して型拡張を記述しても動きません。
  • 途中でも書きましたが、拡張したい型は./node_modules/@types/react/index.d.tsから探してきてください。

参考

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

tinput要素の型を拡張して使える属性を増やす

最近、やっと実務でTypeScriptを使う機会がありました。フォーム周りを作成している際、inputにautofocusなどの属性を追加したところコンパイルエラーが出てしまいました。戸惑ったところでしたので記事にしておきます。

環境

$ npx create-react-app react-ts-app --typescript

普通のやーつ

エラー

input[type="text"]にautofocusをつけたら下記のように怒られました。

Property 'autofocus' does not exist on type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'.  TS2322

無くても問題はありませんが、ユーザビリティを考えると諦めきれなかったので拡張します。

1. 拡張を記述するファイルを追加

ディレクトリ

├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json

src配下にファイルを追加します。フォルダ名もファイル名も多分なんでもいいです?

├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
    ├── type
        ├── react.ts
├── tsconfig.json

2. react.tsに拡張を記述

react.ts
import 'react';

declare module 'react' {
  interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
    autocomplete?: boolean;
    tabindex?: boolean;// こうゆうのも一緒に追加
  }
}

TypeScriptの型はデフォルトで./node_modules/@types/react/index.d.tsに記述してあります。
interface InputHTMLAttributes<T> extends HTMLAttributes<T>の部分はこちらのファイルを参照しました。

./node_modules/@types/react/index.d.ts
├── node_modules
    ├── @types
        ├──react
           ├──index.d.ts
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json

あとは普通のinterface、型宣言です。当たり前ですが、autocomplete等にもHTMLで有効な型が決まってるのでそれに合わせてください。
参考 : MDN 入力欄 (フォーム入力) 要素

declare

アンビエント宣言というらしいです。恥ずかしながら初めて知りました。
参考 : TypeScriptの翻訳機が知らないような機能を使う時は、アンビエント宣言をしておく

3. tsconfig.jsonにreact.tsを読むように追記

tsconfig.json
{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./src/type"]
  },
  "include": ["src"]
}

いらない部分は端折ってます。"./node_modules/@types"を書かないとnodo_modules配下の型設定を読み込まないので書いてください。そのあとに先ほど作成したreact.tsのディレクトリ"./src/type"を書きます。

4. npm start

$ npm start

サーバーを立てるとエラーが消え、autofocusもコンパイルできました。npm startをしたまま操作していた方は、一度落として再実行してください。

注意点

  • create-react-appはコンパイルする対象が"./src"配下になっているので、プロジェクトのルートに新規ディレクトリを作成して型拡張を記述しても動きません。
  • 途中でも書きましたが、拡張したい型は./node_modules/@types/react/index.d.tsから探してきてください。

参考

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

input要素の型を拡張して使える属性を増やす

最近、やっと実務でTypeScriptを使う機会がありました。フォーム周りを作成している際、inputにautofocusなどの属性を追加したところコンパイルエラーが出てしまいました。戸惑ったところでしたので記事にしておきます。

環境

$ npx create-react-app react-ts-app --typescript

普通のやーつ

エラー

input[type="text"]にautofocusをつけたら下記のように怒られました。

Property 'autofocus' does not exist on type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'.  TS2322

無くても問題はありませんが、ユーザビリティを考えると諦めきれなかったので拡張します。

1. 拡張を記述するファイルを追加

ディレクトリ

├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json

src配下にファイルを追加します。フォルダ名もファイル名も多分なんでもいいです?

├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
    ├── type
        ├── react.ts
├── tsconfig.json

2. react.tsに拡張を記述

react.ts
import 'react';

declare module 'react' {
  interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
    autofocus?: boolean;
    autocomplete?: boolean;// こうゆうのも一緒に追加
  }
}

TypeScriptの型はデフォルトで./node_modules/@types/react/index.d.tsに記述してあります。
interface InputHTMLAttributes<T> extends HTMLAttributes<T>の部分はこちらのファイルを参照しました。

./node_modules/@types/react/index.d.ts
├── node_modules
    ├── @types
        ├──react
           ├──index.d.ts
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json

あとは普通のinterface、型宣言です。当たり前ですが、autofocus等にもHTMLで有効な型が決まってるのでそれに合わせてください。
参考 : MDN 入力欄 (フォーム入力) 要素

declare

アンビエント宣言というらしいです。恥ずかしながら初めて知りました。
参考 : TypeScriptの翻訳機が知らないような機能を使う時は、アンビエント宣言をしておく

3. tsconfig.jsonにreact.tsを読むように追記

tsconfig.json
{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./src/type"]
  },
  "include": ["src"]
}

いらない部分は端折ってます。"./node_modules/@types"はデフォルトの設定です。そのあとに先ほど作成したreact.tsのディレクトリ"./src/type"を書きます。

4. npm start

$ npm start

サーバーを立てるとエラーが消え、autofocusもコンパイルできました。npm startをしたまま操作していた方は、一度落として再実行してください。

注意点

  • create-react-appはコンパイルする対象が"./src"配下になっているので、プロジェクトのルートに新規ディレクトリを作成して型拡張を記述しても動きません。
  • 途中でも書きましたが、拡張したい型は./node_modules/@types/react/index.d.tsから探してきてください。

参考

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

<TypeScript> input要素の型を拡張し、使える属性を増やす

最近、やっと実務でTypeScriptを使う機会がありました。フォーム周りを作成している際、inputにautofocusなどの属性を追加したところコンパイルエラーが出てしまいました。戸惑ったところでしたので記事にしておきます。

環境

$ npx create-react-app react-ts-app --typescript

普通のやーつ

エラー

input[type="text"]にautofocusをつけたら下記のように怒られました。

Property 'autofocus' does not exist on type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'.  TS2322

無くても問題はありませんが、ユーザビリティを考えると諦めきれなかったので拡張します。

1. 拡張を記述するファイルを追加

ディレクトリ

├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json

src配下にファイルを追加します。フォルダ名もファイル名も多分なんでもいいです?

├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
    ├── type
        ├── react.ts
├── tsconfig.json

2. react.tsに拡張を記述

react.ts
import 'react';

declare module 'react' {
  interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
    autofocus?: boolean;
    autocomplete?: boolean;// こうゆうのも一緒に追加
  }
}

TypeScriptの型はデフォルトで./node_modules/@types/react/index.d.tsに記述してあります。
interface InputHTMLAttributes<T> extends HTMLAttributes<T>の部分はこちらのファイルを参照しました。

./node_modules/@types/react/index.d.ts
├── node_modules
    ├── @types
        ├──react
           ├──index.d.ts
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json

あとは普通のinterface、型宣言です。当たり前ですが、autofocus等にもHTMLで有効な型が決まってるのでそれに合わせてください。
参考 : MDN 入力欄 (フォーム入力) 要素

declare

アンビエント宣言というらしいです。恥ずかしながら初めて知りました。
参考 : TypeScriptの翻訳機が知らないような機能を使う時は、アンビエント宣言をしておく

3. tsconfig.jsonにreact.tsを読むように追記

tsconfig.json
{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./src/type"]
  },
  "include": ["src"]
}

いらない部分は端折ってます。"./node_modules/@types"はデフォルトの設定です。そのあとに先ほど作成したreact.tsのディレクトリ"./src/type"を書きます。

4. npm start

$ npm start

サーバーを立てるとエラーが消え、autofocusもコンパイルできました。npm startをしたまま操作していた方は、一度落として再実行してください。

注意点

  • create-react-appはコンパイルする対象が"./src"配下になっているので、プロジェクトのルートに新規ディレクトリを作成して型拡張を記述しても動きません。
  • 途中でも書きましたが、拡張したい型は./node_modules/@types/react/index.d.tsから探してきてください。

参考

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

はじめてのThree.js 2章 日記

はじめてのthree.js

新たに知った知識

  • ceil関数はmathの切り上げ
Math.ceil(.95) //1になる
  • fogは一行
scene.fog = new THREE.Fog(, nearプロパティ, farプロパティ)
  • 3dモデリングでは三角形派と四角形派がある
  • faceは何番のポイントを3つ指定して生成する
  • 鳥類にはfov(視野)が360°の生き物もいる, 通常は60-90(デフォルト50)

気づいたこと

  • オブジェクトを生成する時にnameプロパティを追加しておくと操作しやすい
  • 一つのオブジェクトに対してのプロパティ多すぎ笑 早く覚えよう
  • メインのプロパティはほとんどp5と同じ

wow moment

まだ解決していない点

3章はこちら

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

Kinx ライブラリ - CSV Parser

CSV

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。

今回は CSV パーサーです。TSV もあります。

使い方

using CSV

CSV ライブラリは標準組み込みではないため、using ディレクティブを使用して明示的に読み込む。

using CSV;

全てを一度にパース

ファイルを読み込んで全体をパースするには、CSV.parse() を使う。

using CSV;
var r = CSV.parse("filename.csv");

データの形式は以下の通り。全ての行オブジェクトの配列として返す。行オブジェクトには data フィールドがあり、そこに配列として各要素の文字列が設定される。ただし、コメント行(先頭が #)の場合、data フィールドは空となり comment フィールドにコメント文字列が入る(# も含む)。

[{
    "data": ["aaa", "bbb", "ccc"]
}, ..., {
    "data": {},
    "comment": "# comment"
}]

1行ずつコールバック

行数が多い場合、全体を一気にパースすると大量にメモリを消費する可能性がある。そこで、以下のように 1 行ずつコールバックさせることもできる。コールバックの引数は 1 行分の行オブジェクトとなる。

using CSV;
CSV.parse("filename.csv", &(row) => {
    System.println(row);  // like `{ data: ["aaa", "bbb", "ccc"] }`
});

尚、この場合 CSV.parse() は値を返さない(null を返す)。

文字列をパース

文字列をパースする場合、CSV.parseString() を使う。コールバック版も同様。

using CSV;
var r = CSV.parseString(csvString);
CSV.parseString(csvString, &(row) => {
    ...
});

CSV 仕様

解釈する CSV の仕様は以下の通り。

  • 先頭文字が # の場合、コメント行と認識。
  • ダブルクォートは無くても良い。
  • ,"、改行を含めたい場合はダブルクォートが必要。
  • ダブルクォート内では以下の仕様。
    • , は直接記載可能。
    • """ と重ねて記載。
    • 改行はそのまま改行することで表現する。
  • 最終行の末尾に改行コードは無くても良い。

サンプル

サンプルとして以下をパース。

# comment in CSV.
abcde,efgh,non-quoted string
"abcde","efgh","quoted string"
"abcde",efgh,"quoted & non-quoted string in the same row"
"abcde",efgh,"can use , or "" in csv"
"abcde",efgh,"can use newlines

in the row"
# another comment in CSV.
abcde,efgh,not necessary \n the end of csv

結果はこうなる。

[{
    "data": {},
    "comment": "# comment in CSV."
}, {
    "data": ["abcde", "efgh", "non-quoted string"]
}, {
    "data": ["abcde", "efgh", "quoted string"]
}, {
    "data": ["abcde", "efgh", "quoted & non-quoted string in the same row"]
}, {
    "data": ["abcde", "efgh", "can use , or \" in csv"]
}, {
    "data": ["abcde", "efgh", "can use newlines\n\nin the row"]
}, {
    "data": {},
    "comment": "# another comment in CSV."
}, {
    "data": ["abcde", "efgh", "not necessary \\n the end of csv"]
}]

TSV

TSV とは CSV のカンマ区切りがタブ区切りになったもの。

using CSV は一緒。使うときに CSVTSV にするだけで使える。

using CSV;
var r = TSV.parse(tsvFilename);
TSV.parseString(tsvString, &(row) => {
    ...
});

パーサー

パーサー自体を Kinx で書いています。

パーサーの形態は1文字先読みの再帰下降パーサーですが、そもそも CSV の仕様に再帰的な要素がないので再帰しません。言うなればただの下降パーサーです。

コードは GitHub にあります。全体で100行ちょっとしかないのですぐ理解できるかと思います。

内部ライブラリ用の特殊キーワードについて

ちなみに、_class_function というキーワードが使われていますが、一般では使いません。意味は classfunction と同様です。

何が違うかというと、_ 付きのほうは例外発生時にスタックトレースに情報を残さないといった動作をします(また、-d オプションで VM コードを表示しません)。これは内部ライブラリの場合、スタックトレースに情報を残すと混乱する可能性があるためです。例えば、例外の起点が内部ライブラリの行番号を示していると、そこに問題があると勘違いする可能性があるためで、例外の起点はあくまでユーザー・コードを示すようにしています。VM コード出力でもユーザー・コードの邪魔になると考えられるので表示しないといった配慮がなされる形です。

このストラテジーに従うと逆にライブラリ内部にバグがあった場合は調査困難になりますが、通常ケースでパラメーター異常による例外、といったケースのほうが一般的と考えられるので、この仕組みを入れてあります。尚、内部ライブラリ自体のデバッグをしたい場合は _classclass に、_functionfunction に変更すればできます。

おわりに

CSV パーサーは何か既存のライブラリを使おうかと思って探したんですが、イマイチ統合するのが手間取りそうだったのと、実は簡単なんじゃないかと思ってスクラッチで書いてみました。何か問題があればお知らせください。

ではまた、次回。

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

defer属性で外部JavaScriptファイルはheadタグ内に

覚えたてため、記録に残しておく。

これまではHTMLのbodyタグの1番最後に置いていた

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="./style.css">
  <title>title</title>
</head>

<body><script src="./main.js"></script>
</body>
</html>

何故かというとHTMLのheadタグ内に外部JavaScriptファイルを読み込ませようとすると、HTMLのbodyタグ内を読み込む前にJavaScriptの内容を実行しようとするためエラーが発生する。そのため、HTMLのbodyタグ終了直前に外部ファイルを置いていた。そうすると、HTMLを読み込み終わってからJavaScriptが実行されるためエラーが発生しない。

defer属性を追加すると

しかしながら、scriptタグにdefer属性というものを使用すると、外部JavaScriptファイルが非同期でダウンロードされ、HTMLファイルを読み込み終わってから外部JavaScriptファイルが実行されるためエラーが発生しない。

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="./style.css"><script defer src="./main.js"></script>
  <title>title</title>
</head>

<body>
</body>
</html>

参考

https://www.wakuwakubank.com/posts/614-javascript-async-defer/

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

Javascriptの復習(1)

この記事について

プログラミングスクールでJavaScriptを勉強した初心者がアウトプットの場として書いたものなので、偶然見つけてしまった人は閉じてください(見る価値がないと思います)。Rubyも既に学習したのでRubyと比較しつつ書いていきたいと思います。

JavaScriptについて

スクリプト言語(習得が簡単になるように工夫されている)の一種で、ブラウザに動きのある変化を加えるのに必須な言語だそうです。Javaと名前が似ていますが、全くの別物です。

JavaScriptの書き方

主に2つあり、①コンソール上に直接書く、②html上にscriptタグで呼び出す の2通りです。
①の方法についてですが、コンソール上ではJavaScriptのコードの実行結果をその場で確認できます。
②については、以下のようにhtml上に書くことで呼び出すことができます。

index.html
<body>
    <script>
        // この中にJavaScriptのコードを記述すれば実行結果が出る。
    </script>
</body>

また、JavaScriptのコードが書かれた別のファイルの名前を書くことでファイルごと呼び出すこともできます。

index.html
<body>
    <script src="script.js"></script>
</body>

console.log()

Javascriptの書き方を学ぶにあたって初めて使った関数で、括弧の中に文字列や数字を入れるとコンソール上で表示されます。

script.js
//文の終わりに;をつける(無くても問題ない時もあるが念のため)
console.log("Hello World");

変数の定義

変数を定義すれば、関数に使用することができます。
Rubyと違い letという文字から定義を始める必要があります。

script.js
//変数の定義(let 変数名 = 値や文字列)
let name = "";
//これで実行するとコンソールには"猫"と表示される
console.log(name);

//変数は上書きすることもできる
let name = "";
name = ""
//猫ではなく犬と表示される
console.log(name)

定数の定義(変数との違い)

定数も定義して使用するものですが、変数と違い上書きができないです。

script.js
//定数の定義(const 変数名 = 値や文字列)
const name = "";
console.log(name);
//これで実行するとコンソールには"猫"と表示される
//定数を上書きしようとするとエラーが出ます。
name = ""

スクリーンショット 2020-05-22 21.26.09.png

一旦ここで区切ろうと思います。
次回は条件式について書こうと思います。

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

はじめてのThree.js 1章 日記

はじめてのthree.js

始めた理由

  • ある程度htmlとcssの知識があり、p5.jsのコーディング経験があったため
  • 自作のポートフォリオサイトを作るためと将来仕事にするならリッチなサイトを作成したいため Three.jsを勉強し始めた
  • オライリーはdeep learningの本など質が良かったのと、three.jsを調べているとほとんどこの本がはじまりっぽかったので記録してみた

新たに知った知識

  • デスクトップでもモバイルでも結構webGLはサポートされてる(1部のみだと思っていた)
  • Unix/Linuxシステムにはデフォルトでpythonがインストールされてる
  • pythonのmオプションはmoduleのm
  • npmのgオプションはglobalのg
  • monogooawの存在
  • webGLレンダラー以外にもCSS,SVG,canvasなどがある
  • lookAtでカメラが対象物に向く
  • shadowの計算は重いのでデフォルトではoffになってる
  • statsはアニメーションをするのを手伝ってくれるライブラリ
  • three.jsでよく見るGUIライブラリはgoogle発

気づいたこと

  • 環境構築段階でc9の話が出ていたけど、この時点ではまだamazonに買収されていないのでテック業界のスピードの速さを感じた
  • VS codeのlive serverめちゃ便利
  • 本の文字を追うよりもコードを読んだ方が早い時もある
  • renderSceneはP5でいうdrawみたいな概念

wow moment

まだ解決していない点

2章はこちら

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

北海道の訪問記録を残すWebサイトを作った

全国の北海道ファンの皆さんこんにちは.
早速ですがこの地名を読めますか?
幌筵1
当然読める日本人の方々はここから
漢字の読めない若い方々はここから
読めなかった非国民の方々はここからアクセスできます.
私は北方領土をロシア領と呼ぶ売国奴2なのでアクセスできませんでした.

北海道は広いですよね.いろんなところに行きますよね.

今回,北海道の自治体単位での訪問記録を付けられるWebサイトを作ったので紹介いたします.
要するに,経県値の丸パクリですね.
皆と共有して思い出話に花を咲かせましょうマウント取り合いまくりましょう.

Webサイト概要

ページ概要
日本が主権を失った島々が堂々と居座っていますが,日本の自治体としては正しく存在するのでセーフです.

使い方

  • 北海道が表示されるので自治体を直接ポチポチするか,下の振興局別 のリストからポチポチします.
  • 右下のTwitterアイコンをクリックして共有します.

最大レベルは925です.真の道産子目指して頑張りましょう.
赤に染まった北海道を目指しましょう.

ホストサービスはFirebase Hostingを使っています.
使用したライブラリはVue.jsです.初めて触りました3
JSは素のまま書いています.トランスパイルなんて知りません.

サイト作成までの流れ

  1. 国土地理院から北海道の地形データを引っ張ってくる
  2. SVGに変換,IDなどの情報などをつけ足す
  3. Firebase Hostingのテンプレートを作成
  4. JSのコードを書く(定義含め300行くらい)
  5. デプロイ

簡単ですね.勉強含め4日くらいでできました.

地味にはまったところ

SVG中のstyle要素の名前空間

Vue.jsのコンテキストに含まれるSVGにそのまま<style>要素を置くとtemplate compile errorになるため,名前空間を宣言してあげましょう4

コンパイルエラーになる例

NG
<style>
path {
    fill: #FFFFFF;
}
</style>

動作する例

OK
<svg:style>
path {
    fill: #FFFFFF;
}
</svg:style>

Win10 Mobile版Edgeでのv-on:inputイベント

Windows 10 Mobileの端末ではRadioボタンやSelect要素のinputイベントが発火しません.
他のブラウザやデスクトップ版Edge5では動作するため,見落としがちです.changeイベントを使いましょう.

イベントが発生しない例

NG
<input type="radio" V-bind:id="'option'+index" v-bind:value="index"
    v-model="value" v-on:input="$emit('input',{'index':index,$event})">

イベントが発生する例

OK
<input type="radio" V-bind:id="'option'+index" v-bind:value="index"
    v-model="value" v-on:change="$emit('change',{'index':index,$event})">

データ更新に合わせたURLの変更

vue-routerを併用する際に同時にURLも変更したい際の実装例のお決まりはあるのでしょうか?
今回は算出プロパティ内で変更しましたが,ほかのやり方があると思います.

Gorilla
var app = new Vue({
    el: "#app",
    router,
    computed:{
        newurl:function(){
            return "abc"
        },
        rout:function(){
            router.replace({path:`/app/${this.newurl}`})
            return ""
        },
    }
}).$mount("#app")

所感

正直,普段作る業務アプリの範囲内だと,UIとViewModelを分けるうまあじがあまりありませんね.
開発してて思ったのは「よくわからないけど動いてる.すごい」でした,
今回のように,趣味で使う小さいアプリには簡単に適用できたので,慣れの問題でしょうかね.

最後に私の訪問マップにてお終いとさせていただきます6
以上,ありがとうございました.

その他
  • 写真集
    • 斜里町 知床1湖
    • 美瑛町 色彩の丘
    • 礼文町 北のカナリアパーク
    • 釧路市 幣舞橋 北海道行きたいですね.


  1. ほろむしろ,またはぱらむしる 

  2. 実の親からの呼称です 

  3. 普段はWindows FormsアプリとAndroid 4.1対応のアプリを作っています.Windows 7はまだ買えます 

  4. この記事の価値はここにしかないのでここ以外は読み飛ばしていただいて構いませんよ 

  5. Dev ToolsのEmulationでLumia 650にすると再現できます. 

  6. アクセスできないといいつつアピールする屑 

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

TypeScript: テンプレート文字列のタグ関数の作り方

テンプレート文字列とは

バッククオート`で囲った文字列のこと。変数展開が使える。

const str = `foo bar`
const str2 = `hoge ${str}`
//=> "hoge foo bar"

タグ関数とは

テンプレート文字列の前に書くことで、文字列を処理できる関数。この関数は受け取った文字列を処理して、文字列を返してもいいし、受け取った文字列を処理して、何らかのオブジェクトを返したりしてもいい。

const str = tag`foo baar`
         // ^^^ ここで使える関数

タグ関数の作り方

受け取った文字列をそのまま返す関数の実装例です:

const tag = (
  template: TemplateStringsArray,
  ...expressions: ReadonlyArray<any>
) => template.reduce((a, b, idx) => a + b + `${expressions[idx] ?? ''}`, '')
console.log(tag`hello world ${new Date()}`)
//=> hello world Fri May 22 2020 20:01:16 GMT+0900 (Japan Standard Time)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTMLから画像を生成するシンプルな方法(Node.js)

どうもこんにちは、@y_temp4です。

最近、趣味で個人開発している Web サービスの AnyMake にて、画像を動的に生成するプログラムを書く機会があったのでその方法についてまとめてみます。

関連記事:埋め込みコンテンツを追加しました!| AnyMake | note

node-html-to-image を使う

自分は画像生成周りは全然詳しくないのですが、普段フロントエンドのコードを書くことが多いので

HTML から画像生成できたら便利だな〜

と思いました。

そこで少し調べると、node-html-to-imageというライブラリが見つかり、これを使うと割と簡単にやりたいことが実現できそうでした。

使い方

以下のコードは README からの引用ですが、このように HTML を引数として渡してやると、その HTML の表示結果が画像として出力されます。

const nodeHtmlToImage = require("node-html-to-image");

nodeHtmlToImage({
  output: "./image.png",
  html: "<html><body>Hello world!</body></html>"
}).then(() => console.log("The image was created successfully!"));

上記の例だと、「Hello World!」と書かれたシンプルな画像ファイルが生成されます。

今回自分は画像を返す API を作りたかったので、README にある以下のコードを参考に作成しました。

const express = require("express");
const router = express.Router();
const nodeHtmlToImage = require("node-html-to-image");

router.get(`/api/tweet/render`, async function(req, res) {
  const image = await nodeHtmlToImage({
    html:
      "<html><body><div>Check out what I just did! #cool</div></body></html>"
  });
  res.writeHead(200, { "Content-Type": "image/png" });
  res.end(image, "binary");
});

非常にシンプルですがこれで最低限やりたいことは実現できたので、お手軽に画像生成したい場合はこれが良さそうです。


ちなみにですが、このライブラリの内部処理としては Puppeteer で HTML を描画してスクリーンショットを撮っているだけです。

言われてみれば確かにこれでいけるな...と README や内部の処理を読んでいて思いましたが、やっている事的にあんまり軽くはなさそうなので、もしこれより良さそうな方法で簡単に HTML から画像を生成する方法をご存知の方がいらっしゃればぜひコメント欄にて教えていただけますと嬉しいです ?

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

Vue.jsの学習記録

最近Vue.jsを勉強しているので、備忘録として。

Vue.jsを勉強しようと思った理由

  • JavaScriptの基礎的な勉強を一通り終えた(TODOアプリを作れるレベル)ので、新しくJSのフレームワークに触れてみたかった。
  • JSには様々なフレームワーク(Angular,Reactとか)と比べて、人気が高く、初学者でもとっつきやすい印象を受けたから。
  • 独自のHTMLタグを作ることができ、データバインディングが非常に簡単という特徴があるから、開発の可能性が広がる。

といった点から、Vue.jsを学びました。
ちなみに、学習している教材は、udemyのVue JS入門決定版!jQuery を使わない Web 開発 - 導入からアプリケーション開発まで体系的に動画で学ぶを使って勉強しています。

Vue.jsの導入

以下のURLからVue.jsのインストールができます。
Vue.jsのインストール
導入する方法は、直接ダウンロードする方法と、CDNによる読み込み、NPMを利用したダウンロード、CLIを利用したダウンロードの4つの方法があります。

学習を目的としている方は、直接ダウンロードする方法か、CDNによる読み込みの方法が比較的すぐ導入できるのでオススメです。

初めに

HTMLの記述

<body>
  <div id="app">
      <!-- ここにVue.jsの内容を記述 -->
  </div>

</body>

divタグを作って、idを"app"とします。

JavaScriptにVueのインスタンスを作成

var app = new Vue({
  el: '#app'
})

JavaScriptにVueインスタンスを作成しました。elプロパティを使って、先ほどHTMLに記述したid"app"をマウント(id"app"が含まれている要素を結びつける)しています。

ここまでが、Vue.jsを書く初めの準備。

Hello, Vue.js!と表示させたい

Vue.jsを使って、HTML上でHello, Vue.js!と表示させます。

<body>
  <div id="app">
      <p>{{ message }}</p>
  </div>

</body>
new Vue({
  el: '#app',
  data: {
      message: 'Hwllo, Vue.js!'
  }
})

HTMLで、まずmustache構文{{}}を使ってオブジェクトを呼び出します。
JSで、dataプロパティにmessageオブジェクトを定義し、Hello, Vue.js!と記述します。
以上がHTML上にHello, Vue.js!と表示させる方法です。

ディレクティブについて

ディレクティブとは、Vue独自の属性のことです。(v-bind、v-modelとか)
勉強してきたディレクティブを紹介します。

v-bind

v-bindはclassvaluestyleなど、v-bind:valueのようなかたちで属性を呼び出すことが可能です。
一部紹介

<div id="#app">
    <!-- input欄の中にhogeの値を表示 -->
    <input type="text" v-bind:value="hoge">
    <!-- urlプロパティに記入した値のリンク先に飛ぶ -->
    <a v-bind:href="url">Googleに飛びます</a>
</div>
new Vue({
   el: '#app',
   data: {
       hoge: 'hoge',
       url: 'https://www.google.com'
   }
})

こんな記述になります。

v-model

双方向データバインディングができます。
以下のような記述です。

<div id="app">
    <!-- pタグとinputタグ共に'ホゲー'の文字列が出力される -->
    <p>{{hoge}}</p>
    <input type="text" v-model="hoge">
</div>
new Vue({
    el: '"app',
    data: {
        hoge: 'ホゲー'
    }
})

inputタグの中で文字を変更した時、リアルタイムにpタグの文字列も影響します。

v-if

Vue.jsで書く、if文ですね。

<div id="app">
    <p v-if="hoge"> "hoge"がtrueなら表示されます!! </p>
</div>
new Vue({
    el: '#app',
    data: {
        hoge: true 
    }
})

dataプロパティにfalseと記述すると、pタグが消えます。

v-for

Vue.jsで書く、おなじみfor文ですね。

<div id="app">
    <ul>
        <li v-for="friut in fruits">
        <!-- 配列'fruits'の中身が全て表示される -->
            {{ fruit }}
        </li>
    </ul>
</div>
new Vue({
    el: '#app',
    data: {
        fruits: [ 
                'apple',
                'banana',
                'grape'
            ]
    }
})
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Electronで擬似ツイッターデスクトップアプリを表示させてみよう

誰でも10分でできます。

本日、Traversy MediaにてElectronの動画がアップされており、Udemyにてコースの販売を開始したとのアナウンスを受け、さっそく取り掛かってみました。
https://www.youtube.com/watch?v=pNdAFwKPbdQ&t=2s
Udemy割引コードは動画概要欄。(本日5/22から5日間限定と30日限定のものがあります。)

興味のある方は安いうちに買っちゃいましょう。

まえおき

!!!本記事はプログラミング歴3ヶ月の人間が書いております。!!!
node.jsすらもイマイチよくわかっていません。(なんだよdenoって!)
https://nodejs.org/en/download/ (要インストール)

ですので、詳細に関しては公式ドキュメントか、Traversy Mediaの過去の動画をご覧ください。
公式:https://www.electronjs.org/

Traversy Media: https://www.youtube.com/watch?v=kN1Czs0m1SU

英語が全くダメという方は、大阪コード学園さんのこちらの動画をご参照ください。
https://www.youtube.com/watch?v=sYbIgLKQu7g

導入

まずはプロジェクトフォルダ内にjsonファイルをnpm initで設置。

デフォルトではjsonファイル内の"scripts"が下記のようになっているので、"electron ."としておきます。

デフォルト
"test": "echo \"Error: no test specified\" && exit 1"

変更後
"start": "electron ."

Electronをインストール

次にnpm i -D electronでインストール。

同時に main.jsファイルも同ディレクトリ上に作成しておきましょう。

main.jsを編集

main.js
const { app, BrowserWindow } = require('electron');

let mainWindow;

function createMainWindow() {
  mainWindow = new BrowserWindow({
    title: 'ImageShrink',
    width: 500,
    height: 600,
    icon: './assets/icons/Icon_256x256.png' 
/*
icon:は、Windowsデスクトップであれば
起動したウィンドウの左上にアイコンが表示される。
Macでは仕様上表示されない。
*/
  });
  });

  mainWindow.loadURL(`https://twitter.com`); // 
}
/*
呼び出し先のURLはバックスラッシュで囲います。
ディレクトリないのファイルを読み込む場合は、
mainWindow.loadFile('./app/index.html');
といった具合にシングルクオートで囲います。
*/
app.on('ready', createMainWindow);

ファイルを保存したら、あとはターミナル上でnpm startと実行すれば、こんな感じでアプリのウィンドウが表示されるはずです。
Screen Shot 2020-05-22 at 17.43.09.png

Flutter同様、あまりプログラミングに詳しくなくても、楽しく取り組めそうだなと思い、早速勉強してみる事にしました。引き続きElectron関連の内容を吐いていく予定です。アカン箇所などありましたら、優しくご指導ご鞭撻の程、宜しくお願い申し上げます。

※qiita二本目の投稿ですが、本日はじめてスニペットのmarkdown技法を知りました(笑)
今後もアウトプットを兼ねてqiita活用させていただきます。

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

AngularJS文法チートシート

この内容について

この内容は、私が運営しているサイトに、より見やすく掲載しているので、よければそちらもご活用ください。
AngularJSチートシート | コレワカ

AngularJSとは

AngularJSはWeb系の全ての機能が完結するJavaScript製のMVWフレームワークのこと
AngularJS公式サイト

基本的な書き方

HTML
<div ng-app="myApp" ng-controller="myCtrl">
  <!-- AngularJS適用範囲 -->
</div>
JavaScript
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {});

変数・型

変数

See the Pen AngularJS_variable by engineerhikaru (@engineerhikaru) on CodePen.

配列変数

See the Pen AngularJS_array_variable by engineerhikaru (@engineerhikaru) on CodePen.

連想配列変数

See the Pen AngularJS_object-variable by engineerhikaru (@engineerhikaru) on CodePen.

ディレクティブ

ng-appディレクティブ

AngularJSアプリケーションのルート要素であることをAngularJSに伝える。

HTML
<div ng-app="myApp">
  <!-- -->
</div>
JavaScript
var app = angular.module("myApp", []);

ng-initディレクティブ

AngularJSアプリケーション開始時に変数を作成・初期化する


See the Pen
AngularJS_ng-init
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-controllerディレクティブ

AngularJSアプリケーション変数を処理するコントローラーを追加する

HTML
<div ng-app="myApp" ng-controller="myCtrl">
  <!-- AngularJS適用範囲 -->
</div>
JavaScript
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {});

ng-modelディレクティブ

入力値をスコープ内の変数にバインドする


See the Pen
AngularJS_ng-model
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-repeat

配列を展開する


See the Pen
AngularJS_ng-repeat
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-if

条件によってDOM内の要素を削除または再作成する


See the Pen
AngularJS_ng-if
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-show

条件に基づいて要素を表示・非表示にする (反対はng-hide)


See the Pen
AngularJS_ng-show
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-change

入力値が変更された時に、関数を実行する


See the Pen
AngularJS_ng-change
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-switch

一致する条件に基づいて要素の制御をする


See the Pen
AngularJS_ng-switch
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-click

ボタンがクリックされた時に、関数を実行する


See the Pen
AngularJS_ng-click
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-style

要素にスタイルを指定する


See the Pen
AngularJS_ng-style
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-class

クラスを動的に設定する


See the Pen
AngularJS_ng-class
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-submit

submitボタンがクリックされた時に、関数を実行する


See the Pen
AngularJS_ng-submit
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-bind

テキストデータを出力


See the Pen
AngularJS_ng-bind
by engineerhikaru (@engineerhikaru)
on CodePen.


ng-cloak

AngularJSが制御を開始するまでコンテンツを表示しない


See the Pen
AngularJS_ng-cloak
by engineerhikaru (@engineerhikaru)
on CodePen.


フィルター

uppercase

文字列を全て大文字にする (反対はlowercaseで全て小文字になる)


See the Pen
AngularJS_filter-uppercase
by engineerhikaru (@engineerhikaru)
on CodePen.


number

数字をカンマ区切りにする (オプションで小数点以下何桁まで表示するかを設定可能)


See the Pen
AngularJS_filter-number
by engineerhikaru (@engineerhikaru)
on CodePen.


currency

数字を通貨形式にフォーマットする


See the Pen
AngularJS_currency
by engineerhikaru (@engineerhikaru)
on CodePen.


date

日付を指定した形式にフォーマットする


See the Pen
AngularJS_filter-date
by engineerhikaru (@engineerhikaru)
on CodePen.


orderBy

配列を昇順・降順に並べる


See the Pen
AngularJS_orderBy
by engineerhikaru (@engineerhikaru)
on CodePen.


limitTo

指定された数までの配列の中身を取り出す


See the Pen
AngularJS_filter-limitTo
by engineerhikaru (@engineerhikaru)
on CodePen.


filter

指定された文字に一致する配列の中身を取り出す


See the Pen
AngularJS_filter-filter
by engineerhikaru (@engineerhikaru)
on CodePen.


この内容について

この内容は、私が運営しているサイトに、より見やすく掲載しているので、よければそちらもご活用ください。
AngularJSチートシート | コレワカ

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

【Nuxt.js】firebase基礎編(Auth版):メールアドレスログインをできるようにしよう

前置き

login4.gif
ログイン機能があると
サービスの幅が広がりますよね?
でも難しそう?
そう思っていませんか??
それを解決するのがfirebase Auth!
これを使えば簡単に実装できます?

今回はログインの実装がメインです!
そのため新規アカウント作成画面は作りません。
まずauthに慣れていきましょう?

?アプリ開発基礎編
 追加オプション機能では
 アカウント作成などもやっていきます!
https://note.com/aliz/n/n8411db2c9a20

【使うもの】
・Firebase Authentication
・Vuex(ログイン状態の保持)

Step1: firebase authの準備

まずはfirebaseでプロジェクトを作成し、
ログイン方法の設定をしていきます?

プロジェクトの作成方法はこちら
step1-5. アカウントを選択
プロジェクト概要画面まで
https://note.com/aliz/n/nacc97fe7d019#Ls0Km

・プロジェクトができたら
 サイドメニューのAuthenticationを選択
 ログイン方法を設定を選択

auth1.png

・メールを選択

Frame 2.png

・メールでのログインを有効にし、保存

Frame 3.png

・usersタブに戻りユーザーを追加

Frame 4.png

登録できるとユーザー情報が表示されます?

Frame 5.png

Step2: firebaseとの連携

こちらの記事の
Step3、Step4をご覧ください?
https://note.com/aliz/n/nacc97fe7d019#Ls0Km

plugin/firebase.js
import firebase from "firebase/app"

if (!firebase.apps.length) {
firebase.initializeApp({
 apiKey: "貼り付け",
 authDomain: "貼り付け",
 databaseURL: "貼り付け",
 projectId: "貼り付け",
 storageBucket: "貼り付け",
 messagingSenderId: "貼り付け",
 appId: "貼り付け",
 measurementId: "貼り付け"
})
}

export default firebase

Step3: ログイン機能の実装

login.gif
公式ガイドのこちらを見てみましょう?
Get Startedタブから順番に進めてみます?
ログインフォームを作って
methodsにこの関数を書けば良いわけですね?
signInWithEmailAndPassword()

https://firebase.google.com/docs/auth/web/start#sign_in_existing_users

index.vue
<template>
 <div class="page">
   <form
     class="form"
     @submit.prevent
   >
     <label class="label">
       <span class="label">
         email
       </span>
       <input
         class="input"
         type="text"
         v-model="email"
       >
     </label>
     <label class="label">
       <span class="label">
         password
       </span>
       <input
         class="input"
         type="password"
         v-model="password"
       >
     </label>
     <button
       class="button"
       type="submit"
       @click="login"
     >
       Login
     </button>
   </form>
 </div>
</template>

<script>
import firebase from '~/plugins/firebase'

export default {
 data() {
   return {
     email: '',
     password: ''
   }
 },
 methods : {
   login() {
     firebase.auth().signInWithEmailAndPassword(this.email, this.password)
     .then(user => {
       // eslintがある場合は
       // 引数にuser追加とeslint-disable-lineの記載
       console.log('成功!')// eslint-disable-line
     }).catch((error) => {
       alert(error)
     });
   },
 }
}
</script>

<style lang="scss">
 .Login {
   > .form {
     > .button {
       display: block;
     }

     > .label {
       > .input {
         display: block;
         border: 1px solid blck; 
       }
     }
   }
 }
</style>

Step4: Vuexに移行

今のままだとログインするだけなので
Vuexを使うメリットがないのですが、
ログインしたという状態を
保持するための準備です!

index.vue
<template>
 <div class="page">
   <form
     class="form"
     @submit.prevent
   >
     <label class="label">
       <span class="label">
         email
       </span>
       <input
         class="input"
         type="text"
         v-model="email"
       >
     </label>
     <label class="label">
       <span class="label">
         password
       </span>
       <input
         class="input"
         type="password"
         v-model="password"
       >
     </label>
     <button
       class="button"
       type="submit"
       @click="login"
     >
       Login
     </button>
   </form>
 </div>
</template>

<script>
import firebase from '~/plugins/firebase'

export default {
 data () {
   return {
     email: '',
     password: '',
   }
 },
 methods : {
   login (email, password) {
     this.$store.dispatch('login', {email: this.email, password: this.password})
   },
 }
}
</script>
store/index.js
import firebase from '~/plugins/firebase'

export const state = () => ({
})

export const getters = {
}

export const actions = {
 login(context, payload) {
   firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
     .then(user => {
         console.log('成功!')
       }).catch((error) => {
         alert(error)
       })
 },
}

export const mutations = {
}

【解説】
index.vueで入力した
email, passwordを引数で渡します?
index.jsのactionsでは
第一引数が必ずcontextになるので
第二引数としてemail, passwordを受け取ります?

Step5: ユーザーデータの取得

login2.gif

ログインができたら
ユーザー情報を表示したいので
情報を取得してきましょう!?
こちらを見てみましょう?
onAuthStateChanged()

https://firebase.google.com/docs/auth/web/start#set_an_authentication_state_observer_and_get_user_data

index.js
import firebase from '~/plugins/firebase'

export const state = () => ({
 user: {
   uid: '',
   email: '',
 },
})

export const getters = {
 user: state => {
   return state.user
 }
}

export const actions = {
 login({ commit }, payload) {
   firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
     .then(user => {
         console.log('成功!')
         firebase.auth().onAuthStateChanged(function (user) {
           if (user) {
             commit('getData', { uid: user.uid, email: user.email })
           }
         })
       }).catch((error) => {
         alert(error)
       })
 },
}

export const mutations = {
 getData (state, payload) {
   state.user.uid = payload.uid
   state.user.email = payload.email
 }
}

【解説】
それぞれの役割を確認したい方はこちら
https://note.com/aliz/n/n6f4a42bce5b5

?state?
 ・取得情報の格納場所

?getters?
 ・stateを取得

?actions?
 ・ログインと同時に取得したいので
  ログイン成功時(.then)に記載
  →step7でログインと取得で
   切り分けるように調整をします✂︎
 ・ガイド参考
  これで取得ができます!
  ID:user.uid
  ?:user.email
  引数に直接user.uidとは書けないので
  一旦uidと置き換えます
 ・commit
  ログイン情報をstateに入れるため
  mutationsを呼び出します

?mutations?
 取得した情報でstateを書き換えます

【index.vue】
computed users()でgettersを呼び出し表示

Step6: ログイン情報保持

login3.gif

先ほどログインが成功した時に
ユーザーデータを取得できたので
こちらを追加しましょう!
・stateにログイン状態の真偽値を追加
 →ログインできたらtrueに変更
・v-ifで真偽値による出し分け
 →ログインtrue, ログアウトfalse

index.js
import firebase from '~/plugins/firebase'

export const state = () => ({
 user: {
   uid: '',
   email: '',
  // ログイン状態の真偽値を追加
   login: false,
 },
})

export const getters = {
 user: state => {
   return state.user
 }
}

export const actions = {
 login({ commit }, payload) {
   firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
     .then(user => {
         console.log('成功!')
         firebase.auth().onAuthStateChanged(function (user) {
           if (user) {
             commit('getData', { uid: user.uid, email: user.email })
             // ユーザー情報の取得と同時にcommitで真偽値の切り替え
             commit('switchLogin')
           }
         })
       }).catch((error) => {
         alert(error)
       })
 },
}

export const mutations = {
 getData (state, payload) {
   state.user.uid = payload.uid
   state.user.email = payload.email
 },
 // 真偽値を切り替えるmutationsを追加
 switchLogin (state) {
   state.user.login = true
 },
}
index.vue
<template>
 <div class="page">
   <p
     v-if="user.login"
     class="text"
   >
     {{ user }}
   </p>
   <form
    v-else
   >
    // 省略
   </form>
 </div>
</template>

<script>
import firebase from '~/plugins/firebase'

export default {
 computed: {
   user () {
     return this.$store.getters['user']
   },
 },
 data () {
   return {
     email: '',
     password: '',
   }
 },
 methods : {
   login (email, password) {
     this.$store.dispatch('login', {email: this.email, password: this.password})
   },
 }
}
</script>

Step7: ログイン保持のチェック✅&調整

login4.gif

最後にcomponentsに移動させ
全てのコンポーネントで
ログインが保持されるかチェック✅
リロードしない限り
保持されることが確認できます✨?

また、store/index.jsのactionsを
役割で切り分けてみます✂︎

【Login.vue】
index.vueをまるまるコピペ
・fibaseのimportが不要→削除
・全体のdiv class="login"に変更

Login.vue
<template>
 <div class="login">
   <p
     v-if="user.login"
     class="text"
   >
     {{ user }}
   </p>
   <form
     v-else
     class="form"
     @submit.prevent
   >
     <label class="label">
       <span class="label">
         email
       </span>
       <input
         class="input"
         type="text"
         v-model="email"
       >
     </label>
     <label class="label">
       <span class="label">
         password
       </span>
       <input
         class="input"
         type="password"
         v-model="password"
       >
     </label>
     <button
       class="button"
       type="submit"
       @click="login"
     >
       Login
     </button>
   </form>
 </div>
</template>

<script>
export default {
 computed: {
   user () {
     return this.$store.getters['user']
   },
 },
 data () {
   return {
     email: '',
     password: '',
   }
 },
 methods : {
   login (email, password) {
     this.$store.dispatch('login', {email: this.email, password: this.password})
   },
 }
}
</script>
index.vue
<template>
 <div class="page">
   <Login />
   <p
     v-if="user.login"
     class="text"
   >
     ログインに成功!
   </p>
 </div>
</template>

<script>
import Login from '~/components/Login.vue'

export default {
 components: {
   Login: Login,
 },
 computed: {
   user () {
     return this.$store.getters['user']
   },
 },
}
</script>

【store/index.js】
・ログイン(login)
・ログイン情報の取得(checkLogin)
で切り分けました✂︎
actions内で別のactionsを
dispatchで呼び出せます?

store/index.js
import firebase from '~/plugins/firebase'

export const state = () => ({
 user: {
   uid: '',
   email: '',
   login: false,
 },
})

export const getters = {
 user: state => {
   return state.user
 }
}

export const actions = {
 login({ dispatch }, payload) {
   firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
     .then(user => {
         console.log('成功!')
         dispatch('checkLogin')
       }).catch((error) => {
         alert(error)
       })
 },
 checkLogin ({ commit }) {
   firebase.auth().onAuthStateChanged(function (user) {
     if (user) {
       commit('getData', { uid: user.uid, email: user.email })
       commit('switchLogin')
     }
   })
 },
}

export const mutations = {
 getData (state, payload) {
   state.user.uid = payload.uid
   state.user.email = payload.email
 },
 switchLogin (state) {
   state.user.login = true
 },
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

不等号で文字列型の数値を比較してはいけない理由(JavaScript)

結論

JavaScriptでconsole.log('3' < '12')を実行すると、文字列比較となってしまうため、結果はfalseと返ってくる。
単純に数を比較した場合と異なる結果が出ることがあるので、
数値を比較したい場合は文字列型(string)ではなく数値型(number)で比較すべき。

なぜこうなるのか

不等号で文字列型の数値を比較すると、文字のUnicode値が比較されるかららしい。

こちらを参考にしました

調査記録

実際の挙動も確かめたかったので手を動かしてみた。

numberとnumber

console.log(1 < 2); // true
console.log(2 < 1); // false

number同士の比較なので、当然みたままの数字が大きい方がtrueになる

stringとnumber

console.log('1' < 2); // true
console.log('2' < 1); // false

片方だけstringの場合、stringはnumberに変換されるため、
numberとnumberで比較した場合と同じ結果が得られる。

stringとstring (1文字の場合)

console.log('1' < '2'); // true
console.log('2' < '1'); // false

Unicodeで比較する。
'1'はu100301、'2'は u100302なので、
結局、numberとnumberで比較した場合と同じ結果が得られる。

stringとstring (複数文字列で、文字列の長さが違う場合) *ここがやっかい!

console.log('3' < '12'); // false
console.log('12' < '3'); // true

左詰で、1文字ずつ順番にUnicodeで比較する。
'3'はu100303、'1'は u100301になるので、この時点での結果は'3' > '1'と同じになる。
次の文字列は片方しか値がないため、その時点で処理が終了し、
結局、'3'と'1'を比較した結果が出力される

補足

stringとstringで文字列を比較した際、以下のような結果になるのも上記と同じ理由だと思われる。

console.log('c' < 'ab'); // false
console.log('ab' < 'c'); // true

'c'u00603と'a'u00601を比較し、この時点で結果は'c' > 'a'と同じになる。
次の文字列は片方しか値がないため、その時点で処理が終了し、
'c'と'a'を比較した結果が出力される。

まとめ

数字を比較したい場合は文字列型ではなく数値型で比較すべき。
(JavaScriptの勉強過程で拾い集めた情報からたどり着いた答えです。間違っているところがあればご指摘お願いいたします。)

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

Vue.jsで作る!自動保存するToDoリスト~その1~

誰向けですか?

・Vue.js超初心者
・Vue.jsの基本構文は覚えたけど次は何すればいいの?
・リロードしたら消えちゃう!!

↑という方向けです。自分自身の学習のためにも記事を書かせていただきます!記述で間違いなどがありましたら教えてくださるとありがたいです?‍♂️

このパートの完成イメージ

スクリーンショット 2020-05-22 16.03.14.png

とりあえずinputに書いてbuttonを押すと配列(items)に追加できるようにしよう!
次回以降で順にCSSで見た目、watchやmountedを使用して保存できるようにします。

このパートですること

1.HTMLを書いてVueで操作できるようにする
2.Vueで配列の作成やinputをバインディングする
3.関数を作成して追加、削除できるようにする

1.HTMLの記述

index.html
<div id="app">
        <div>
           <!-- v-modelはこのinputをVueとバインディングさせる役割 -->
            <input v-model="message">
             <!-- @click=""の中はVueで記述するメソッド(@はv-on:の省略です) -->
            <button @click="add_item()">追加</button>
            <button @click="all_del_item">全削除</button>

            <!-- v-for( 配列, index(配列内の要素に振られた添字)を(item,idx)と表している ) -->
            <ol><li v-for="(item,idx) in items">
                {{ item }} <button @click="del_item( idx )"> 削除 </button>
            </li></ol>
        </div>
    </div>

Vue.jsの記述

main.js

const myApp = new Vue({

        // id="app"をVueでマウントする
        el: '#app',
        //何の処理もない配列やオブジェクトなどをおく場所(もしdataに処理を加えたい場合はcomputedを使う)
        data: {
            //配列の初期状態,良い朝の流れである
            items: ['起きる', '洗顔', '歯磨き',],
            //inputの初期状態(既に何か書かれていたら変なので空にしておく)
            message: ''
        },


これではまだmessageの追加や削除ができないので、data{},の続きにmethodsを記述する。

// Vueアプリケーション内で使用する関数を定義する場所
        methods: {
            // itemsに追加するメソッド、{ itemsにmessageをpush(追加)する }
            add_item: function () { this.items.push( this.message ) },
            // 削除ボタンを押した時のメソッド、{ items(自身)を1つsplice(削除)する }
            del_item: function ( _idx ) { this.items.splice( _idx, 1 ) },
            // itemsを全て削除するメソッド、spliceの数を大きい数字にすれば全て消えるでしょう
            all_del_item: function ( _idx ) { this.items.splice( _idx, 10000 ) }
        },

#まとめ

実際に記述してinputに書いた文字が追加・削除できたでしょうか?
次のパートではこの寂しい見た目をCSSで装飾しましょう!(SCSSかBootstrapで予定しています)
この記事を読んでくださったあなたの成長を応援させていただきます!!!

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

Tensorflow.jsのfacemeshで顔向き推定を試してみた

これはHead Pose Estimation using OpenCV and Dlibを参考にTensorFlow.jsのfacemeshにSolvePnPを用いた顔向き推定の実装の備忘録です。

前書き

調査前はVTuberが顔動かすのが当たり前の時代ですので大概のライブラリで顔向き推定は実装しているものかと思っていたのですが、どうやらそうではないようです。

それなりに高速に動くface-api.jsでは議論はあれども実装は進んでいない状況ですし、jeelizFaceFilterlive demoを見る限り速度、精度共に微妙そうです。

調べた中ではML Kitの顔認識で実装しているようですが、今回はブラウザで試してみたいのでML KitはiOS/Android用ですので検証しないことにしました。

そこで高速で精度良いfacemeshに顔向き推定を実装をするのも面白そうだと思い試しました。

実装について

Head Pose Estimation using OpenCV and Dlibを元に実装しまして、Rotation Matrix to Euler Anglesの方法でEulerAnglesを求めました。

実装して解ったのですが、SolvePnPは結構な頻度で外れ値を出します。
そのため、フレームレートを稼ぐのに難があります。
また、facemeshは3D座標を持っていますのでもっと簡単な計算で算出できそうなことに後から気づきました。
これから試す人はオンラインデモを参考にあとで後悔しないようお気をつけください。

OpenCV.jsのビルド

solvaPnP、Rodriguesの関数が必要で、OpenCV.jsをビルドしたのですが、そのままだと9.7Mありましたのでcore only buildをしまして、solvaPnP、Rodriguesを追加したうえで、2.5Mまで縮小することができました。

製作物

オンラインデモ

https://infocom-tpo.github.io/facevrm/

References

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

TensorFlow.jsのfacemeshで顔向き推定を試してみた

これはHead Pose Estimation using OpenCV and Dlibを参考にTensorFlow.jsのfacemeshにSolvePnPを用いた顔向き推定の実装の備忘録です。

前書き

調査前はVTuberが顔動かすのが当たり前の時代ですので大概のライブラリで顔向き推定は実装しているものかと思っていたのですが、どうやらそうではないようです。

それなりに高速に動くface-api.jsでは議論はあれども実装は進んでいない状況ですし、jeelizFaceFilterlive demoを見る限り速度、精度共に微妙そうです。

調べた中ではML Kitの顔認識で実装しているようですが、今回はブラウザで試してみたいのでML KitはiOS/Android用ですので検証しないことにしました。

そこで高速で精度良いfacemeshに顔向き推定を実装をするのも面白そうだと思い試しました。

実装について

Head Pose Estimation using OpenCV and Dlibを元に実装しまして、Rotation Matrix to Euler Anglesの方法でEulerAnglesを求めました。

実装して解ったのですが、SolvePnPは結構な頻度で外れ値を出します。
そのため、フレームレートを稼ぐのに難があります。
また、facemeshは3D座標を持っていますのでもっと簡単な計算で算出できそうなことに後から気づきました。
これから試す人はオンラインデモを参考にあとで後悔しないようお気をつけください。

OpenCV.jsのビルド

solvaPnP、Rodriguesの関数が必要で、OpenCV.jsをビルドしたのですが、そのままだと9.7Mありましたのでcore only buildをしまして、solvaPnP、Rodriguesを追加したうえで、2.5Mまで縮小することができました。

製作物

オンラインデモ

https://infocom-tpo.github.io/facevrm/

References

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

JavaScriptにおいてforを用いた繰り返し文の取扱説明書

繰り返し文のサンプルコード

以下、サンプルです。この仕組みを説明していきます。

num = 1;

for(let i = 0; i<10 ; i +=1){
  console.log(num + "回目だよ" )
  num += 1
}

出力結果は以下の画面です。
JavaScript-2.png

for文の解説

for(){}で括られているものは繰り返し扱いになります。
()で括っているものは計算式でしたね。どういう風に実行するかというための式が書いてあります。
{}で括っているものは実行するコードが書いてあります。
サンプルコードにはconsole.logが書いてあるためこちらが実行するものになります。
今回はletで変数iを設定しており、このiは10回まで繰り返すという表記にしています。

for(let i = 0; i<繰り返す回数; i += 1)省略

このような感じです。繰り返す回数を記載するのは上記のコードの部分です。
そしてその後に繰り返すたびに変数iに1ずつ足していくコードが書かれています。
そしてその結果をconsole.log()で出力していることになります。
console.log()って何だっけ?って思った人は以下の記事をみてください。
console.log()の取扱説明書

次は関数について記事を書いていこうと思います。

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

【初級】JavaScriptのfor文について

今回はJavaScriptのFor文について解説していきたいと思います。
似たような効果のある文にwhileなどがありますが、現在はfor文がよく使われているのでこちらを勉強していきましょう♪

? for文ってどこに使われる?

結論から言うとfor文は繰り返し処理の際に使われる書き方となります。
また、JavaScript(以降JSと記載します)には、3つのfor文があります。

1.for   ←繰り返しの回数を指定する場合に使う。
2.for in  ←オブジェクトを繰り返す時に使う。現在はあまり使われないようです。(今回は触れません)。
3.for of  ←配列の中身を出力する時に使われる。これはES6から使用OK!

まずはfor ofに焦点を当てていきたいと思います。

? for of

基本形【配列を扱う場合】

const numbers =[1,2,3]; //適当な配列

for( 引数 of 配列){
  処理(引数)
}
このようになります。
実際にnumbersの中身をコンソールに表示させて見ましょう。

for(number of numubers){
  console.log(numuber);
};
コンソール上の出力は1,2,3,とそれぞれ出力されているかと思います。
このように、引数の部分は、配列名の単数形で記入するのが良いです。

for( of )このofの部分の考え方は
one of themに似ています。
one of themは英訳するとそれらの中の一つと訳されます。
them【配列】を指定して、one【個々の値】を定義して,繰り返し順番に出力していくというイメージです。

配列のデータを全部出したいときはこのような書き方をすれば良いと思います。
オブジェクトを取り扱うときはもうひと手間かかるので、今後の記事で紹介していければと思います。

? for文

基本形

for(初期値;繰り返す条件;増減++ --); //このような形になります。
どういうことか分かりづらいので簡単にコードを書いていきたいと思います。

for(let i = 0; i<=100; i++){ 
 console.log(i);
}

for文のカッコの中を解説すると、
for(i=0に定義 ; iが100より小さい場合 ; iに+1を足していく)
日本語化するとこのような意味のコードになります。
コンソール上には、0~100までの数値が順番に出力されていると思います。

繰り返す回数を決めたい場合はfor文を使ってあげると良いかと思います。

? まとめ

  • for文は繰り返し処理の時に使う
  • for ofはES6から使えるようになった
  • for( 0f )は (one of them)と覚えると理解がしやすい。
  • 配列の中身をすべて出力したいなら for of, 繰り返す回数を決める場合はfor文を使うと良い。
  • for文は for(初期値;条件;増減)

?次回予告

次回はswitch文について書いていこうと思います!!!

今後は、Node.jsとJavaScriptの二本立てで両方更新していきたいと思います!!
Nodeも各パート5分で理解できるように基礎的な所から頑張って書いていこうと思うのでよろしくお願いいたします。

記事を見ていただきありがとうございます。
5分で理解できる内容を目標に日々執筆しております!
ご指摘等有りましたら、ドシドシ編集リクエストお待ちしておりますのでよろしくお願いいたします

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

JavaScript: Array.reduce()メソッドで配列要素を入れ替える

Array.prototype.reduce()は、応用範囲の広い配列操作のメソッドです。ただ、与えられるパラメータの数も多く、使い方がわかりにくいきらいは否めません。配列要素を入れ替える例がわかりやすそうかと思いついたので採り上げます。

数値要素の合計を求める

簡単な例からはじめます。よく使われるのが、数値の配列要素の合計です。アロー関数式=>で与えたコールバック関数の第1引数(sum)が結果の集計で、第2引数の要素(num)を順に取り出して集計にまとめます。合計の場合には、集計結果に要素の値を加えてゆくということです。

const sum = [0, 1, 2, 3, 4].reduce((sum, num) => sum + num);
console.log(sum);  // 10

要素に処理を加えた新たな配列を返す

Array.prototype.reduce()の第2引数は集計の初期値です。前項の例では省きました。すると、インデックス0の値を初期値として、インデックス1から処理がはじまるのです。でも、集計値が配列などのオブジェクトになる場合には、初期値を与えなければなりません。

数値の配列要素を、それぞれ2乗するのがつぎの例です。集計の配列(resultArray)に要素を加えるときは、Array.prototype.push()を用いるのが通常でしょう。ここではあえて、コールバック関数の第3引数(id)を使ってみました。

const array = [0, 1, 2, 3, 4].reduce((resultArray, num, id) => {
    resultArray[id] = num ** 2;
    return resultArray;
}, []);
console.log(array);  // [0, 1, 4, 9, 16]

もっとも、各配列要素への処理が加えられた同じ要素数の配列を返すなら、Array.prototype.map()メソッドの方が端的です。

const array = [0, 1, 2, 3, 4].map((num) => num ** 2);

条件に合った要素からなる配列を返す

Array.reduce()は、Array.map()と異なり、返す配列の要素数は問われません。たとえば、数値の配列から偶数値だけの配列をつくって返すこともできます。この場合、要素数が変わりますので、集計配列(resultArray)に条件に合った値を加えるのはArray.push()メソッドです。

const array = [0, 1, 2, 3, 4].reduce((resultArray, num) => {
    if (num % 2 === 0) {
        resultArray.push(num);
    }
    return resultArray;
}, []);
console.log(array);  // [0, 2, 4]

ただ、条件に合った値が収められた配列をつくるには、Array.prototype.filter()メソッドが用意されています。

const array = [0, 1, 2, 3, 4].filter((num) => num % 2 === 0);

配列要素を入れ替える

Array.reduce()の処理は一応わかっても、他の簡単なメソッドで済ませられるのならどこがいいのか、と思われるかもしれません。けれど逆に考えれば、Array.map()Array.filter()がなかったとしても、Array.reduce()で同じ処理はできます。それが、応用範囲の広いメソッドだという理由です。

とはいえ、Array.reduce()メソッドのありがたみはわかりません。思いついた例が、配列要素を入れ替える処理です。これまでのメソッドで扱おうとすれば、Array.prototype.splice()を使うことになるでしょう。その場合、要素の削除と挿入のふたつの操作が必要です。いずれも、もとの配列の対象要素からあとのインデックスがずれてしまいます。

この要素入れ替えの処理を、つぎのような関数(replaceArrayElements())として定めてみましょう。

replaceArrayElements(配列, 入れ替え先インデックス, 入れ替えもとインデックス)

Array.reduce()メソッドのコールバック関数は、第4引数にもとの配列が受け取れます。そして、メソッドの戻り値は処理を終えた新たな配列です。つまり、もと配列のインデックスは崩れません。したがって、以下のように入れ替えるインデックスの要素を、差し替えさえすればよいのです。

戻り値の配列(resultArray)には、あえてインデックスによるブラケット[]アクセスもArray.push()メソッドも用いず、スプレッド構文...で新たな配列をつくってみました。これでインデックスのずれを気にすることなく、要素の入れ替えができます。

function replaceArrayElements(array, targetId, sourceId) {
    return array.reduce((resultArray, element, id, originalArray) => [
        ...resultArray,
        id === targetId ? originalArray[sourceId] :
        id === sourceId ? originalArray[targetId] :
        element
    ], []);
}
const array = replaceArrayElements([0, 1, 2, 3, 4], 3, 1);
console.log(array);  // [0, 3, 2, 1, 4]

Array.reduce()のパラメータすべてを用いて、このメソッドがどう役立つか示せたのではないでしょうか。

もっとも、配列要素の入れ替えというお題については、分割代入を使った方がもう少し簡潔になりそうです。なお、配列はスプレッド構文...で複製しました。

function replaceArrayElements(array, targetId, sourceId) {
    const cloneArray = [...array];
    [cloneArray[targetId], cloneArray[sourceId]] = [array[sourceId], array[targetId]];
    return cloneArray;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

disabled属性のフィールドのデータ送信方法

超絶初級レベルなんでしょうが、躓いたので備忘録としてdisabled属性のフィールドのデータを送信する方法をメモメモ、、、、。

やりたかったこと&躓いたこと

複数のラジオボタンのうち、1つのラジオボタンだけ権限付けをしておいて、権限がない人は、そのラジオボタンを選択できない。
ラジオボタン以外にもテキストエリアがあって、そのテキストエリアはだれでも編集できる。
権限がある当該のラジオボタンにif文でdisabled属性を付けておいた。
しかし、初めから権限付きラジオボタンが選択されていて、権限がない人がテキストエリアを編集しようとすると、ラジオボタンの情報が送信されないので、ぬるぽ、、、。

disabled

formのフィールドにdisabled属性を付けると、無効状態になって入力できない&データの送信もできない様になる。

jsで送信直前にdisabled属性を解除

送信ボタン押下でdisabledを解除する。但しこれは、押下後画面遷移するまで時間がかかる場合、disabledだった項目が選択(入力)できるような状態になってしまうので、これじゃダメな場合もある。(解除の方法をネットで調べると.disabled=falseとか出てくるが、私はそれではできなかった。)

<input type="radio"   name="radio" id="radio" value="送信内容" disabled="disabled" /> 
<input type="submit" value="送信" onclick="javascript:unDisabled();" />
<script type="text/javascript">
            function unDisabled()
            {
                $('.radio').removeAttr("disabled");

            }
        </script>

送信直前にdisabledの値をhiddenにコピー

ただし、今回のように権限付けしてあってdisabledが動的に決まるものだと、同じ内容のデータを2度送信してしまう可能性がある。これの回避は結構めんどくさそう。(いい方法があるのか分らんが、、、)

<script type="text/javascript">
            function text_copy()
            {
                document.formMain.text2.value = document.formMain.text1.value ;
                return true;
            }
        </script>
<input type="hidden"   name="text2" id="hidden_text" value="" />
            <input type="radio"   name="radio" id="radio" value="送信内容" disabled="disabled" />
            <input type="submit" value="送信" onclick="text_copy();" />

readonlyにしてcssで選択不可にする

https://freelance-jak.com/technology/javascript/553/

こういう方法もあるらしい。

参考

https://lightgauge.net/language/html/2525/

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