SVG pointer-events 2017

SVG Advent Calendar 2017の20日目。

pointer-events: noneについては 過去書いたとおり、なかなかどうして、好きすぎて困っている。

また、pointer-eventsプロパティーが大好きなみなさんなら、これがSVG由来のものだというのは当然ご存知だろう。HTMLの要素に指定が有効な値は実質autononeだけだが、SVGの要素についてはその限りではない。 MDNを見ると以下の値がある

  • auto
  • none
  • fill
  • stroke
  • all
  • painted
  • visibile
  • visibleFill
  • visibleStroke
  • visiblePainted

これらの値によってポインターイベントのターゲットにどのように変化するのか、ひとまずpointer-eventsプロパティーに関わるSVGの特徴を理解する必要がある。

塗りと線

SVGの図形は座標・方向・向かう強さで表すことができ、それはベクターツールで言うところの「アンカーポイント」と「ハンドルの向き」「ハンドルの長さ」である。アンカーポイントとハンドルが描く軌跡がパスであり、SVGに変換した際に図形の輪郭となる。

ベクターツールにおいて、繋がったアンカーポイントの連続は「線」と呼ばれる。線の始点と終点を結んだ領域は「塗り」と呼ばれる。それをSVGに変換すると「塗り」はfill、「線」はstrokeとなる。

可視性

SVGはvisibilityプロパティーで可視か不可視かを指定できる。CSSと同じく、visiblehiddenを値に持つ。visibility = visibleの時はSVGは可視で、visibility = hiddenの時は不可視となる。

塗りと線に指定できる値

概ね CSSの<color>型と同じ。厳密には <paint>型なのだが、実質的に<color>型という解釈でいいと思う。

https://www.w3.org/2011/07/29-svg-minutes.html#item02 https://triple-underscore.github.io/SVG11/types.html#DataTypeColor

SVGの装飾方法

SVGにスタイルを当てる方法はだいたい3つある。フィルターやマスクなど考えるとその限りではないが、まぁだいたい3つでいいと思う。

<!-- style属性での指定 -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="30" style="fill: gold; stroke: black; stroke-width: 2" />
</svg>
<!-- style要素での指定 -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <style>
    circle {
      fill: gold;
      stroke: black;
      stroke-width: 2
    }
  </style>
  <circle cx="50" cy="50" r="30" />
</svg>
<!-- プレゼンテーション属性での指定 -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="30" fill="gold" stroke="black" stroke-width="2" />
</svg>

(大まかに言って)このいずれかの装飾で、可視性/塗り/線を設定する。

早見表

そしてpointer-eventsプロパティーと可視性/塗り/線の関係は次の通りとなる。

pointer-events値\依存する項目 visibility値 fill値 stroke値 簡単な解説
auto visible none以外 none以外 visiblePaintedと同じ
none -- -- -- 何もターゲットにならない
fill -- -- -- 塗り部分がターゲット
stroke -- -- -- 線部分がターゲット
all -- -- -- 塗りと線がターゲットになる
painted -- none以外 none以外 値がnone以外なら塗りも線もターゲット
visible visible -- -- 可視であればnone値でも塗りと線がターゲットになる
visibleFill visible -- -- 可視であればnone値でも塗りがターゲット
visibleStroke visible -- -- 可視であればnone値でも線がターゲット
visiblePainted visible none以外 none以外 可視でかつnone値以外の塗りと線がターゲット

pointer-eventsに指定できる値は他にinheritがあるが、これはただの継承なので表には含めない。

こう見ると、だいたいはfillstrokeの値には左右されないが、 painted / visiblePaintedだとnone以外でないといけなくなることがわかる。

noneとtransparentとopacityとvisibility

visibility:hidden以外でも「見えない」指定は可能だ。色指定なしのnone、透明色指定のtransparent、全透過のopacity: 0がある。

しかし仕様によればtransparentopacitypointer-eventsプロパティーの挙動には影響を与えない。影響を与えるのは「色指定あるなし」と「可視性あるなし」のみということだ。

実食

仕様をあれこれ説明してもしっくりこないと思うのでさっさと触っていこうと思う。SVGでfillとstrokeが色付きとnoneの4パターンのpath要素を用意し、プルダウンでpointer-eventsvisibilityopacityを切り替えられるサンプルを作った。path要素あたりをクリックし、イベントが制限されていなければ黄色いエリアに「Clicked!」と文字が出る(その後消える)。

実装ロジックとしては、プルダウンを選択するとそれぞれのプレゼンテーション属性値が各SVGに書き込まれる仕組みになっている。

触ってみると気づくことがある。「fill = noneのみ」とか「stroke = noneのみ」をターゲットにしたpointer-eventsの制御ができない。これは仕様にないので仕方がない。しかし次の3点は仕様と違っておかしい。

  • strokeでもfill=noneなSVGをクリックでイベントが発火する
  • fillでもstroke=noneなSVGをクリックでイベントが発火する
  • paintedでもfillもstrokeもnoneなSVGでクリックイベントが発火する

仕様とはいったい? 特に3つ目は不思議な動きをする。これにvisibility = hiddenにしてみると、期待通りfill = none / stroke = noneなSVGはクリックが発火しなくなるのだ。paintedvisibilityの値で挙動が変わらないはず。手元のGoogle Chrome 63、Safari 11.0、Firefox 57.0.1で確認したがいずれも同じだった。

他の値の組み合わせはどうやら仕様通りに実装されていそうだった。全部のブラウザでは見ていないので何か差があるかもしれない。

誰か何か知っていることがあったら 教えてください


SVG怖くなってきた。