🦞

Vugel使ってみた

2023/11/07に公開

Nuxt3環境だと,Fabric.jsが``DOMParser is not defined''で動かず,Nuxt3でFabric.jsを導入している記事のソースをコピペしても動かない,つまり環境依存があるということで,そういった手法を取らなくても済むcanvas生成プラグインはないのか→ありました。それがVugelです。

導入 要Vue3が使える環境

yarn add vugel 
# OR npm install vugel

Vugelここがすごい templateで要素を配置して描画

通常のcanvasでは,<script>内においてcanvasをidで取得し,描画コンテキストを取得して書き始めるらしいです。

以下公式Docsより

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <script type="application/javascript">
      function draw() {
        var canvas = document.getElementById("canvas");
        if (canvas.getContext) {
          var ctx = canvas.getContext("2d");

          ctx.fillStyle = "rgb(200, 0, 0)";
          ctx.fillRect(10, 10, 50, 50);

          ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
          ctx.fillRect(30, 30, 50, 50);
        }
      }
    </script>
  </head>
  <body onload="draw();">
    <canvas id="canvas" width="150" height="150"></canvas>
  </body>
</html>

一方Vugelの場合

app.vue
<script setup>
import { Vugel } from "vugel";
</script>
<template>
    <vugel :settings="{clearColor: 'black'}" style="width: 500px; height: 500px">
        <my-vugel-root-component example-prop="hello world" />
    </vugel>
</template>
MyVugelRootComponent.vue
<template compiler="vugel">
    <container>
        <rectangle :x="10" :y="10" :w="width" :h="100" color="#ff0000" />
        <text :x="10" :y="140" :font-size="10" :font-weight="600">{{ exampleProp }}</text>
    </container>
</template>

<vugel>を要素を置きたい部分に設置し,<template compiler="vugel">要素を持つコンポーネントをネストするだけで動きます。
HTMLを作る要領で要素を配置できることで,より直感的な開発が出来ると思います。

使用例

vercelにデプロイしたURL
ツールとしては未完成(各種ゲームの機能の追加,画像として出力する機能)ですがVugel周りは出来てます。

StartPositionCanvas.vue
<script setup lang="ts">
import { VugelMouseEvent } from 'vugel';

const props = defineProps({
  deck: Array as () => Card[]
})

const dragTarget: Ref<(Node | null)[]> = ref([])
let startEvent: VugelMouseEvent | null = null;
let startX: number[] = [];

let startY: number[] = [];
interface Card {
  img: string;
}

const start = (e: VugelMouseEvent) => {
  props.deck.forEach((card: Card, index) => {
    if (card.img === e.target.tex.source.lookupId && dragTarget.value) {
      startEvent = e;
      currentCardIndex.value = index
      // As Vugel doesn't expose the properties, we refer directly to the element.
      startX[index] = dragTarget.value[index].el.x as number;
      startY[index] = dragTarget.value[index].el.y as number;
    }
  })

};

const move = (e: VugelMouseEvent,) => {
  props.deck.forEach((card, index) => {
    if (card.img === e.target.tex.source.lookupId && startEvent) {
      const deltaX = e.canvasOffsetX - startEvent.canvasOffsetX;
      const deltaY = e.canvasOffsetY - startEvent.canvasOffsetY;
      dragTarget.value[index]!.x = startX[index] + deltaX;
      dragTarget.value[index]!.y = startY[index] + deltaY;
      // Do not scroll on mobile.
      e.originalEvent.preventDefault();
    }
  })
};

const end = (e: VugelMouseEvent) => {
  startEvent = null;
  console.log(dragTarget.value[currentCardIndex.value].x)
};

start, move, end

const radian = ref([0, 0, 0, 0, 0, 0, 0, 0])
const currentCardIndex = ref()
function rotateRight45Degree() {
  radian.value[currentCardIndex.value] += 0.7853981633974483
}
function rotateLeft45Degree() {
  radian.value[currentCardIndex.value] -= 0.7853981633974483
}

</script>
<template compiler="vugel">
  <picture :w="40" :h="40" :x="600" src="/arrow-down-left-bold.png" @mousedown="rotateLeft45Degree"></picture>
  <picture :w="40" :h="40" :x="650" src="/arrow-down-right-bold.png" @mousedown="rotateRight45Degree"></picture>
  <rectangle :h="10" :x="0" :y="100" :w="768" color="rgb(0, 255, 0)" />
  <container :flex="true">
    <picture v-for="(card, index) in deck" :ref="el => dragTarget[index] = el" :src="card.img" :rotation="radian[index]"
      :w="80" :h="128" :pivot="0.5" @mousedown="start" @mousemove="move" @mouseup="end" @mouseleave="end" />
  </container>
</template>

カードをドラッグする動きは公式のExampleソースコードを大体流用していますが,変更点として,

  • dragTargetは配列にして複数枚のカードを取り扱えるようにしている
  • カードのURLとe.target.tex.source.lookupIdが一致している時に処理を実行することで,指定のカードが動かされるようにしている

と言ったことをしています。他には,

  • 各カード毎に:rotation="radian[index]"を適用することで,カードを回転して設置する挙動を作っている
  • カードを<container :flex="true">内に置くことで,カードを追加した際に重ならずに,横並びに置いてくれるようにしている

と言った工夫をしています。

あとがき

  • 大分放置されているプラグインだったので使いこなすのに苦労した:頼みの綱のgithubのIssuesなどもほとんど機能してないので手探りでした。エラーとconsole.logのみが相棒
  • こんなものを使わなくても普通にcanvas使う方法を探すか,現在もアクティブなプラグインを探して導入を試みたほうが良いかも

Discussion