100時間後に死ぬNFT のコード解説(2) SVG
「100時間後に死ぬNFT」のコード解説の続き、SVG編です。
絵文字
ワニの画像や(墓石に供えられた)花の画像は絵文字を使っています。
一般的なSVGではpath要素を使って画像を表現しますが、それだとファイルサイズが大きくなり、フルオンチェーンNFTでは厳しいです。
絵文字なら <text font-size="50">🐊</text>
だけです。サイズ調整もfont-sizeを変更すればいいので簡単です。
ただし、絵文字は環境によって見た目が変わります。
左から Windows - Android - iPad で確認した画像です。(iPadが妙にリアル・・・)
(そして、今気づいたのですが、フォントもだいぶ違いますね。)
アニメーション
SVGでアニメーションさせる方法はいろいろあるようですが、今回は<animateMotion>
を使いました。
<text font-size="90">🐊 <animateMotion path="M-100,0 C-100-100 100,100 100,0 C100-100 -100,100 -100,0Z" dur="3s" repeatCount="indefinite"/> </text>
これでワニ(text要素)が、指定したpath
に沿って動きます。dur="3s"
はアニメーション時間で、3秒かけてpathを1周します。repeatCount="indefinite"
で無期限リピートさせています。
ライフが半分以下になると動きが徐々に遅くなりますが、これは残りライフによってdur="3s"
からdur="60s"
まで段階的に変えています。
animateMotion - SVG: Scalable Vector Graphics | MDN
クリッピング
<clipPath id="r"> <path d="M-200-200h400v400h-400z"/> </clipPath> <g clip-path="url(#r)"> <text .../> </g>
ワニが大きくなると動いたときにSVGの範囲をはみ出してしまうので、範囲外は描画しないようにクリッピングしています。
<clipPath id="xxx">
で範囲を定義して、clip-path="url(#xxx)"
で適用します。
クリッピングとマスキング - SVG: Scalable Vector Graphics | MDN
拡大縮小
ワニや墓石のサイズを変えるためにtransform
を使っています。
transform="scale(1.5)"
とすると1.5倍のサイズになります。
<g transform="scale(1.5)">...</g>
注意が必要なのは、拡大縮小は原点を中心に行われるので、原点が左上(デフォルト)だと拡大したときに右下方向に広がってしまいます。
そこで今回は画像の中央を原点とするため<svg viewBox="0 0 400 400"...>
ではなく、<svg viewBox="-200 -200 400 400"...>
としています。
(左上原点のままでtranslateを使って位置を修正したり、matrixを使う方法もありますが、ちょっと面倒です。)
transform - SVG: Scalable Vector Graphics | MDN
テキストの幅を調整
今回は、ユーザーが名前を付けるシステムなので、長い名前を付ける人がいるかもしれません。墓石に刻まれた名前が石をはみ出してしまわないようにtextLength
でテキスト幅を指定しています。それだけだと文字が重なってしまうので、lengthAdjust="spacingAndGlyphs"
を指定します。
<text textLength="100" lengthAdjust="spacingAndGlyphs">...</text>
反対に名前が短い場合には横に引き伸ばされてしまうので、名前が5文字未満ならデフォルト表示、5文字以上なら textLength
とlengthAdjust
を指定しています。
lengthAdjust - SVG: Scalable Vector Graphics | MDN
文字の縁取り
名前や年齢、HPのテキストは背景色で縁取りをしています。 (ワニが大きくなりすぎたときに文字と重なってしまうため。)
テキストの縁取りはstoke
で色を指定するのですが、そのままだと縁が文字の内部を覆ってしまうので、paint-order: stroke
を指定しておきます。
text{ paint-order: stroke; stroke: wheat; stroke-width: 3px; }
paint-order - CSS: カスケーディングスタイルシート | MDN
圧縮(最適化)
無駄な容量を減らすためにSVGの圧縮(最適化)を行っています。
例えば、<rect>
ではなく<path>
を使うことで短くできます。
<rect fill="#ffffff" x="100" y="200" width="300" height="150" />
↓最適化後 (表示される図形は全く同じです)
<path fill="#fff" d="M100 200h300v150H100z"/>
少しの差ですが積み重なるとかなり圧縮できます。まあ、そのかわり読みやすさや編集しやすさは失われてしまいます。
最適化はこちらのサイトで行いました → SVG Viewer
SVGを貼り付けてOPTIMIZEをクリックすると最適化されます。(PRETTIFY をクリックすると整形されて読みやすくなります。)
おわりに
フルオンチェーンNFTには欠かせないSVGですが、調べてみるとなかなか奥が深いですね。まだまだ知らない機能がいっぱいです。
次回、コード解説(3) 失敗・トラブル編 に続きます。