- 投稿日:2019-10-11T21:27:24+09:00
Vue + SVG で作るグラフィックアプリの骨格
はじめに
この記事はもう一つの記事、
Vue + Canvas で作るグラフィックアプリの骨格
の SVG 版です。Canvas や SVG のおかげでブラウザ上でもインタラクティブにグラフィックを簡単に扱えるようになりました。
おまけに Vue を使うとまるでパソコン上のアプリを作るかのように Web 上にグラフィックアプリを作ることができます。
この記事では Vue + SVG でマウスで四角を書いていくような 80 行程度の簡単なサンプルを作ることで、Web 上のグラフィックアプリの骨格を示したいと思います。
準備
- 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 />と展開されます。
最後に
この記事が何かの役にたてたら幸いです。わからないことがあればコメントください!
- 投稿日:2019-10-11T20:43:49+09:00
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.jsconst 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> 「<script id="script_1">」タグ内<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> 「<script id="script_1">」タグ内<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
- 投稿日:2019-10-11T18:34:06+09:00
Vue + Canvas で作るグラフィックアプリの骨格
はじめに
この記事はもう一つの記事、
Vue + SVG で作るグラフィックアプリの骨格
の Canvas 版です。Canvas や SVG のおかげでブラウザ上でもインタラクティブにグラフィックを簡単に扱えるようになりました。
おまけに Vue を使うとまるでパソコン上のアプリを作るかのように Web 上にグラフィックアプリを作ることができます。
この記事では Vue + Canvas でマウスで四角を書いていくような 100 行程度の簡単なサンプルを作ることで、Web 上のグラフィックアプリの骨格を示したいと思います。
準備
- 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>最後に
この記事が何かの役にたてたら幸いです。わからないことがあればコメントください!
- 投稿日:2019-10-11T16:48:47+09:00
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
のように行コメントでも利用可能になります。ディレクティブ コメントと同じ単語で始まる行コメントが誤動作するかもしれません。- RFC37『
overrides
/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
になりそう?
※ これらが本当に実施される保証はありません。
- 投稿日:2019-10-11T13:14:54+09:00
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ボディを文字列バッファに積んで返すので、データサイズの大きいリクエストには不向きな実装になっています。
- 投稿日:2019-10-11T11:45:08+09:00
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 contentstream.Readableには当然ながらwriteメソッドが生えていないので、Readableオブジェクトを初期化する段階で加工済みのデータを渡してやる必要がある。
しかし、ストリームの内容をすべてバッファに入れると、メモリーを消費したりと、ストリーム処理ではなくなっていまうので、ジェネレーターを使う。
- 投稿日:2019-10-11T11:28:16+09:00
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())