SVG芸人

・イラストレーター

・複雑GUI会

・Vue.js Japan UG

・deno_ja

hashrock

SVG芸人が

よく聞かれる質問

「何でSVG?」

  • なんで数ある技術の中からSVGなの?
  • SVGの何が嬉しいの?
  • っていうかSVG芸人って何?

即答できない

  • 一言で説明しづらい
  • 一切使わなくてもフロントエンドは作れる
  • 使ってるうちに楽しくなってくる系

「使って楽しい」が
伝わればいいな…

お品書き

  • 平成のワードアート
  • 未来のワードアート
  • ワードアートのSVG実装
  • ついでにエディタの実装

ワードアートとは

ワードアート

  • テキストをデザインできるMS Officeの共通モジュール
  • かなり昔のOfficeからある
    • 確認した中で最古のWordArtは1994年
  • https://makewordart.com/

特徴的な極太書体

波打ってる

激しいグラデーション

ドロップシャドウ

3D風変形エフェクト

MS WordでのWordArtの
つくりかた

1994年(平成6年)

当時のWordArt

令和のワードアート

SVGならどこまでできる?

SVGで作ってみました

踊る

回る

セレクタブル

contenteditable

SVGすごい!

っていうかブラウザがすごい

SVGでのワードアートの作り方

<text x="40" y="80">Hello World</text>

1.テキスト要素

  <text x="40" y="80">Hello World</text>
  <path
    fill="none"
    stroke="blue"
    stroke-width="3"
    d="M 28 152 C 28 152 49 78 100 67 C 151 56 210 164 255 148 C 300 133 295 59 295 59"
  />

2.テキスト

  曲線

<text x="40" y="80">
  <textPath href="#MyPath">
    Hello World
  </textPath>
</text>
<path
  id="MyPath"
  fill="none"
  stroke="blue"
  stroke-width="3"
  d="M 28 152 C 28 152 49 78 100 67 C 151 56 210 164 255 148 C 300 133 295 59 295 59"
/>

3.テキスト

  曲線

<textPath>を追加

テキストと曲線を

関連付ける

<path
  id="MyPath"
  fill="none"
  stroke="blue"
  stroke-width="3"
  d="M 28 152 C 28 152 49 78 100 67 C 151...."
/>

「d属性」が曲線を定義している

しかし手書きはきつい

リアルタイム編集できれば便利そう…

SVG DOM は DOM Level 2 と互換性を保つように構築されている。

(W3C SVG 1.1 仕様)

SVGもDOMから扱える

ReactやVueなどのFWはSVGに対応している

バインディングも当然可能

Vueで曲線エディタを実装

3次ベジェ曲線

よく使うやつ

<path d="M70 110 C 70 140, 110 140, 110 110" />
<path d="M70 110 C 70 140, 110 140, 110 110" />

a

b

c

d

a

b

c

d

4つの制御点をもとに曲線を描画

ベジェ曲線を手描きしてみよう

<path :d="curvesStr" id="path1" />
  points: [
    { x: 200, y: 200 },
    { x: 240, y: 200 },
    { x: 210, y: 250 },
    { x: 250, y: 250 }
  ]
curvesStr() {
  return this.curves
    .map(i => {
      return `M ${i.points[0].x},${i.points[0].y} C ${i.points[1].x},${i.points[1].y} ${i.points[2].x},${i.points[2].y} ${i.points[3].x},${i.points[3].y}`;
    })
    .join(" ");
}

座標の配列(data内)

computedで文字列化してd属性に渡す

<path :d="curvesStr" id="path1" />
  points: [
    { x: 200, y: 200 },
    { x: 240, y: 200 },
    { x: 210, y: 250 },
    { x: 250, y: 250 }
  ]
curvesStr() {
  return this.curves
    .map(i => {
      return `M ${i.points[0].x},${i.points[0].y} C ${i.points[1].x},${i.points[1].y} ${i.points[2].x},${i.points[2].y} ${i.points[3].x},${i.points[3].y}`;
    })
    .join(" ");
}

この4つの点を

編集するUIを作ればOK

頂点・ハンドルの

ドラッグ

基本的にはHTML要素のドラッグと

変わらないはず

ただし注意点あり

ユーザー単位の世界

(単位なし)

スクリーン単位の世界

(px)

MouseEventから取れる座標は単位px

SVG側の単位に変換が必要

function screenToSvg(point, el, svg) {
  const pt = svg.createSVGPoint();
  pt.x = point.x;
  pt.y = point.y;
  return pt.matrixTransform(el.getScreenCTM().inverse());
}

ユーティリティ関数を作って対応

詳しくはviewBox / getScreenCTMなどの

キーワードで検索

きちんと対応すれば変形がかかった要素内でも

正常にドラッグ可能

ついでに作ったカラーピッカー

派手にする

Drop Shadow

SVGフィルタ

  • defs下に定義する
  • 組み合わせるといろんなフィルタを作れる
    • そのものズバリのDrop Shadowはない
<filter id="dropshadow">
  <feGaussianBlur in="SourceAlpha" stdDeviation="1" />
  <feOffset dx="5" dy="5" result="offsetblur" />
  <feComponentTransfer>
    <feFuncA type="linear" slope="0.5" />
  </feComponentTransfer>
  <feMerge>
    <feMergeNode />
    <feMergeNode in="SourceGraphic" />
  </feMerge>
</filter>

グラデーション

線形グラデーション

  • (x1, y1), (x2, y2)の2点間をグラデーションする
  • グラデーション定義もバインディングして変更できる
<linearGradient
  gradientUnits="userSpaceOnUse"
  id="rainbow"
  :x1="gradient.start.x"
  :y1="gradient.start.y"
  :x2="gradient.end.x"
  :y2="gradient.end.y"
>
  <stop offset="0%" stop-color="#ff0000" />
  <stop offset="16.7%" stop-color="#ffff00" />
  <stop offset="33.3%" stop-color="#00ff00" />
  <stop offset="50.0%" stop-color="#00ffff" />
  <stop offset="66.7%" stop-color="#0000ff" />
  <stop offset="83.3%" stop-color="#ff00ff" />
  <stop offset="100%" stop-color="#ff0000" />
</linearGradient>

DEMO

できなかったこと

  • グラデーションエディタの実装

  • SVG出力(Inkscapeで読むと壊れます)

  • キーボード操作対応

  • 複数選択

  • 3D変形

どうしてSVGなのか?

svgをGUI構築フレームワークとして考えることも可能

UIコンポーネント構築のベースとして
使い勝手が良い

自由度が高く
hackyになりにくい

(テキスト周りは除く)

作ったものが
自分の想定を
超えてくる感がある

(個人の感想です)

作ったものの使いみちが特にないので…

とりあえずTシャツにしました