読者です 読者をやめる 読者になる 読者になる

SVG2で検討されているプロパティ

CSS Property Advent Calendar 2013の12日目の記事です。

もともと:futureや:pastなどを考えていたのですが、9日目にTime-dimensional擬似クラスについて調べてみたで書かれています*1ので、今日はSVG 2で検討されているプロパティを2つ見てみます*2

paint-orderプロパティ

SVGの描画モデルでは、図形やテキストは、まずfillが描画され、次にstrokeが描画され、その上にmarkersが描画されます(SVG 1.1ではRendering ModelのPainting shapes and textを参照)。

ですが、時と場合によってはこの順番を変えたい時があります。特にfillとstrokeの順番を変えたいときがあります。strokeは、strokeの中心がオブジェクトの縁(アウトライン)にあうように描画されるので、太くすればするほど内側にも食い込んできます。そのためテキストに太めのstrokeをつけたい場合には、大きなstroke-widthを指定するとfillがstrokeで隠れてしまう、というとても残念な結果になります。


図1:strokeプロパティだけを指定するとstrokeがオブジェクトの内側に食い込んできてしまう。それを回避するためにuse要素を使ってテキストをコピーしたり、フィルターを利用してきた。paint-order:strokeを指定するとstroke、fillの順に描画できる。

これを回避するには、fillだけ指定した要素の後ろに、strokeを指定した要素をもう1つおいたり、フィルターを使ったりしてきました。

<!-- use要素を使ってテキストを重ね書き -->
<use xlink:href="#text" style="fill:none; stroke:#fff; stroke-width:8px; ..." />
<text x="160" y="192" id="text" style="fill:#000">World Wide Web</text>

<!-- filterを使ってstrokeのようなものをつける -->
<defs>
  <filter id="filter">
    <feMorphology in="SourceAlpha" operator="dilate" radius="3" result="morph" />
    <feFlood style="flood-color:#fff" result="flood" />
    <feComposite in="flood" in2="morph" operator="in" result="composite" />
    <feMerge>
      <feMergeNode in="composite" />
      <feMergeNode in="SourceGraphic" />
    </feMerge>
  </filter>
</defs>
<text x="160" y="256" style="fill:#000; filter:url(#filter);">World Wide Web</text>

しかしSVG 2では、paint-orderプロパティによって描画の順番を変えることができるようになる見込みです。現在のところ、このプロパティには描画したい順番にキーワード(fill、 stroke、markers)を記述します。記述しなかったプロパティは、fill、stroke、markersの順に描画されるので、strokeを一番最初に描画して残りは通常どおりで良い、という場合にはpaint-order:stroke;と記述すれば十分です。

<!-- SVG 2ではpaint-orderプロパティを指定するだけ -->
<text x="160" y="320" class="stroke" style="paint-order:stroke; fill:#000 stroke:#fff; stroke-width:8px; ...">World Wide Web</text>

このpainte-orderプロパティはGeckoBug 838805)とBlink(Issue 223766)で実験的にサポートされています。Geckoではsvg.paint-order.enabledをtrueに、Chromeでは「試験運用版のウェブ プラットフォームの機能を有効にする。」必要があります。

vector-effect

SVGではオブジェクトを拡大したり縮小したりといったことが簡単にできます。これまではSVGではオブジェクトの全てが一律に拡大・縮小されてきましたが、SVG 2では拡大縮小してもstrokeを一定に保つことができるようになる見込みです。具体的にはSVG Tiny 1.2にもあるvector-effectプロパティを使います。strokeを一定に保ちたいオブジェクトにvector-effect: non-scaling-stroke;を指定します。

これがどう役に立つのかですが、こういう例はどうでしょうか。strokeだけ指定された星を画面上にたくさん配置します。この時、それぞれの星は大きさが異なりますがstrokeは一定にしたいとしましょう。GUIで例えるならば、画面上にスタンプで星をはりつけていくイメージです。そして、適当にグループ化を行い、その後拡大・縮小を行ったものと考えてください。

ともあれ、大きさが異なるだけであれば星の形は1つだけ定義して、後はクローンを配置しておけば良くなります。

<defs>
  <path id="star" d="..." style="stroke:#fff; strok-width:3px;" />
</defs>
<g>
  <use xlink:href="#star" x="..." y="..." />
  <g transform="scale(0.5)">
    <use xlink:href="#star" x="..." y="..." />
    <g transform="scale(1.25)">
      <use xlink:href="#star" x="..." y="..." />
    </g>
  </g>
  <use xlink:href="#star" x="..." y="..." transform="scale(0.75)" />
</g>

ところが、単純にクローンを配置するだけでは拡大・縮小(transformのscale)の影響を受けてstrokeの太さが変わってします。しかし、vector-effect: non-scaling-stroke; を指定するとstrokeの太さは拡大・縮小の影響を受けません。

<defs>
  <path id="star" d="..." style="vector-effect: non-scaling-stroke; stroke:#fff; strok-width:3px; " />
</defs>

自分で書いていてあまり良いユースケースではないなあと思いますが、ともあれstrokeを一定に保つことができます。

さて、このvector-effectプロパティはGeckoBug 528332)とWebKitBug 31438)およびWebKitから派生したBlinkでサポートされています。

おわりに

SVG 2では、ここで挙げた以外の機能の追加も検討されており、既存のプロパティの拡張も検討されています。例えば、fillやstrokeプロパティに複数の値をカンマ区切りで指定して、fillやstrokeを重ね塗りすることもできる(本稿執筆段階ではSVG2 Editor's Draftの11.2. Specifying paintにあります)ようになる見込みです(SVG 1.1などではfillやstrokeに空白区切りでフォールバックを複数指定することはできます)。

またフィルタやマスク、合成とブレンディングはFXTFの仕様を参照する形になる方向ですので、利用できるプロパティや値も増えるでしょう。

来年も、そしてそれ以降もSVG 2の動向が楽しみですね。

*1:余談ですが、WebVTTと擬似クラスでは、OperaFOMS 2012で行ったWebVTTのデモが好きです。:pastを使ったカラオケもあります(が、当時から仕様が微妙に変わっているためOpera 12くらいでしかカラオケは楽しめません)

*2:余談ですが、pointer-eventsfilterはもともとSVGで定義されています。