TECH BOX

Technology blog from Web Engineer

この記事は最終更新日から4年以上経過しているため正確ではないかもしれません

Konva.jsを使って花火を表現

先日、自身のサイト(arc-one.jp)をリニューアルしました。
せっかくなので遊び心を取り入れて、キービジュアル部分をクリックすると「花火」か「雫」が出現するようになっています。

前回は雫の作り方を紹介したので、今回は花火の作り方を紹介します。

まずは完成形から。

See the Pen konva -hanabi by Nobuyuki Kondo (@artprojectteam) on CodePen.0

使用ライブラリやステージの作成などの基本は前回の雫作成記事を参照してください。

花火素材の準備

花火は下記の3つで作られています。

See the Pen konva -hanabi – material by Nobuyuki Kondo (@artprojectteam) on CodePen.0

円とリングはKonva.jsのKonva.CircleKonva.Ringを使っています。
真ん中の赤い円は左のスポークにかぶせて真ん中を開けるようにするために使います。
また、スポークが広がって消えているように見せるためにも使います(後述)。

スポークはKonva.Pathを使います。

パスはm33.9644661 42.5h-33.9644661v-5h33.9644661l-24.0165043-24.0165043 3.5355339-3.5355339 24.0165043 24.0165043v-33.9644661h5v33.9644661l24.0165043-24.0165043 3.5355339 3.5355339-24.0165043 24.0165043h33.9644661v5h-33.9644661l24.0165043 24.0165043-3.5355339 3.5355339-24.0165043-24.0165043v33.9644661h-5v-33.9644661l-24.0165043 24.0165043-3.5355339-3.5355339zとなっており、これをSVGファイルにすると同じものができます。

SVGファイルにする場合は下記。

<svg height="80" viewBox="0 0 80 80" width="80" xmlns="http://www.w3.org/2000/svg">
  <path d="m33.9644661 42.5h-33.9644661v-5h33.9644661l-24.0165043-24.0165043 3.5355339-3.5355339 24.0165043 24.0165043v-33.9644661h5v33.9644661l24.0165043-24.0165043 3.5355339 3.5355339-24.0165043 24.0165043h33.9644661v5h-33.9644661l24.0165043 24.0165043-3.5355339 3.5355339-24.0165043-24.0165043v33.9644661h-5v-33.9644661l-24.0165043 24.0165043-3.5355339-3.5355339z" fill="#ffffff"/>
</svg>

Konva.jsではSVGのパスをそのまま使えるので、デザインしたものを一度SVGにしてパスとグループを駆使するというのも簡単にできます。

花火アニメーション

まずはアニメーションを考えなければいけません。
今回は下記のようなアニメーションになるように簡単なコンテを描きました。

  1. 丸い火花
  2. 火花が広がりつつスポークが表れる
  3. スポークが消えていく

流れはこんな感じです。
コンテには書いていませんが、初期表示は円を小さくして全体をスケールアップしながら行うことで、よりリアリティになるようにします。

火花を作る

花火とはいえクリックしたところから発火するため、通常の花火のように下から打ち上がるような感じではありません。
なので、火花が出現して消える演出を行います。

See the Pen konva -hanabi – hibana by Nobuyuki Kondo (@artprojectteam) on CodePen.0

本来は一瞬なのでアニメーションは100msで行うのですが、分かりづらいのでこのサンプルでは600msで見やすくしています。

const size = 80
const ring = new Konva.Ring({
  x: size,
  y: size,
  innerRadius: 0,
  outerRadius: size,
  fill: '#fff'
})

リングはKonvaではこのように作ります。
真ん中から変化をさせたい場合はinnerRadiusの値を変化させます。
上記の場合はinnerRadius: 0なので真ん中が開いていない円になります。

const anim = new Konva.Tween({
  node: ring,
  innerRadius: size,
  duration: 0.6,
  easing: Konva.Easings.StrongEaseIn
})
anim.play()

innerRadiusをリングのサイズと同じにすれば内側から消えるように変化します。

スポークを散らす

もう一つはスポーク部分を散らすアニメーションを行います。

See the Pen konva -hanabi – spoke animation by Nobuyuki Kondo (@artprojectteam) on CodePen.0

スポークを散らすということでスポークの方をなんとかしがちですが、スポークの上に乗せている赤い円を大きくすることで散っているように見えます。

const size = 80
const circle = new Konva.Circle({
  x: size,
  y: size,
  radius: size,
  scale: {
    x: 0.3,
    y: 0.3
  },
  fill: '#cf2620'
})

const anim = new Konva.Tween({
  node: circle,
  scaleX: 1.1,
  scaleY: 1.1,
  duration: 0.5,
  easing: Konva.Easings.EaseOut
  }
})
anim.play()

アニメーション自体はとても簡単で、赤円自体は80×80で作成しますが、0.3倍まで縮小しておきます。
(花火素材のセクションで表示している赤円の大きさ)

そこから、1.1倍に拡大します。

1.1倍の理由

上記のサンプルだと等倍にしてもわかりませんが、実は薄っすらとスポークの端っこが残る事があります。
最終的にはスポーク部分も含めてグループ全体を拡大していった際にわかりやすくなるために大きめに拡大します。

アニメーションの開始の時に赤円が既に大きく見える理由

0.3から1.1に拡大しているはずなのにアニメーション開始時に赤円が大きく見えるのはイージングがease-outになっているためです。
ease-outは初速が早くなるために赤円が大きく見えます。

ease-inの場合はこの逆になります。

火花とスポークのアニメーションを同時に行う

今度は、火花とスポークのアニメーションを同時に行います。

See the Pen konva -hanabi – animation 1 by Nobuyuki Kondo (@artprojectteam) on CodePen.0

ちょっと不格好ですね。
むしろ白いのが邪魔です。

仕上げ

最後の仕上げとして、これらの素材を0.1からスタートして大きくなって消えるようにします。

下準備

素材を大きくするにはそれぞれ大きくしていたのでは負荷が高いので、素材をすべてグルーピングします。

const size = 80
const group = new Konva.Group({
   x: size,
   y: size,
   offset: {
    x: size,
    y: size
  },
  scale: {
    x: 0.1,
    y: 0.1
  }
})

xとyは最終的にクリック座標が入ります。
offsetでsize分ずらしているのはアニメーションのアンカー位置が真ん中になるようにするためです。

あとは各素材をgroupの子要素にして、グループ自体を花火レイヤーに追加します。

group.add(spoke)
group.add(circle)
group.add(ring)

hanabiLayer.add(group)

アニメーションさせる

アニメーションはいたってシンプルでスケールを0.1から2.5にします。
これを1500msかけてアニメーションさせます。

See the Pen konva -hanabi – animation publish by Nobuyuki Kondo (@artprojectteam) on CodePen.0

ここで「あれ?」と思った人は鋭いです。
先程、等倍のままアニメーションをした時と、グループを拡大しながらアニメーションさせたのでは見え方が若干変わります。

これは、グループ全体の変化時間(duration)があるためです。
火花(リング)は0.1s、スポークは0.5sというのは変わらないため、火花部分はグループが小さい時にほぼ終了してしまうため、先程の等倍時のアニメーションとは変わって見えています。

グループのアニメーションは下記になります。

const anim = new Konva.Tween({
  node: group,
  scaleX: 2.5,
  scaleY: 2.5,
  duration: 1.5,
  easing: Konva.Easings.EaseOut
})
anim.play()

これで完成ですが、赤円の開始タイミングを少しずらすと最終的なスポークが散っていく大きさが変わってきます。
この辺は好みで花火を演出していいと思います。

下記は赤円の変化開始タイミングを200ms遅延させた場合

See the Pen konva -hanabi – animation publish 2 by Nobuyuki Kondo (@artprojectteam) on CodePen.0

クリックイベント

あとは、クリックした箇所から出現させるだけですが、クリックイベントに関しては前回の雫記事と同様なのでそちらを参考にしてください。


アニメーションは一見難しく見えることもあります。
しかし、分解して考えたり工夫することで、一見難しそうに見えるものでも比較的簡単にそれらしく見せることも可能です。