20191011のNode.jsに関する記事は7件です。

Vue + SVG で作るグラフィックアプリの骨格

はじめに

この記事はもう一つの記事、
Vue + Canvas で作るグラフィックアプリの骨格
の SVG 版です。

Canvas や SVG のおかげでブラウザ上でもインタラクティブにグラフィックを簡単に扱えるようになりました。

おまけに Vue を使うとまるでパソコン上のアプリを作るかのように Web 上にグラフィックアプリを作ることができます。

この記事では Vue + SVG でマウスで四角を書いていくような 80 行程度の簡単なサンプルを作ることで、Web 上のグラフィックアプリの骨格を示したいと思います。

できあがったものは、以下のような感じです。
ss.gif

準備

  • node, npm をダウンロードします

https://nodejs.org
この記事では node v12.4.0 で検証しています

  • vue-cli をダウンロードして、とりあえず qvs という名前でアプリを作ります。
$ npm i @vue/cli -g
$ vue create qvs
$ cd qvs
$ yarn serve

この記事では vue 3.12.0 で検証しています。yarn を使ってない人は最後の行は

$ npm run serve

になると思います。

なお、以下のコードは ESLint の掟に従ってないので、vue create qvs の時に ESLint をはずさないと多分エラーになると思われます。
また、セパレータを前に書いたり、引数とか変数の名前が _ だったりするのは単なる趣味ですので、気に入らなくても怒らないでください。

コード

src/App.vue を以下のようにしてみます。

App.vue
<template>
    <div id=app style="overflow: scroll">
        <div id=menu style="position: fixed; top: 0; left: 0">
            <button @click="mode='select'"  >select </button>
            <button @click="mode='rect'"    >rect   </button>
        </div>
        <svg
            style       = "margin: 24px 4px 4px 4px; background: aliceblue"
            :class      = "{ drawRect: mode == 'rect' }"
            :width      = "extent[ 0 ]"
            :height     = "extent[ 1 ]"
            @mousedown  = "mouseDown"
            @mousemove  = "mouseMove"
            @mouseup    = "mouseUp"
            @keyup.esc  = "keyUpESC"
        >
            <template v-for="_ in elements">
                <rect v-bind="_" stroke=black fill=none />
            </template>

            <rect v-bind=dragRect stroke=blue fill=none />
        </svg>
    </div>
</template>

<script>
export default {
    data    : () => (
        {   b           : null
        ,   c           : null
        ,   mode        : 'select'
        ,   elements    : []
        }
    )
,   computed: {
        extent  () {
            return [ 700, 500 ]
        }
    ,   svg     () {
            return this.$el.getElementsByTagName( 'svg' )[ 0 ]
        }
    ,   dragRect() {
            return ( ! this.b || ! this.c )
            ?   null
            :   {   x       : this.b.offsetX
                ,   y       : this.b.offsetY
                ,   width   : this.c.offsetX - this.b.offsetX
                ,   height  : this.c.offsetY - this.b.offsetY
                }
        }
    }
,   methods : {
        mouseDown( _ ) {
            this.b = _
        }
    ,   mouseMove( _ ) {
            this.c = _
        }
    ,   mouseUp( _ ) {
            this.c = _
            switch ( this.mode ) {
            case 'rect':
                this.elements.push( this.dragRect )
                break
            }
            this.b = null
        }
    ,   keyUpESC( _ ) {
            this.mode = 'select'
        }
    }
,   mounted() {
        this.svg.setAttribute( 'tabindex', 0 )
    }
}
</script>

<style>
.drawRect {
    cursor: crosshair
}
</style>

勘所

キーイベントの取得

svg に tabindex 属性をつけると、キーイベントを取得できるようになります。

    this.svg.setAttribute( 'tabindex', 0 )

カーソルの切り替え

mode が 'select' と 'rect' の2つの状態が存在します。
'rect' の状態の時にクロスヘアカーソルを表示するために
クラスとスタイルのバインディングを動的に使っています。

    :class = "{ drawRect: mode == 'rect' }"


<style>
.drawRect {
    cursor: crosshair
}
</style>

v-bind でプロパティを一括設定

プロパティ名と同じキーを持つ辞書を用意して

dragRect = {
    x       : 100
,   y       : 200
,   width   : 300
,   height  : 400
}
<rect v-bind=dragRect />

とやると、

<rect x=100 y=200 width=300 height=400 />

と展開されます。

最後に

この記事が何かの役にたてたら幸いです。わからないことがあればコメントください!

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

ejsのテンプレートに文字列の配列(?)を渡すと「"」(ダブルクォーテーション)が削られてエラーになる対応方法

本内容を利用した場合の一切の責任を私は負いません。

バージョン

  • OS
    OS 名: Microsoft Windows 10 Home
    OS バージョン: 10.0.18362 N/A ビルド 18362
    システムの種類: x64-based PC
  • Chrome
    バージョン: 77.0.3865.90(Official Build) (64 ビット)
  • node.js
    node-v10.16.0-win-x64
    ejs@2.6.2
    express@4.17.1

本題

ejsが古いためかもしれないが、文字列の配列(?)をテンプレートに渡すと、文字列の「"」(ダブルクォーテーション)括りが削られてエラーになってしまう。
その回避方法。

server.js
const express = require('express');
const ejs = require('ejs');

const App = express();

App.engine('ejs', ejs.renderFile);
App.get('/',
    function(req, res)
    {
        res.render('client.ejs',
            {
                ToTemplate: ["ab", "cd", "ef"]
            });
    });
App.listen(8080);
views/client.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>double quotation into array by ejs</title>
<script id="script_1">
var no_good_1 = <%= ToTemplate %>;
var no_good_2 = [<%= ToTemplate %>];
<%
var no_good_3 = [];
var good = [];

for (var i = 0; i < ToTemplate.length; i++)
{
    no_good_3.push(ToTemplate[i].toString());
    good.push("\"" + ToTemplate[i].toString() + "\"");
}
%>
var no_good_3 = [<%- no_good_3 %>];
var good = [<%- good %>];
</script>
</head>
<body>
&lt;script id="script_1"&gt;」タグ内<br>
<br>
<script>
var content = document.getElementById("script_1");
document.write(content.text);
</script>
</body>
</html>

上記の結果。

out.htm
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>double quotation into array by ejs</title>
<script id="script_1">
var no_good_1 = ab,cd,ef;
var no_good_2 = [ab,cd,ef];

var no_good_3 = [ab,cd,ef];
var good = ["ab","cd","ef"];
</script>
</head>
<body>&lt;script id="script_1"&gt;」タグ内<br>
<br>
<script>
var content = document.getElementById("script_1");
document.write(content.text);
</script>
</body>
</html>
display.txt
「<script id="script_1">」タグ内

var no_good_1 = ab,cd,ef; var no_good_2 = [ab,cd,ef]; var no_good_3 = [ab,cd,ef]; var good = ["ab","cd","ef"];

上記は下記のgithubに。
https://github.com/github895439/other_qiita/tree/master/double_quotation_into_array_by_ejs

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

Vue + Canvas で作るグラフィックアプリの骨格

はじめに

この記事はもう一つの記事、
Vue + SVG で作るグラフィックアプリの骨格
の Canvas 版です。

Canvas や SVG のおかげでブラウザ上でもインタラクティブにグラフィックを簡単に扱えるようになりました。

おまけに Vue を使うとまるでパソコン上のアプリを作るかのように Web 上にグラフィックアプリを作ることができます。

この記事では Vue + Canvas でマウスで四角を書いていくような 100 行程度の簡単なサンプルを作ることで、Web 上のグラフィックアプリの骨格を示したいと思います。

できあがったものは、以下のような感じです。
ss.gif

準備

  • node, npm をダウンロードします

https://nodejs.org
この記事では node v12.4.0 で検証しています

  • vue-cli をダウンロードして、とりあえず qvc という名前でアプリを作ります。
$ npm i @vue/cli -g
$ vue create qvc
$ cd qvc
$ yarn serve

この記事では vue 3.12.0 で検証しています。yarn を使ってない人は最後の行は

$ npm run serve

になると思います。

なお、以下のコードは ESLint の掟に従ってないので、vue create qvc の時に ESLint をはずさないと多分エラーになると思われます。
また、セパレータを前に書いたり、引数とか変数の名前が _ だったりするのは単なる趣味ですので、気に入らなくても怒らないでください。

コード

src/App.vue を以下のようにしてみます。

App.vue
<template>
    <div id=app style="overflow: scroll">
        <div id=menu style="position: fixed; top: 0; left: 0">
            <button @click="mode='select'"  >select </button>
            <button @click="mode='rect'"    >rect   </button>
        </div>
        <canvas
            style       = "margin: 24px 4px 4px 4px; background: aliceblue"
            :class      = "{ drawRect: mode == 'rect' }"
            :width      = "extent[ 0 ]"
            :height     = "extent[ 1 ]"
            @mousedown  = "mouseDown"
            @mousemove  = "mouseMove"
            @mouseup    = "mouseUp"
            @keyup.esc  = "keyUpESC"
        />
    </div>
</template>

<script>
export default {
    data    : () => (
        {   b           : null
        ,   c           : null
        ,   mode        : 'select'
        ,   elements    : []
        }
    )
,   computed: {
        extent  () {
            return [ 700, 500 ]
        }
    ,   canvas  () {
            return this.$el.getElementsByTagName( 'canvas' )[ 0 ]
        }
    ,   ctx     () {
            return this.canvas.getContext( '2d' )
        }
    ,   dragRect() {
            return ( ! this.b || ! this.c )
            ?   null
            :   [   this.b.offsetX
                ,   this.b.offsetY
                ,   this.c.offsetX - this.b.offsetX
                ,   this.c.offsetY - this.b.offsetY
                ]
        }
    }
,   methods : {
        draw() {
            this.ctx.clearRect( 0, 0, ...this.extent )

            for ( let _ of this.elements ) this.ctx.strokeRect( ..._ )

            if ( ! this.dragRect ) return
            switch ( this.mode ) {
            case 'rect':
                this.ctx.setLineDash( [ 1 ] )
                this.ctx.strokeRect( ...this.dragRect )
                this.ctx.setLineDash( [] )
                break
            }
        }
    ,   mouseDown( _ ) {
            this.b = _
            this.draw()
        }
    ,   mouseMove( _ ) {
            this.c = _
            this.draw()
        }
    ,   mouseUp( _ ) {
            this.c = _
            switch ( this.mode ) {
            case 'rect':
                this.elements.push( this.dragRect )
                break
            }
            this.b = null
            this.draw()
        }
    ,   keyUpESC( _ ) {
            this.mode = 'select'
            this.draw()
        }
    }
,   mounted() {
        this.canvas.setAttribute( 'tabindex', 0 )
        this.draw()
    }
}
</script>

<style>
.drawRect {
    cursor: crosshair
}
</style>

勘所

キーイベントの取得

canvas に tabindex 属性をつけると、キーイベントを取得できるようになります。

    this.canvas.setAttribute( 'tabindex', 0 )

カーソルの切り替え

mode が 'select' と 'rect' の2つの状態が存在します。
'rect' の状態の時にクロスヘアカーソルを表示するために
クラスとスタイルのバインディングを動的に使っています。

        :class = "{ drawRect: mode == 'rect' }"
<style>
.drawRect {
    cursor: crosshair
}
</style>

最後に

この記事が何かの役にたてたら幸いです。わからないことがあればコメントください!

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

ESLint 7.0.0 の開発が始まる

現在、実施される Breaking Changes について議論中です。Node.js 8.x が EOL になる頃にアルファ版が出始めると予想しています。

実施される可能性が高い Breaking Changes をちょっとだけ覗いてみましょう。

  • RFC20『設定ファイルによる追加の対象ファイル』 ディレクトリを指定してリントした場合、現在は*.jsファイルだけが対象になります。7.0.0 からは、overridesセクションのファイル指定にマッチするファイルも自動的に対象になります。TypeScript や Vue.js など、.js 以外のファイルを利用している場合に便利になります。
  • RFC32『個人設定ファイルを非推奨にする』 ESLint は、CWD 配下に ESLint の設定ファイルが存在しなかった場合、自動的にホームディレクトリの設定ファイルを探して利用します。 7.0.0 ではこの動作が非推奨になります。8.0.0 で削除されると思います。
  • RFC33『ディレクティブ コメントのコメント』 /* eslint-disable no-undef -- typeofで調べたから */ のように、各ディレクティブ コメントに説明を付与できるようになります。反面、-- が設定の中にあるとうまく動かなくなるかもしれません。
  • RFC34『単一行ディレクティブ コメント』 /* eslint-disable */ のようなブロックコメントのみサポートしているディレクティブ コメントが、// eslint-disable のように行コメントでも利用可能になります。ディレクティブ コメントと同じ単語で始まる行コメントが誤動作するかもしれません。
  • RFC37overrides/ignorePatternsの基準パスを変更』 --config/--ignore-path オプションで指定した設定ファイルの overrides/ignorePatterns プロパティの基準パスが CWD に変更されます。検証スクリプト自体をパッケージ化して再利用している場合などは、新しい振る舞いで便利になります。
  • RFC39『プラグインの読込元を変更』 ESLint 5.x までは ESLint がインストールされている場所を基準にプラグインを読み込んでいました。6.x では CWD を基準にプラグインを読み込んでいます。7.0.0 からはリント対象ファイルに最も近い設定ファイルを基準にプラグインを読み込むようになります。モノリポのように、node_modules が複数存在するようなリポジトリを利用している場合に便利になります。
  • RFC40『非同期 API に移行する』 ESLint を Node.js から利用する場合は CLIEngine 等のクラスを利用します。これらがすべて同期 API を使っているため、できないことがたくさんありました。例えば進捗表示、並列化、そして ES Modules の利用等です。7.0.0 では新たに非同期 API を提供し、今の同期 API は非推奨になります。
  • RFC42『並列化』 リント対象のファイル数に応じて、ファイルが多ければスレッドを作って並列処理するようになります。スレッドを使えない古い Node.js で実行されている場合は、代わりに子プロセスで並列化します。PoC では、20 秒程度かかっていた ESLint 自身のソースコードのリントが、7.5 秒程度で終わるようになりました。
  • RFC44『Node.js 8.x/11.x のサポート終了』 タイトル通り。新しいサポート範囲は ^10.12.0 || >=12.0.0 になりそう?

※ これらが本当に実施される保証はありません。

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

Node.js: http.IncommingMessageをHTTP形式の文字列に変換する関数

Node.jsのhttp.IncommingMessageをHTTP形式の文字列に変換するデバッグ用関数を作ったので紹介します。

どんな関数かというと、次のように第一引数にhttp.IncommingMessageオブジェクトを渡すと:

textifyRequest(req)

下記のような文字列をPromiseで返す関数です:

POST /foobar HTTP/1.1
Host: localhost:9000
Connection: close
Transfer-Encoding: chunked

hello

http.IncommingMessageをHTTP形式の文字列に変換する関数

const textifyRequest = (req: IncomingMessage): Promise<string> =>
  new Promise(resolve => {
    let text = `${req.method} ${req.url} HTTP/${req.httpVersion}\n`

    // serialize headers
    let nextKey = undefined as string | undefined
    for (let keyOrValue of req.rawHeaders) {
      if (nextKey === undefined) {
        nextKey = keyOrValue
      } else {
        text += `${nextKey}: ${keyOrValue}\n`
        nextKey = undefined
      }
    }
    text += '\n'

    // serialize body
    req
      .on('data', chunk => text += chunk)
      .on('readable', () => req.read())
      .on('end', () => resolve(text))
  })

使用例

// HTTPサーバ
http.createServer((req: IncomingMessage, res: ServerResponse): void => {
  textifyRequest(req).then(console.log) // リクエストを受け取ったところに仕込む
  res.end()
}).listen(9000, () => {
  // 上で立てたHTTPサーバにリクエストを送ってみる
  const req = http.request({ host: 'localhost', port: 9000, method: 'POST', path: '/foobar' })
  req.write('hello')
  req.end()
})

実行結果

POST /foobar HTTP/1.1
Host: localhost:9000
Connection: close
Transfer-Encoding: chunked

hello

注意点

HTTPボディを文字列バッファに積んで返すので、データサイズの大きいリクエストには不向きな実装になっています。

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

Node.js: stream.Readableのデータの前後にデータを追加する方法

Node.jsのstream.Readableのデータの前後にデータを追加する方法を紹介する。

const { Readable } = require('stream')

// 元データのストリーム
const source = Readable.from(['source content\n'])

// 元データの前後にデータを追加する処理(ジェネレーターを使う)
const wrap = (stream) => (async function *() {
    yield 'prepended content\n'
    yield* stream[Symbol.asyncIterator]()
    yield 'appended content\n'
})()

// データが追加されたストリーム
const wrappedContent = Readable.from(wrap(source))
wrappedContent.pipe(process.stdout)

実行結果

prepended content
source content
appended content

stream.Readableには当然ながらwriteメソッドが生えていないので、Readableオブジェクトを初期化する段階で加工済みのデータを渡してやる必要がある。

しかし、ストリームの内容をすべてバッファに入れると、メモリーを消費したりと、ストリーム処理ではなくなっていまうので、ジェネレーターを使う。

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

Node.js: stream.Readableを作るシンプルな方法

本稿では、どのようにしてNode.jsのstream.Readableを作ったらいいかを示す。

stream.Readableを作るサンプルコード

const { Readable } = require('stream')
const stream = Readable.from(['hello'])
stream.pipe(process.stdout)

実行結果

hello

説明

Readable.fromメソッドはReadableオブジェクトを生成するユーティリティメソッドで、第一引数にIterable<any>またはAsyncIterable<any>を取る。

上のサンプルコードではArray<string>を渡しているが、Iterable反復処理プロトコルを実装しているオブジェクトなら何でもいいので、配列の他にジェネレーターを渡すこともできる:

const gen = function *() {
    yield 'hello'
    yield 'world'
}
const stream = Readable.from(gen())
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む