🔲

InkscapeによるWeb素材として使いやすいSVGの作り方

2023/06/06に公開

Inkscapeは使いやすいSVGの編集ツールですが、JSで加工するような素材を作るときには不便な罠が多く、あまりWeb素材作成向きではないです。

今回はJSで動的にパスを調整するサンプルとその実現のための準備や操作などをまとめていきます。

絶対にやってはいけないこと

まず絶対にやってはいけないことは以下です。

  • 何かオブジェクトを追加する
  • ドキュメントの設定を変更する

実際にやってみると以下のようになります。

まず 100x100 の正方形を作ります。上のツールバー右を見る限り、確かに原点を開始地点として 100x100 の正方形があります。

ここで ファイルドキュメントのプロパティ からページの高さなどを 100 に設定します。

次に 編集XMLエディター でSVGそのものの情報を見ると、viewBox の値が 0 0 100 100 になっていません。

あーこれは良くない。再度ドキュメントのプロパティを開いてビューボックスの設定を変更すると……

以下のようになります。

最初の時点でいくらツールバーの数値上の 100 に設定したとて、初期設定を変更するとそれに伴ってビューボックスの調整などが入ったりします。また最初の正方形もXMLエディタで見ると設定を変更していない段階で100ではない変な値が入っています。

つまり初期状態ではツールバーと実際のXMLのパス座標に乖離が発生していて、ツールバーや出力パスの座標を全く信じることができません。
そのため、もしJSで諸々のパス操作をしたり、Inkscapeのツールバーに表示されている値が正しい前提で使っていると痛い目を見ることになります。
なので絶対に「初期設定をpx基準の完璧な状態にしてから」オブジェクトの配置をしていくことになります。

始めにやること

以下の手順で操作上の値とパスの座標を一致させていきます。

  • 編集環境設定入出力SVG 出力 で設定を変更
    • パスデータ最適化 から 絶対座標 に変更
      • パスを使うだけなら最適化でも良いが、JSでの加工を前提とする場合は絶対座標の方が都合が良いことが多々あります
      • この設定はドキュメントの設定ではなくInkscapeの設定なので、一度設定すると次からは設定不要です
  • ファイルドキュメントのプロパティ で設定を変更
    • フォーマットや表示単位を mm から px に変更
    • 幅と高さを設定
      • もしファイルとして出力しない場合も初期設定ではA4なので、適当に使いやすくpx単位でキリの良い値にしておくと良いです
      • パスや座標しか使わないならはみ出しても問題はないので、100くらいに設定しておくと使いはすいはずです
    • ビューボックスを開き幅と高さを同じ値に設定する

ここまで設定できれば上のUI上の座標とSVG上の座標がpx単位で一致し、頂点選択やXMLエディターで見たパスがそのまま使える状態になります。

ファイルとして出力

Web用の素材として出力する場合、名前をつけて保存時に種類を変更することでInkscape用の諸々の設定を消した状態で出力できます。
また出力前にいくつか加工をしておくとゴミデータがない状態で出力されるのでファイルサイズを減らせるので実際に作業をしてみます。

サンプルファイルはこちらです。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   width="100"
   height="100"
   viewBox="0 0 100 100"
   version="1.1"
   id="svg35507"
   inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
   sodipodi:docname="sample_01.svg"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg">
  <sodipodi:namedview
     id="namedview35509"
     pagecolor="#ffffff"
     bordercolor="#000000"
     borderopacity="0.25"
     inkscape:showpageshadow="2"
     inkscape:pageopacity="0.0"
     inkscape:pagecheckerboard="0"
     inkscape:deskcolor="#d1d1d1"
     inkscape:document-units="px"
     showgrid="true"
     inkscape:zoom="1.5215769"
     inkscape:cx="69.66457"
     inkscape:cy="100.55358"
     inkscape:window-width="1076"
     inkscape:window-height="703"
     inkscape:window-x="684"
     inkscape:window-y="1106"
     inkscape:window-maximized="0"
     inkscape:current-layer="layer1">
    <inkscape:grid
       type="xygrid"
       id="grid35628"
       originx="0"
       originy="0" />
  </sodipodi:namedview>
  <defs
     id="defs35504" />
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1">
    <path
       id="rect35995"
       style="fill:#808080;stroke-width:8.48528;stroke-linecap:round;stroke-linejoin:round"
       d="M 100,0 H 50 L 0,20 V 50 C 0,77.614237 22.385763,100 50,100 77.614237,100 100,77.614237 100,50 Z"
       sodipodi:nodetypes="ccccscc" />
  </g>
</svg>

見た目はこんな感じです。

まずは 編集XMLエディター を開いておきます。
この状態で出力対象のオブジェクトを選択します。すると対象のオブジェクトの属性などが見れるようになります。

ここで注目すべきは style 属性です。その他のいらなそうな sodipodi:nodetypesid 属性は出力時に消したりできるのですが、この style に関してはInkscape側で消してよいかどうかの判断ができずすべてそのまま残ります。
現在選択中のオブジェクトの style を見てみると以下です。

  • fill
    • #808080
  • stroke-width
    • 8.48528
  • stroke-linecap
    • round
  • stroke-linejoin
    • round

fill は塗りつぶしの色なので残す必要がありますが、その他は線の設定です。塗りつぶしだけ使うならいらない設定なのでこれらは全部消してしまいます。
このように不要な設定がわからない場合は一つ一つ消してみて、見た目の変化で判断してください。

次にファイルとして出力します。

もし画像ファイルとして使う場合には最適化SVGや(サーバーが対応してるなら)プレーンSVG圧縮などを選択すると良いです。
この時以下のように最適化オプションが表示されるので自分に必要なオプションを入れておきます。

例えば上の不要styleを消してプレーンSVGで出力すると以下のようになります。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   width="100"
   height="100"
   viewBox="0 0 100 100"
   version="1.1"
   id="svg35507"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg">
  <defs
     id="defs35504" />
  <g
     id="layer1">
    <path
       id="rect35995"
       style="fill:#808080"
       d="M 100,0 H 50 L 0,20 V 50 C 0,77.614237 22.385763,100 50,100 77.614237,100 100,77.614237 100,50 Z" />
  </g>
</svg>

これを最適化で出力すると以下のようになります。

<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
 <path d="m100 0h-50l-50 20v30c0 27.614 22.386 50 50 50s50-22.386 50-50z" fill="#808080"/>
</svg>

だいぶスッキリしましたね。styleの塗りも fill 属性に変わっていますし、不要な id やコメントなどもないです。オプションを入れればもっと無駄を省けたりします。
ただしパス(d 属性)に関しては絶対パスではなかったりと加工しにくいと思うので、プログラムで加工予定があるなら最適化したものは使わないほうが良いでしょう。

パスの加工

パスの加工のためにサンプルを用意しました。先程の画像を埋め込んであり、5秒後に縦にいい感じに伸ばしたいと思います。

<!DOCTYPE html>
<html lang="ja" style="font-size: 2.5vmin">
  <head>
    <meta charset="utf-8">
    <title>sample</title>
  </head>
  <body>
    <svg
      id="svg"
      version="1.1"
      viewBox="0 0 100 100"
      xmlns="http://www.w3.org/2000/svg"
      style="width: 100px"
    >
      <path
        id="path"
        d="M 100,0 H 50 L 0,20 V 50 C 0,77.614237 22.385763,100 50,100 77.614237,100 100,77.614237 100,50 Z"
        fill="#808080"
      />
    </svg>
    <script>
      ((svg, path) => {
        // 5秒後に画像の長さを縦に長くする
        setTimeout(() => {
          const y = 30;
          svg.setAttributeNS(null, 'viewBox', `0 0 100 ${100 + y}`);
          path.setAttributeNS(
            null,
            'd',
            `M 100,0 H 50 L 0,20 V ${50 + y} C 0,${77.614237 + y} 22.385763,${
              100 + y
            } 50,${100 + y} 77.614237,${100 + y} 100,${77.614237 + y} 100,${
              50 + y
            } Z`,
          );
        }, 5000);
      })(
        document.getElementById('svg'),
        document.getElementById('path'),
      );
    </script>
  </body>
</html>

この場合のパスをどうやって縦にだけ伸ばすかの手順を書いていきます。

  • <svg>viewBox を調整する
  • <path>d 属性にパスの座標を指定する

特に後者が面倒そうです。ここでパスの座標指定を見てみます。

M 100,0 H 50 L 0,20 V 50 C 0,77.614237 22.385763,100 50,100 77.614237,100 100,77.614237 100,50 Z

これは以下のように分解できます。
なお座標指定にあるアルファベットは大文字の時絶対座標、小文字の時は相対座標(その前の頂点からの差分)となっています。
今回は絶対座標で出力するようにしているので、すべて絶対座標で解釈します。

ちなみに座標は以下の順番で格納されていて、縦に伸ばす場合下の3つの頂点を下にずらしたいとします。

  • M
    • 座標の移動(移動だけで線を描かない)
      • X座標,Y座標 と指定する
      • 100,0
    • ①の頂点
  • H
    • 水平に座標を移動しながら線を描く
      • 水平移動なので X座標を指定する
      • 50
    • ②の頂点
  • L
    • 指定座標まで座標を移動しながら線を描く
      • 座標指定なので X座標,Y座標 と指定する
      • 0,20
    • ③の頂点
  • V
    • 垂直に座標を移動しながら線を描く
      • 垂直移動なので Y座標を指定する
      • 50
    • ④の頂点
  • C
    • いくつかの座標を指定して曲線を描く
      • 二次のベジエ曲線なので、制御点1、制御点2、終点を X座標,Y座標 の形でスペースを区切りにして3つ繋げる
      • 0,77.614237 22.385763,100 50,100
    • ⑤の頂点(⑤の曲線の左側)
  • 省略
    • 省略された場合は以前のコマンドを引き継ぐので C が引き続き使われる
      • Inkscape的には対称になるような曲線なので、今回のような場合はもう一つ追加されている。
      • 77.614237,100 100,77.614237 100,50
    • ⑥の頂点(⑤の曲線の右側)
  • Z
    • 今いる座標から始点(Mで指定した座標)まで直線を描く
    • ⑥~①の間をつなぐ

今回動かしたいのは④~⑥の頂点なので、そこに対応する座標の Y座標 だけ加工します。具体的には以下の部分です。

const y = 30;
path.setAttributeNS(
  null,
  'd',
  `M 100,0 H 50 L 0,20 V ${50 + y} C 0,${77.614237 + y} 22.385763,${
    100 + y
  } 50,${100 + y} 77.614237,${100 + y} 100,${77.614237 + y} 100,${50 + y} Z`,
);

今回は「絶対座標」なので、加工する頂点さえ間違えなければ足し算だけでOKです。状況によって加工方法は異なると思いますが、基本的には絶対座標で管理するほうが単純に加工できるかと思います。(動かす部分が複雑すぎて相対パスを使ったほうが良さそうに見える場合もあるが、その場合は動かしやすい頂点を動かして viewBox か移動のスタイルを使って調整したほうが楽)

開始座標、パスの向きの変更

今回はそこそこ楽ですが、場合によっては特定の座標から開始してパスの向きも逆方向にしたい場合があると思います。
Inkscapeは開始地点の座標変更はできないらしいですが、条件付きで開始座標の変更が可能です。

赤と青の頂点を始点と終点にしたい場合、まずこの2つの頂点が「直線である」ことが条件です。
この直線(もしくは2つの座標)をノードツールで選択した後、ツールバーの赤枠で囲まれた 2個の非間のセグメントを削除 をクリックします。すると以下のように線が消えます。

今まで始点と終点は Z で繋がっていましたが、先程の操作をすることで Z が消えて始点と終点が繋がらなくなったので線が消えます。(塗りつぶしは線の中なので特に影響なし。)
この操作を行うことで明示的に始点と終点となる頂点を変更したことになります。

ここで重要なのはどちらが始点なのかは決めることができず、デフォルトでは今までのパスの向きで決まります。
始点と終点が自分の望む逆だった場合、上のメニューの パス向きを逆に をクリックすることでパスの方向が変わり、終点と始点が入れ替わります。

始点が確定したら、もう一度始点と終点を選択し、2個の非端点ノード間のセグメントを削除 の左にある 選択した端点ノード同士を新しいセグメントで連結 をクリックすることで再度 Z が付与され元に戻ります。(一応つなぎ直しておきましょう)

では最後にパスの変更を見てみましょう。まずは最初の状態です。

M 100,0 H 50 L 0,20 V 50 C 0,77.614237 22.385763,100 50,100 77.614237,100 100,77.614237 100,50 Z

ここからセグメントを削除することで始点と終点を無理やり変更すると以下のようになります。

M 0,20 V 50 C 0,77.614237 22.385763,100 50,100 77.614237,100 100,77.614237 100,50 V 0 H 50

ここでパスの向きを逆にすると始点と終点が入れ替わります。0,2050,0 になり最後の座標指定もそれに合わせた変更が行われています。

M 50,0 H 100 V 50 C 100,77.614237 77.614237,100 50,100 22.385763,100 0,77.614237 0,50 V 20

最後に始点と終点の間のセグメントを連結することで始点を変更したパスになります。

M 50,0 H 100 V 50 C 100,77.614237 77.614237,100 50,100 22.385763,100 0,77.614237 0,50 V 20 Z

その他

px基準の新規作成(テンプレートの追加)

一応Inkscapeにはテンプレートがあって px 基準のものもあります。

ですが残念ながらページのフォーマットは mm 基準なので結局やることはあまり変わりません。

ですがInkscapeはテンプレートを自作することができます。初期設定を済ませた空のSVGができたら、ファイルテンプレートを保存 をクリックしてください。

このときに デフォルトテンプレートとして設定 にチェックを入れると、次回以降の新規作成時にこのファイルをベースにしてくれます。あまり印刷物などに使わないならデフォルトを変更してしまっても良いと思います。

まとめ

Webで素材として使いやすいSVGを作るためのコツをまとめました。主に重要なのは以下です。

  • パスは絶対座標で保存する
  • SVGの設定は必ず最初に行う
    • mm の世界から px の世界にする
    • viewBox とSVGのサイズを一致させる
  • パス加工のために座標の読み方を知る
    • また加工しやすいように始めから始点を考えて作るか、条件を満たす場合始点終点を変更することで利用しやすい形にする
  • テンプレートの追加が可能
    • Web素材用途がメインなら事故が発生する前にデフォルトのテンプレートを自作・登録した方が良い

SVGは美しい劣化のない画像を提供でき、可変UIのパーツとして使うことも可能な便利な画像形式です。Inkscapeを使って快適な素材作りをしていきましょう。

Discussion