SVGを直打ちで書く備忘録

SVGをコード直打ちでかけるようになりたかったので勉強してみた。
子供に人気なあのキャラクターを書いてみたけど、書き方を確実に忘れるので備忘録として残しておきます。
(あのキャラクターをそのまま載せるとさすがに怒られそうなのでブログ用に少し変形させました。)
anpan

基本的な形を書く

鬼門は線(path)

  • 四角: <rect x="15" y="15" width="80" height="60" rx="10" ry="10 "/>
    • rx, ryは角丸
  • 楕円: <ellipse cx="29" cy="50" rx="11" ry="10" />
    • 2次ペジェ曲線
    • 3次ペジェ曲線: <path d="M 0 0, C 250 100, 100 300, 300 300"/>

再利用可能なパーツやCSSはdefsタグ内に書く

SVGを手打ちで書く場合、同じパーツを何度も書きたくないですね。
繰り返し使うものはdefsの中に書きます。
パーツはsymbolタグ内に書いて、defsの外側でuseタグ呼び出して描画します。
以下の例ではU字型の線を再利用して3回描画しています。

<svg id="sample1" viewBox="0 0 100 100">
  <defs>
    <symbol id="p1">
      <path class="line" d="M 2 2, Q 6, 10, 10, 2" />
    </symbol>
  </defs>
  <use href="#p1" x="0" y="0"/>
  <use href="#p1" x="10" y="10"/>
  <use href="#p1" x="20" y="20"/>
</svg>

左右対称なら半分だけ作る

defsの応用。 useタグでパーツの変形が可能なので、左右対称の絵なら左側だけ作り、右側は反転表示で済ませる。 これでパーツの位置を微調整するときとかの作業が半分になる。

半分だけの絵を作るためにclipPathを定義してsymbolに適用する。
顔の左側はuseタグでそのまま表示し、顔の右側はuseタグ+transformで反転表示する。
反転した画像はviewBoxの外側に出てしまうので、x座業を指定して正しい位置に表示する。

<svg viewBox="0 0 100 100">
  <defs>
    ...中略...
    <clipPath id="harf">
      <rect x="0" y="0" width="50" height="100" />
    </clipPath>

    <symbol id="anpan" viewBox="0 0 100 100"  clip-path="url(#harf)">
      ...ここに左半分の絵を書く...
    </symbol>
  </defs>

  <use use href="#anpan" />
  <use use href="#anpan" x="-100" style="transform: scale(-1,1);" />
</svg>

まとめ

簡単な図形の組み合わせであのキャランクターがかけました。
次回はもう少し難しい図形を書いてみようと思います。

SVG全体のソースは下記

<svg id="anpanman" viewBox="0 0 100 100">
  <defs>
    <style>
    .line {
      stroke: #111;
      stroke-width: 1;
      stroke-linecap: round;
      fill: none;
    }
    .face {
      fill: #ea6;
    }
    .nose {
      fill: #f42;
    }
    .cheek {
      fill: #f62;
    }
    .light {
      fill: #fff;
    }
    .eye {
      fill: #111;
    }
    </style>

    <clipPath id="harf">
      <rect x="0" y="0" width="50" height="100" />
    </clipPath>

    <symbol id="anpan" viewBox="0 0 100 100"  clip-path="url(#harf)">
      <!-- 輪郭 -->
      <rect class="face line" x="15" y="15" width="80" height="60" rx="10" ry="10 "/>

      <!-- 目 -->
      <ellipse class="eye" cx="40" cy="36" rx="4" ry="6" />

      <!-- まゆ毛 -->
      <path class="line" d="M 32 32, C 33 20, 43 20, 45 28" />

      <!-- 口 -->
      <path class="line" d="M 30 60 ,C 35 67, 65 67, 70 60" />

      <!-- 頬の色 -->
      <ellipse class="cheek" cx="29" cy="50" rx="11" ry="10" />
      <!-- 頬の線 -->
      <path class="line" d="M 26 40, C 45 40, 45 60, 26 60" />
      <!-- 頬のハイライト -->
      <rect class="light" x="25" y="45" width="3" height="3" />

      <!-- 鼻 -->
      <rect class="nose line" x="39" y="40" width="22" height="20" rx="5" ry="5" />
    </symbol>
  </defs>

  <use href="#anpan" />
  <use href="#anpan" x="-100" style="transform: scale(-1,1);" />
  <rect class="light" x="45" y="45" width="3" height="3" />
</svg>