Inline SVG - トラブルとその対策
2013/10/12 第8回HTML5など勉強会 @kadoppe
自己紹介
● 名前
● 門脇 恒平 @kadoppe
● 職業
● ソフトウェアエンジニア
● 所属
● 株式会社シェアウィズ & HTML5-WEST.jp
本日のスライド
● SlideShare
● http://www.slideshare.net/kadoppe
アジェンダ
● SVGとInline SVG
● なぜInline SVGを使ったのか
● トラブルと対策
● 学んだこと・教訓
SVGとInline SVG
SVGとは
● S: Scalable(拡大可能な)
● V: Vector(ベクター形式の)
● G: Graphic(画像)
● 直訳すると「拡大可能なベクター画像」
特徴1: 拡大・縮小してもきれい
点で画像を表現 線で画像を表現
特徴2: XML形式のフォーマット
● XML要素で円や線などの形を表現
● テキストエディタで編集可能
Inline SVGとは
● HTML5 からの新機能
● HTMLドキュメントにSVGマークアップ
を埋め込み可能に
Inline SVGのサンプル
<html>
<body>
<h1>Inline SVGサンプル</h1>
<svg width=100 height=100>
<circle r=50 cx=50 cy=50 />
</svg>
</body>
</html>
Inline SVGのメリット
● 画像のルックアンドフィールをCSSで
変更可能
● 円の色、線の太さを変える、など
● 画像内の各要素に対するユーザ操作を
JavaScriptでハンドリング可能
● マウスクリック、ドラッグなどに対する処理
利用可能なブラウザ
ブラウザ 対応バージョン
Internet Explorer 9以降
Firefox 3.0以降
Chrome 4.0以降
Safari 3.2以降
iOS Safari 3.2以降
Android 3.0以降
JavaScriptライブラリ
● D3.js - http://d3js.org
● Webページでデータビジュアライゼーション
● Raphaël - http://raphaeljs.com
● Webページ上でベクター画像を動的に生成
WebにおけるSVGの活用事例
● 活用例1
● ロゴやアイコン画像
● 活用例2
● インタラクティブなWebアプリケーション
ロゴやアイコン画像
ロゴやアイコン画像
Webアプリケーション
無料学習サイト ShareWis http://share-wis.com
なぜInline SVGを
使ったのか
ShareWis開発前の要件
● GoogleマップのようなUI上に
コンテンツを配置
● マウス操作に応じて各コンテンツが
アニメーション
● iPadのSafariに対応
● 古いブラウザ(IE 8以下)は非対応
利用する技術の選択肢
● Flash
● Google Maps JavaScript API
(カスタムオーバレイ)
● Inline SVG
利用する技術の選択肢
● Flash
● Google Maps JavaScript API
(カスタムオーバレイ)
● Inline SVG
トラブルと対策
Inline SVGから始まる茨の道
やりたかったこと(1)
text要素に下線を引く
text-decoration属性
<svg witdh=100 height=100>
<text text-decoration=underline x=0 y=20>Hello World</text>
</svg>
下線が表示される
発生したトラブル
Firefoxで下線が
表示されない
原因調査
● Bugzilla@Mozillaにバグ報告あり
● Bug 317196 - Support the 'text-
decoration' property in SVG (underline,
etc.) - https://bugzilla.mozilla.org/show_bug.cgi?id=317196
● 報告日: 2005/11/20
● SVG Textのレンダリング方法の問題
対策1: line要素で下線を引く
● 手順
1. text要素の大きさ(高さと幅)をgetBBox()
メソッドを使って取得
2. 適切な座標にline要素を挿入
解説
var textElm = document.getElementById('text');
var box = textElm.getBBox();
alert(box.height); // text要素の高さ
alert(box.width); // text要素の幅
<svg>
<text x=0 y=20>Hello World</text>
<line x1=X1 y1=Y1 x2=X2 y2=Y2 />
</svg>
高さと幅をもとに
line要素の始点・終点を計算
Hello World
【朗報】
対策2: 新しいFirefoxをつかう
● Firefox 25
● 2013/10/29 リリース予定
● 待望のtext要素の下線表示に対応
やりたかったこと(2)
要素の大きさを取得する
getBBox()メソッド
var textElm = document.getElementById('text');
var box = textElm.getBBox();
alert(box.height); // text要素の高さ
alert(box.width); // text要素の幅
発生したトラブル
Firefoxで
NS_ERROR_FAILURE
NS_ERROR_FAILURE?
意味
「なんかエラー」
NS_ERROR_FAILURE?
23 /* Returned when a function fails */
24 ERROR(NS_ERROR_FAILURE, 0x80004005),
mozilla/xpcom/base/ErrorList.h
何らかの原因で関数の実行が失敗した時のエラー
原因調査
● Bugzilla@Mozillaにバグ報告あり
● Bug 612118 - SVGLocatable.getBBox()
fails unless the SVG element it is applied to
is attached and rendered - https://
bugzilla.mozilla.org/show_bug.cgi?id=612118
バグ再現方法
● 方法1
● ドキュメントにまだ追加されていない
要素に対してgetBBox()メソッドを呼び出す
● 方法2
● ドキュメントに追加されているが
display: none な要素に対して
getBBox()メソッドを呼び出す
対策1: try-catch文で囲む
try {
var textElm = document.getElementById('text');
var box = textElm.getBBox();
} catch (e) {
alert(e);
}
NS_ERROR_FAILUREはtry-catchで補足可能
対策2: displayプロパティを確認する
var textElm = document.getElementById('text');
if (getComputedStyle(textElm, null).display !== ‘none’) {
var box = textElm.getBBox();
}
getBBox()メソッドを呼び出す前に
display: noneでないことを確認
やりたかったこと(3)
テキストを折り返す
text要素の文字列を折り返す
これは折り返されたテキストです。ラーメ
ンが食べたい。無鉄砲のラーメンが食べた
いです。
横幅を指定してテキストを自動で折り返し
発生したトラブル
テキストが折り返されない
これは折り返されたテキストです。ラーメンが食べたい
原因調査
● SVGの仕様です
● SVGのtext要素はHTMLのdiv要素みたいに
自動で文字列を折り返してくれない
● textArea要素
● 自動折り返しに対応したtext要素
● まだどのブラウザも対応していない
対策: tspan要素を使う
<svg witdh=100 height=100>
<text>これは折り返されたテキストです。ラーメンが食べたい。</text>
</svg>
<svg witdh=100 height=100>
<text>
<tspan>これは折り返されたテキストです。</tspan>
<tspan>ラーメンが食べたい。</tspan>
</text>
</svg>
文字列を複数の要素に分割
詳しい手順
1.文字列を単語単位に分割
2.指定された幅を超えるまで
1単語ずつtspan要素に追加
3.超えたら末尾の単語を削除して
tspan要素の位置を調整
4.2に戻る
幅を超えたかどうかの判定
for (var i = 0; i < words.length; i++) {
tspanEl.textContent += word
if (tspanEl.getComputedTextLength() > width) {
// 超えた
} else {
// 超えてない
}
}
getComputedTextLength()で幅を取得
結果こうなる
<svg witdh=100 height=100>
<text>
<tspan x=0 dy=”18”>これは折り返された</tspan>
<tspan x=0 dy=”18”>テキストです。ラー</tspan>
<tspan x=0 dy=”18”>メンが食べたい。</tspan>
</text>
</svg>
やりたかったこと(4)
縁取り文字を表示する
縁取り文字
ラーメンが食べたいラーメンが食べたい
発生したトラブル
縁取り文字を簡単に
表示できない
原因調査
● text要素に縁取りを追加する
便利なプロパティが存在しない
方法1: text-shadow
<svg witdh=100 height=100>
<text>ラーメンが食べたい</text>
</svg>
text {
fill: white;
text-shadow: -1px -1px black,
1px -1px black,
-1px 1px black,
-1px -1px black;
}
・縁が汚い
・Firefoxでは縁が表示されない
方法2: text要素を重ねる
<svg witdh=100 height=100>
<text class=”outline”>ラーメンが食べたい</text>
<text class=”body”>ラーメンが食べたい</text>
</svg>
.outline {
stroke: black;
stroke-width: 3px;
}
.body {
fill: white;
}
text要素が2つあるのが
文書の意味的におかしい
● SVG Filter
● SVG画像に様々な効果を適用する機能
● ドロップシャドウ、色の変換など
● まだ試していないので実現できるか不明
方法3: SVG Filterを使う
やりたかったこと(5)
ズーム時に文字サイズを
固定する
ズームの仕組み
<svg witdh=100 height=100>
<g transform=”scale(倍率)”>
<circle />
<circle />
<circle />
<text />
<text />
</g>
</svg>
全体を囲むg要素のtransform属性を操作する
ズーム
発生したトラブル
ズーム時に文字サイズが
変化してしまう
テキストテキスト
テキスト
テキスト
OK
NGNG
原因調査
● SVGの仕様です
対策: 文字サイズを動的に変更
$elm.on(‘mousewheel’, function() {
// 略
var scale = 現在の倍率
var originalFontSize = 24;
var currentFontSize = originalFontSize / scale;
// 略 (text要素のfontサイズ変更処理)
}
問題点:text要素の数が多いと処理が重たくなる
対策: 文字サイズ変更を遅延
var timer = false;
$elm.on(‘mousewheel’, function() {
if (timer !== false) {
clearTimeout(timer);
}
timer = setTimeout(function() {
var scale = 現在の倍率
var originalFontSize = 24;
var currentFontSize = originalFontSize / unscale;
// 略 (text要素の文字サイズ変更処理)
}, 200);
}
マウスホイールイベントが連続して発生しているときは
文字サイズを変更しない
学んだこと
SVG Textには
トラブルがいっぱい
●SVGの仕様では(シンプルに)実現でき
ない表現がある
●ブラウザ間の差異が存在する
教訓
●SVGで(シンプル)に実現できないこと
はしない
●ブラウザ間の差異を許容する
そうしないと…
● コードが必要以上に複雑になる
● パフォーマンス低下・バグ発生の原因に
そんなInline SVGは
使っていて楽しい技術
みんなもっと
使って情報交換しよう
おしまい

Inline SVG - トラブルとその対策