かわいい3Dエンジン「Zdog」の紹介

## はじめに

<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">Hello world, I give you Zdog 🐶 <a href="https://t.co/yL9FsIN6C7">https://t.co/yL9FsIN6C7</a><br>Round, flat, designer-friendly pseudo-3D engine for canvas &amp; SVG <a href="https://t.co/JXhYlCd8Sb">pic.twitter.com/JXhYlCd8Sb</a></p>&mdash; Dave DeSandro (@desandro) <a href="https://twitter.com/desandro/status/1133373535542489088?ref_src=twsrc%5Etfw">2019年5月28日</a></blockquote>

先日、[CodePen](https://codepen.io/)を眺めていたところ、[Zdog](https://zzz.dog/)というものを見つけて、とてもよかったので一通りドキュメントをみていろいろ作ってみました。

いつもならそれで終わりなのですが、6月4日にリリースされたばかり[^1]で日が浅く、まだ日本語の情報が少なかったため、自分のための備忘録も兼ねて簡単にご紹介します。

## Zdogとは

![zzzdog.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/961a66e8-b785-2e72-dcbc-e795e535d9bb.png)

> Round, flat, designer-friendly pseudo-3D engine for canvas & SVG

フラットで丸みを帯びたレンダリングがなされる擬似3Dエンジンです。

![sushidesu2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/4d81ab03-3078-e83c-1c4a-44629dbf008a.gif)
[こんなの](https://sushidesu.com)や、

![zdog.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/abd5a6a6-e635-e938-bea8-c9af1351e626.gif)
こんなのが簡単に作れます。
ロゴがこうやってぐりぐり動かせるのとてもいいと思います。

> Zdog v1 is a beta-release, of sorts.

まだベータ版ということで、V2が現在開発中[^2]のようです。

## Zdog 入門

せっかくなので、公式ドキュメントのGetting startedを参考にしながら、Zdogで3Dモデルを表示して、ドラッグで動かすまでをやっていこうと思います。

### インストール

公式ドキュメントには3通りの導入方法が紹介されていました。詳しくは[公式サイト](https://zzz.dog/#install)をご覧ください。

今回は、CDNを使います。

### 0. 準備

下記のサンプルコードはCDNを利用する場合の例です。この記事ではこのサンプルコードを使って説明していきます。

```html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Zdog Tutorial</title>
  <script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
</head>
<body>
  <canvas width="240" height="240" class="zdog-canvas"></canvas>
  <script>
    // ここにコードを記述
  </script>
</body>
</html>
```

### 1. `Illustration`インスタンスを作成

まず初めに親となる`Illustration`インスタンスを作成します。

```javascript
// ここにコードを記述
const illo = new Zdog.Illustration({
  element: '.zdog-canvas'
})
```

`element: '.zdog-canvas'`とすることで表示する`<canvas>`要素を指定しています。

![illust.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/46f83fc7-ccee-fcbd-adc5-973a022ce2ee.jpeg)

`<canvas>`要素と結び付けた`Illustration`に、この後登場する`Ellipse`や`Shape`などの図形を追加していき、最後に`Illustration`をレンダリングするという流れになります。

### 2. 図形を追加

今回は円を表示してみようと思います。

```javascript
// ここにコードを記述
const illo = new Zdog.Illustration({
  element: '.zdog-canvas'
})

// 円を追加
new Zdog.Ellipse({
  addTo: illo,
  diameter: 80,    // 直径
  stroke: 20,      // 線の太さ
  color: '#E62'    // 図形の色
})
```

`addTo: illo`とすることで先程作成した`Illustration`に図形を追加しています。
ただ、まだ追加しただけでレンダリングを行っていないので図形は表示されません。

### 3. レンダリング

```javascript
// ここにコードを記述
const illo = new Zdog.Illustration({
  element: '.zdog-canvas'
})

// 円を追加
new Zdog.Ellipse({
  addTo: illo,
  diameter: 80,    // 直径
  stroke: 20,      // 線の太さ
  color: '#E62'    // 図形の色
})

// 図形を描画
illo.updateRenderGraph()
```

`Illustration`クラスの`updateRenderGraph`メソッドを呼び出すことで図形をレンダリングします。

![start3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/c1eff95b-d015-c05e-4eb2-38eb671c0472.jpeg)

たった3ステップでフラットな3D図形を表示することができました！

しかし、このままだと動かないので3Dっぽくありません。ということで、次は図形をアニメーションさせてみようと思います。

### 4. アニメーション

アニメーション用の関数を作成して、`requestAnimationFrame()`に渡します。

```javascript
// - 省略 -
const animate = () => {
  illo.rotate.x += 0.03
  illo.rotate.y += 0.03             // 図形を回転
  illo.updateRenderGraph()          // 回転後の図形を描画

  requestAnimationFrame( animate )  // 再度呼び出し
}
animate()
```

![move2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/c7bcfe43-82fa-c4d2-fd47-6ae475b00dab.gif)

くるくる回ります！

### 5. ドラッグで回転

```javascript
// ここにコードを記述
const illo = new Zdog.Illustration({
  element: '.zdog-canvas',
  dragRotate: true    // ←これを追加
})
```

`dragRotate: true`と設定するだけです。簡単にぐりぐりできます！

![drag.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/6cea8464-858b-944f-e12b-7008174c644b.gif)

### 完成

初心者の私でも特につまずくことなく3D図形を表示して、ドラッグでぐりぐりすることができました!

> Zdog is friendly. Modeling is done with a straight-forward declarative API.

とあるようにとても直感的でわかりやすいですね。

最後に、完成したコードを改めて記しておきます。

```html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Zdog Tutorial</title>
  <script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
</head>
<body>
  <canvas width="240" height="240" class="zdog-canvas"></canvas>
  <script>
    // ここにコードを記述
    const illo = new Zdog.Illustration({
      element: '.zdog-canvas',
      dragRotate: true
    })

    new Zdog.Ellipse({
      addTo: illo,
      diameter: 80,    // 直径
      stroke: 20,      // 線の太さ
      color: '#E62'    // 図形の色
    })

    const animate = () => {
      illo.rotate.x -= 0.03
      illo.rotate.y += 0.03             // 図形を回転
      illo.updateRenderGraph()          // 回転後の図形を描画

      requestAnimationFrame( animate )  // 再度呼び出し
    }
    animate()
  </script>
</body>
</html>

```

## 番外編：ネコをつくる:crying_cat_face:

これだけだとドキュメントまんまなので、番外編としてネコをつくってみます。少し長いので隠してあります。

<details><summary>クリックして開く</summary><div>

### 頭をつくる

![neko_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/910ced50-7110-27b7-ca7a-13899037af92.jpeg)

```javascript
const head = new Zdog.Shape({
  addTo: illo,
  stroke: 200,
  color: '#9ED'
})
```

頭を作ります。`Shape`クラスはパスで様々な形状の図形を作ることができますが、何も指定しない場合、ただの点が作られます。今回はその点の太さを調整して、球をつくっています。

### 右耳を作る

ネコといえば耳なので耳を作ります。`Polygon`クラスを使うと多角形を作ることができますが、今回は先ほど使った`Shape`にパスを指定して三角形を作っています。

```javascript
const ear_right = new Zdog.Shape({
  addTo: illo,
  stroke: 20,
  fill: true,
  path: [
    { x: 0, y: -10 },
    { x: 30, y: 30 },
    { x: -30, y: 30 }
  ],
  translate: { x: -90, y: -80, z: 10 },
  rotate: { z: -0.6 }
})
```

`translate`と`rotate`を指定することで、図形の位置を調整することができます。

![neko_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/56d10539-52d9-e982-41ae-521a2091fdec.jpeg)

まだネコではありません。

### 左耳を作る

目や耳など、同じものを何個も作るときには、`copy()`メソッドを使うと便利です。

```javascript
// ear_left
ear_right.copy({
  addTo: illo,
  translate: { x: 90, y: -80, z: 10 },
  rotate: { z: 0.6 }
})
```

引数として、様々なオプションを渡すことで、位置や大きさなどを調整できます。

![neko_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/94e49cc5-ce47-7031-6d9e-8127636eb74e.jpeg)

まだネコではありません（目がないので）

### 目を作る

`Ellipse`で円を作る際に`quarters`を指定することで半円が作れます。

```javascript
const eye_right = new Zdog.Ellipse({
  addTo: illo,
  diameter: 40,
  quarters: 2,
  stroke: 10,
  rotate: { z: -Zdog.TAU / 4 },
  translate: { x: -40, z: 90 }
})
// eye_left
eye_right.copy({
  translate: { x: 40, z: 90 }
})
```

図形を1/4や1/2などキリの良い単位で回転させたい場合は[`Zdog.TAU`](https://zzz.dog/api#utilities-tau)を使います。

![neko_4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/2b6c1272-0150-737b-467b-24fcc22550be.jpeg)

これはネコです。(目があるので)

### 口があるとかわいいので

`Shape`を使って線を引いていきます。頂点のx,y,z軸を示すオブジェクトを渡すだけなのでとても簡単です。

```javascript
const mouth = new Zdog.Shape({
  addTo: illo,
  stroke: 8,
  path: [
    { x: -14 },
    { x: 14 }
  ],
  translate: { y: 20, z: 90 }
})
```

![neko_5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/52c89bef-2a59-506a-3b40-e27f2c764c52.jpeg)

よりネコらしくなりました。でもまだ何か足りない気もします。

### ひげも必要でした

口と同じ要領で`path`をひいて位置を調整します。

```javascript
// ひげ
const hige_left = new Zdog.Group({
  addTo: illo,
  translate: { x: -90, y: 10, z: 60 }
})

// 1本目のひげ
const hige = new Zdog.Shape({
  addTo: hige_left,
  stroke: 9,
  path: [
    { x: 15 },
    { x: -15 }
  ],
  rotate: { z: 0.4 }
})
// 2本目のひげ
hige.copy({
  translate: { y: 40 },
  rotate: { z: -0.2 }
})

// まとめて複製
hige_left.copyGraph({
  rotate: { z: Zdog.TAU / 2 },
  translate: { x: 90, y: 50, z: 60 }
})
```

ひげのように複数の図形をまとめるときに便利なのが`Group`です。`addTo`に作成した`Group`を指定することでまとめることができます。

`copy()`メソッドは、子孫のアイテムまでコピーしてくれないので、まとめた図形を複製する際は`copyGraph()`メソッドを使います。

### 完成

![neko_6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441311/c3c21eaf-7585-44f0-da2c-c31277166197.jpeg)

かわいいネコができました！一応、[こちら](https://codepen.io/sushidesu/pen/JQRxxj)にコードも載せておきます。

</div>

## おわりに

簡単な説明ということで物足りない部分もあるかと思いますが、参考になれば幸いです。

また、このZdogをいたるところに使って[^3]作成した[ホームページ](https://sushidesu.com)があります。よろしければぜひご覧ください。

## 参考

- [Zdog · Round, flat, designer-friendly pseudo-3D engine for canvas and SVG](https://zzz.dog/)
- [metafizzy/zdog - GitHub](https://github.com/metafizzy/zdog)
- [オシャレなフラットデザインを擬似的に3D化できるブラウザ向け3Dエンジン「Zdog」 - GIGAZINE](https://gigazine.net/news/20190602-zdog/)
[^1]: [v1.0.0 - Initial public release](https://github.com/metafizzy/zdog/releases/tag/v1.0.0)
[^2]: https://github.com/metafizzy/zdog#beta
[^3]: 言うほど使っていません、、、