Nuxt.js と Konva.js を使って、入力したテキストをリアルタイムに描画してくれる文字画像ジェネレータを作ります。※モバイル未対応
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 プロジェクトを作成します。
// 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をインストールします。
// yarn
yarn add vue-konva konva --save
// npm
npm install vue-konva konva --save
#2. pluginsに追加
plugins
配下に下記のような js ファイルを作成します。ここではファイル名を vue-konva.js
としてグローバルで登録しておきます。
import Vue from 'vue'
import VueKonva from 'vue-konva'
Vue.use(VueKonva)
#3. nuxt.config.jsにパスを追加
plugins: [
{
src: '~/plugins/vue-konva',
mode: 'client'
}
],
#4. 入力フォームをつくる
まずは UI から実装していきます。
まずはじめに index.vue
や default.vue
などに Buefy のデフォルトのテンプレートが適用されているので、これらを一掃しておきます (しなくても特に問題はありませんが) 。
ここでは、ユーザがテキストを入力するフォームと、文字の大きさを調節する スライダー を実装します。<b-field>
や <b-input>
は Buefy 独自のタグで、HTMLの <form>
や <input>
だと思ってもらえれば問題ありません。
<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
<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>
ユーザがフォームとスライダーで指定した値が inputText
と fontSize
にバインディングされます。
align: 'center'
と verticalAlign: 'middle'
でテキストを縦横中央揃えにします。さらにその位置から offsetY: 50
で 縦方向 に 50px だけ 上に ずらしています。これらの基準となるのは v-text
に指定した width
と height
の値であり、ステージの幅・高さではないので注意が必要です。
v-text
には他にも様々なオプションを指定できます。
Konva Class: Text
#6. scriptを書く
背景画像を読み込む部分と、描画されたステージを画像として保存する部分を実装します。
ここでは背景画像として 1200x630 の画像を使用します。ここからダウンロードして、staticフォルダの直下に保存してください。
なお、このままのサイズでブラウザ上に描画すると大き過ぎて、画面が小さいと見切れてしまいます。レスポンシブにするのがベストですが、ここでは単純のために Konva の scale プロパティを使って拡大縮小することにします。ステージのサイズを 600x315 に設定し、画像を0.5倍にして読み込み、書き出し時にステージを2倍にします。
<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-text
の config
などで利用する変数を data
の中で定義しておきます。
次に created
の中で、DOM が構成される前に背景画像を読み込みます。window
を利用するので、クライアントサイドのみで動くように if (process.client)
を入れています。
画像の保存に関しては、<b-button @click="save">
で save()
が呼び出されます。Vue Konva の公式ドキュメント に載っている方法を試したところうまくいかなかった(そもそもデモが動いていない)ので、 React Konva の公式ドキュメント に載っている方法を利用しました。後半は無理やり感が否めないですが、このやり方は MDN にも載ってたりします...
#7. 完成
ここまで仕上がったら、ローカルサーバーを起動して localhost:3000
にアクセスしてみてください。
// yarn
yarn dev
// npm
npm run dev
オリジナルのフォントを設定 したり、背景画像に合わせて v-text
の位置をうまく調節したりすれば、クソコラ画像ジェネレータが作れそうですね(作れるとは言っていない)。他にも、ソースコードのスクリーンショット風画像を生成してくれる Carbon みたいなのも作れそうです。
#8. 補足
iOS や Android では上記の方法+静的ホスティングだと画像をローカル (カメラロール) に保存できません...
#9. 参考
公式ドキュメント