inline svg - トラブルとその対策
DESCRIPTION
2013/10/12 に大阪で開催された「第8回HTML5など勉強会」で利用した資料ですTRANSCRIPT
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アプリケーション
ロゴやアイコン画像
ロゴやアイコン画像
なぜ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?
23 /* Returned when a function fails */24 ERROR(NS_ERROR_FAILURE,
0x80004005),
意味
「なんかエラー」
NS_ERROR_FAILURE?
23 /* Returned when a function fails */24 ERROR(NS_ERROR_FAILURE, 0x80004005),
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は使っていて楽しい技術
みんなもっと使って情報交換しよう
おしまい