LoginSignup
3
2

More than 1 year has passed since last update.

Nuxt.jsで文字画像ジェネレータをつくる

Last updated at Posted at 2021-05-31

Nuxt.js と Konva.js を使って、入力したテキストをリアルタイムに描画してくれる文字画像ジェネレータを作ります。※モバイル未対応

generator.gif

GitHub のリポジトリとデモサイトはこちら。
Repo: https://github.com/HarukiKinoshita/nuxt-text-image-generator
Demo: https://nuxt-text-image-generator.vercel.app

構成

  • nuxt: 2.0.0
  • nuxt-buefy: 0.3.2
  • konva: 7.2.5

Vue.js / Nuxt.jsとは

Vue.js は Javascript フレームワークの1つで、Nuxt.js は Vue.js をベースに様々な機能を盛り込んだフレームワークです。

Vue は リアクティブ なシステムと呼ばれ、コンポーネントが保持している値が更新されると、ビューが更新されます。また 双方向データバインディング といって、DOM 要素の値と Vue インスタンスの値を連動させてくれます。

このジェネレータでは、これらの特徴を活かして、フォームにテキストを入力すると即座に変数の値を書き換えて Konva に渡し、canvas 要素に反映して描画結果を更新することができます。

Konva.js とは

Konva.js は、HTML5 の canvas 要素を操作してくれる Javascript ライブラリです。今回は Vue のリアクティブとバインディングに対応した Vue Konva を利用します。

1. インストール

まずは Nuxt.js プロジェクトを作成します。

terminal
// yarn
yarn create nuxt-app <project-name>

// npx
npx create-nuxt-app <project-name>

// npm
npm init nuxt-app <project-name>

なおサンプルコードでは UI フレームワークに Buefy を使っています。create-nuxt-app の途中で Buefy を選択すればインストールされます。

続けて、下記コマンドを実行してvue-konvaとkonvaをインストールします。

terminal
// yarn
yarn add vue-konva konva --save

// npm
npm install vue-konva konva --save

2. pluginsに追加

plugins 配下に下記のような js ファイルを作成します。ここではファイル名を vue-konva.js としてグローバルで登録しておきます。

~/plugins/vue-konva.js
import Vue from 'vue'
import VueKonva from 'vue-konva'

Vue.use(VueKonva)

3. nuxt.config.jsにパスを追加

nuxt.config.js
plugins: [
  {
    src: '~/plugins/vue-konva',
    mode: 'client'
  }
],

4. 入力フォームをつくる

まずは UI から実装していきます。

まずはじめに index.vuedefault.vue などに Buefy のデフォルトのテンプレートが適用されているので、これらを一掃しておきます (しなくても特に問題はありませんが) 。

ここでは、ユーザがテキストを入力するフォームと、文字の大きさを調節する スライダー を実装します。<b-field><b-input> は Buefy 独自のタグで、HTMLの <form><input> だと思ってもらえれば問題ありません。

~/pages/index.vue
<template>
  <section class="section container">
    <h1 class="title">Text Image Generator</h1>

    <!-- 入力フォーム -->
    <b-field
      label="テキスト"
      position="is-centered"
    >
      <b-input
        placeholder="Type Something"
        type="text"
        v-model="inputText"
      />
    </b-field>

    <!-- 文字サイズを調節するスライダー -->
    <b-field label="Font Size">
      <b-slider v-model="fontsize" :min="24" :max="48" :step="4" ticks rounded>
      </b-slider>
    </b-field>

    <!--
       描画領域をここに実装
    -->

  </section>  
</template>

v-model="inputText" で、フォームに入力された文字列を、後ほど定義する変数 inputText にバインディングしています。

5. 描画領域をつくる

続いて Konva が描画する領域を作成します。

Konva は、ステージ・レイヤー・シェイプ (画像やテキストなど) という3つの階層で構成されており、Vue Konva ではそれぞれ v-stage, v-layer, v-image (画像) v-text (テキスト) というタグに対応しています。Vuetifyを使っていると紛らわしいですね。

ここでは、背景画像を設定する v-image と、ユーザがフォームに入力した文字列を設定する v-text を使って、下記のようなシンプルな構成にしてみます。

v-stage ref="stage"
└── v-layer ref="layer"
    ├── v-image
    └── v-text
~/pages/index.vue
<template>
  <section class="section>

  <!-- 省略 -->

  <div id="konvaCanvas" style="margin: auto;">

    <!-- ステージ -->
    <v-stage ref="stage"
    :config="{
      width: 600,
      height: 315,
    }">

      <!-- レイヤー -->
      <v-layer ref="layer">

        <!-- 画像 -->
        <v-image :config="{
          image: bgImage,
          scaleX: 0.5,
          scaleY: 0.5
        }"/>

        <!-- テキスト -->
        <v-text
        :config="{
          text: `${this.inputText}`, 
          align: 'center',
          verticalAlign: 'middle',
          offsetY: 20,
          fill: '#b0bec5',
          fontFamily: 'Helvetica',
          fontSize: `${fontsize}`,
          fontStyle: 'bold',
          lineHeight: 1.4,
          padding: 50,
          width: 600,
          height: 315,
        }"/>
      </v-layer>
    </v-stage>
  </div>
  <b-button @click="save" class="button is-primary" icon-left="download">
    画像を保存
  </b-button>
</section>

ユーザがフォームとスライダーで指定した値が inputTextfontSize にバインディングされます。

align: 'center'verticalAlign: 'middle' でテキストを縦横中央揃えにします。さらにその位置から offsetY: 50縦方向50px だけ 上に ずらしています。これらの基準となるのは v-text に指定した widthheight の値であり、ステージの幅・高さではないので注意が必要です。

v-text には他にも様々なオプションを指定できます。
Konva Class: Text

6. scriptを書く

背景画像を読み込む部分と、描画されたステージを画像として保存する部分を実装します。
ここでは背景画像として 1200x630 の画像を使用します。ここからダウンロードして、staticフォルダの直下に保存してください。

なお、このままのサイズでブラウザ上に描画すると大き過ぎて、画面が小さいと見切れてしまいます。レスポンシブにするのがベストですが、ここでは単純のために Konva の scale プロパティを使って拡大縮小することにします。ステージのサイズを 600x315 に設定し、画像を0.5倍にして読み込み、書き出し時にステージを2倍にします。

~/pages/index.vue
<script>
export default {
  data() {
    return {
      bgImage: '',
      inputText: '',
      fontSize: 36
    }
  },
  created () {
    if (process.client) {
      const image = new window.Image();
      image.src = "background.png";
      image.onload = () => {
        this.bgImage = image;
      }
    }
  },
  methods: {
    save () {
      // v-stage ref="stage" の中身を JSON オブジェクトとして取得する
      const stage = this.$refs.stage.getStage();

      // ダウンロード
      var link = document.createElement('a');
      link.download = 'image_' + this.inputText.slice(0,10).replace(/ /g, "_");
      link.href = stage.toDataURL({
        // ステージを2倍に拡大する
        pixelRatio: 2
      })
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    }
  },
}
</script>

まず v-textconfig などで利用する変数を data の中で定義しておきます。

次に created の中で、DOM が構成される前に背景画像を読み込みます。window を利用するので、クライアントサイドのみで動くように if (process.client) を入れています。

画像の保存に関しては、<b-button @click="save">save() が呼び出されます。Vue Konva の公式ドキュメント に載っている方法を試したところうまくいかなかった(そもそもデモが動いていない)ので、 React Konva の公式ドキュメント に載っている方法を利用しました。後半は無理やり感が否めないですが、このやり方は MDN にも載ってたりします...

7. 完成

ここまで仕上がったら、ローカルサーバーを起動して localhost:3000 にアクセスしてみてください。

terminal
// yarn
yarn dev

// npm
npm run dev

下記のような画面が表示されれば、完成です!
スクリーンショット 2021-05-31 23.40.16.png

オリジナルのフォントを設定 したり、背景画像に合わせて v-text の位置をうまく調節したりすれば、クソコラ画像ジェネレータが作れそうですね(作れるとは言っていない)。他にも、ソースコードのスクリーンショット風画像を生成してくれる Carbon みたいなのも作れそうです。

8. 補足

iOS や Android では上記の方法+静的ホスティングだと画像をローカル (カメラロール) に保存できません...

9. 参考

公式ドキュメント
- Getting started with vue and canvas via Konva
- How to save a drawing from react-konva?

3
2
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
3
2