LoginSignup
7
6

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-10-11

はじめに

この記事はもう一つの記事、
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>

最後に

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

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6