SVGで縦書きテキストを使おうとしたらIEでずれる件について

text要素を縦書きにしたSVGをIEで見ると座標がずれて表示されるという問題にぶち当たったので、だいぶニッチな話題だと思いますが一応記録しておきます。

チャート図をSVGで表示しようと思った

今関わってる案件で、ちょっとしたチャート図的なものを入れたかったのですよね。まぁイメージ的にこんな感じのやつです。

chart-google

 

で、最初は普通にgifとかpngで貼ろうかと思ったんですけど、インラインSVGにしちゃった方がいいかなーと思いまして。図の中にテキスト部分も多いので、その方が選択してコピペしたりもできるし、あとテキストがHTMLの一部になってると検索とかにも有効なのかなーと思いまして。

で、イラレにあったデータをSVGに書き出して、そのソースをHTMLに丸ごとコピペして置いてみたんですよ。

まずそもそもIEだとインラインSVGは高さ150pxのサイズにされてしまうという仕様(バグではないらしいです。詳しくは「IEのインラインSVGの固有サイズ表示をSVG1.1仕様に修正する方法」を参照)があるのですが、そこは「IEにも対応したレスポンシブSVGの作り方」という記事を参考に、アスペクト比分のpaddingを指定することで対応。

しかしそれでも解決できない部分がありまして、縦書きテキストの座標がどうもおかしいのですね〜。

縦書きの場合に座標の基準点が違う?

スペース的な都合でテキストの一部を縦書きにしてみたんですよ。だいぶ簡略化したものですが、具体的にはこんな感じのものです。

test-chrome

 

ソースでいったらこんな感じです。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37.25 97.67">
  <defs>
    <style>
      .a{
        fill:#fff;
        stroke:#000;
        stroke-miterlimit:10;
      }
      .b{
        writing-mode:tb;
        text-orientation:upright;
        glyph-orientation-vertical:0deg;
        font-size:18px;
      }
    </style>
  </defs>
  <title>test</title>
  <rect class="a" x="0.5" y="0.5" width="36.25" height="96.67"/>
  <text class="b" transform="translate(18.63 21.83)">テスト</text>
</svg>

これをIEで表示させるとこうなるのですよね〜。

test-ie

 

ふーむ。何だこれは。ちなみにIEの9,10,11(つまりSVG対応している全て)とEdge、全てこの表示でした。

ま、テキスト位置ってのはtransform="translate(18.63 21.83)で座標指定しているわけなので、おそらく基準点が違うんだろうと思われるのですよね。つまりChromeやFirefoxだとx座標の基準点がテキストの中央なのに対して、IEは左であると。

jQueryで結構簡単に直せました。

果たしてどうしたもんかなーと思ってググってみたんですが、意外にも同じような状態で悩んでいる人というのは見当たらず。唯一見つかったのは、ちょっと微妙に状況は違うけれど「SVGでテキストの縦位置(baseline)を調整する方法を調べてみた」という記事でした。

こちらの記事ではjsで座標を設定し直して解決ということで、自分のケースもこれでいけるかな、と。つまりIEの時だけテキストの幅の分だけ座標をマイナスしておけば良いわけで。

で、座標なんですがtransform="translate(18.63 21.83)だとjsで取得しづらいので、まずはここを単純にx="18.63" y="21.83"と書き換えるところから始めます。
(言うまでもないかもしれないですが、この作業はそんなに数が多くなくてさほど手間じゃないという場合のみ行いましょう。大量にある場合はこれをやるよりもむしろスクリプト側で上手く抽出して対処した方が良いと思いますね。)

で、その後はjQueryでこんな感じの処理をしたらOKでした。

var useragent = window.navigator.userAgent.toLowerCase();
if ( !( useragent.indexOf( 'msie' ) < 0 && useragent.indexOf( 'trident' ) < 0 ) ||
  useragent.indexOf( 'edge' ) > 0 ) {
  $( function () {
    var $e1 = $( "text.b" );
    $e1.each( function () {
      var width = this.getBBox().width;
      var x = $( this ).attr( "x" );
      var ie_num = Math.floor( ( Number( x ) - width / 2 ) * 100 ) / 100;
      $( this ).attr( "x", ie_num );
    } );
  } );
}

SVGでも普通にjQueryでアクセスできたので楽でしたね。

というわけで、もし同じような感じで困っている方いましたら参考にどうぞー!

この投稿へのコメント

コメントはありません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


この投稿へのトラックバック

トラックバックはありません。

トラックバック URL