飛ばねぇ馬はただの馬。

Life is too short for bad code.

Vue.jsとKonva.jsでcanvasに入門した

これはなに

Vue.jsとVue.jsに対応したCanvasラッパーであるKonva.jsを使ってCanvasに入門した話をまとめる。

つかった技術

  • Vue.js
  • Konva.js (vue-konvaパッケージ)

なにをつくった

時間とともに七色に変化する円をつくった。

f:id:pranc1ngpegasus:20210828211441g:plain

右側に表示されている16進数は2桁ずつのRGB値となっており、七色に変化させたかったのでHSB色空間からRGB色空間に変換したものを使っている。

つくり方

1. Vue.jsプロジェクトを作成する

Vue CLI 3を使ってプロジェクトを作成した。

プリセットをデフォルト(babel, eslint)、パッケージ管理ツールをyarnとした。

2. vue-konvaパッケージをインストールする

Vue.jsに対応したCanvasラッパーãKonva.jsしか見つからなかったので、vue-konvaパッケージをインストールする。

yarn add vue-konva konva

3. 不要なコンポーネントのお掃除をする

入門なので、余計な記述があると混乱するため不要な記述は削除しておく。 また、src/components/HelloWorld.vueも不要なので削除する。

<template>
  <div>
  </div>
</template>

<script>
export default {
  name: 'app',
  components: {
  }
}
</script>

4. コンポーネントを作成して円を描く

src/componentsディレクトリに適当な名前でvueファイルを作成し、以下のコードを記述する。

<template>
  <v-stage :config="configKonva">
    <v-layer>
      <v-circle :config="configCircle"></v-circle>
    </v-layer>
  </v-stage>
</template>

<script>
const width = window.innerWidth;
const height = window.innerHeight;

export default {
  data() {
    return {
      configKonva: {
        width: width,
        height: height
      },
      configCircle: {
        x: 200,
        y: 200,
        radius: 100,
        fill: "#00c800",
        stroke: "black",
        strokeWidth: 4,
        shadowBlur: 10
      }
    };
  }
</script>
<template>
  <div>
    <Circle />
  </div>
</template>

<script>
import Circle from './components/Circle.vue'

export default {
  name: 'app',
  components: {
    Circle
  }
}
</script>

この段階で以下のような円が描画されます。

f:id:pranc1ngpegasus:20210828211526p:plain

5. 円の色を変える

どこを変えたらいいの

vue-konvaでは、v-stageディレクティブにconfigKonvaオブジェクトを渡すことでcanvasの設定を行い、v-circleディレクティブにconfigCircleというオブジェクトを渡すことで円を描画しているようでした。 円の中のè²を変えるためには、configCircleオブジェクトのfillプロパティを変えるとよさそうです。

configCircle: {
  x: 200,
  y: 200,
  radius: 100,
  fill: "#00c800",
  stroke: "black",
  strokeWidth: 4,
  shadowBlur: 10
}

fillプロパティは#からはじまる6桁の16進数を指定することによりRGBの値を設定できるので、この値を上書きしていくことにします。

HSBからRGBへの変換

今回、16進6桁のRGB値を七色に変化させるためにHSB色空間というものを使ってみました。 HSB色空間とは、色をH(Hue: 色相)、S(Saturation: 彩度)、B(Brightness: 明度)で表現する色空間のことです。 今回は、彩度、明度を100%にした状態で、色相を0~360まで変化させることで七色に色が変化させることにしました。

HSBからRGBへの変換ã参考に、変換するコードを実装したのがこちら。

Hに0~360までの値、Sに0~1までの値、Vに0~1までの値を引数として与えることでRGBの値を16進2桁ずつの配列として返してくれます。

hsv2rgb: function (H,S,V) {
  var C = V * S;
  var Hp = H / 60;
  var X = C * (1 - Math.abs(Hp % 2 - 1));

  var R, G, B;
  if (0 <= Hp && Hp < 1) {
    [R,G,B]=[C,X,0]
  }
  else if (1 <= Hp && Hp < 2) {
    [R,G,B]=[X,C,0]
  }
  else if (2 <= Hp && Hp < 3) {
    [R,G,B]=[0,C,X]
  }
  else if (3 <= Hp && Hp < 4) {
    [R,G,B]=[0,X,C]
  }
  else if (4 <= Hp && Hp < 5) {
    [R,G,B]=[X,0,C]
  }
  else if (5 <= Hp && Hp < 6) {
    [R,G,B]=[C,0,X]
  }

  var m = V - C;
  [R, G, B] = [R+m, G+m, B+m];

  R = Math.floor(R * 255).toString(16);
  G = Math.floor(G * 255).toString(16);
  B = Math.floor(B * 255).toString(16);

  return [R ,G, B];
}

実際に変えてみよう

RGBの値は16進6桁の文字列として与える必要があるため、zeroPadding関数を使ってhsv2rgb関数の最後にRGBそれぞれの値を0埋めするコードを追加した。

zeroPadding: function (num, length) {
  return ('0000000000' + num).slice(-length);
},
hsv2rgb: function (H,S,V) {
  // ここまで省略
  R = Math.floor(R * 255).toString(16);
  G = Math.floor(G * 255).toString(16);
  B = Math.floor(B * 255).toString(16);

  R = this.zeroPadding(R, 2);
  G = this.zeroPadding(G, 2);
  B = this.zeroPadding(B, 2);

  return [R ,G, B];
}

この2つの関数とsetIntervalを使って色を変化させてみます。

var inthsv = 0;

setInterval( () => {
  inthsv++;
  inthsv %= 360;
  var strrgb = this.hsv2rgb(inthsv, 1, 1).join('');
  this.configCircle.fill = '#' + strrgb;
  console.log(strrgb);
}, 1/30 * 1000);

6. 完成形のコード

<template>
  <v-stage :config="configKonva">
    <v-layer>
      <v-circle :config="configCircle"></v-circle>
    </v-layer>
  </v-stage>
</template>

<script>
const width = window.innerWidth;
const height = window.innerHeight;
var inthsv = 0;

export default {
  data() {
    return {
      configKonva: {
        width: width,
        height: height
      },
      configCircle: {
        x: 200,
        y: 200,
        radius: 100,
        fill: "#00c800",
        stroke: "black",
        strokeWidth: 4,
        shadowBlur: 10
      }
    };
  },
  mounted() {
    setInterval( () => {
      inthsv++;
      inthsv %= 360;
      var strrgb = this.hsv2rgb(inthsv, 1, 1).join('');
      this.configCircle.fill = '#' + strrgb;
      console.log(strrgb);
    }, 1/30 * 1000);
  },
  methods: {
    zeroPadding: function (num, length) {
      return ('0000000000' + num).slice(-length);
    },
    hsv2rgb: function (H,S,V) {
      var C = V * S;
      var Hp = H / 60;
      var X = C * (1 - Math.abs(Hp % 2 - 1));

      var R, G, B;
      if (0 <= Hp && Hp < 1) {
        [R,G,B]=[C,X,0]
      }
      else if (1 <= Hp && Hp < 2) {
        [R,G,B]=[X,C,0]
      }
      else if (2 <= Hp && Hp < 3) {
        [R,G,B]=[0,C,X]
      }
      else if (3 <= Hp && Hp < 4) {
        [R,G,B]=[0,X,C]
      }
      else if (4 <= Hp && Hp < 5) {
        [R,G,B]=[X,0,C]
      }
      else if (5 <= Hp && Hp < 6) {
        [R,G,B]=[C,0,X]
      }

      var m = V - C;
      [R, G, B] = [R+m, G+m, B+m];

      R = Math.floor(R * 255).toString(16);
      G = Math.floor(G * 255).toString(16);
      B = Math.floor(B * 255).toString(16);

      R = this.zeroPadding(R, 2);
      G = this.zeroPadding(G, 2);
      B = this.zeroPadding(B, 2);

      return [R ,G, B];
    }
  }
};

</script>

<style>
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #F0F0F0;
}
</style>

まとめ

Vue.jsとKonva.jsを使ってcanvasに入門してみた。 Konva.jsがわかりやすくラップしてくれていてサクっと描画することができて好印象だった。