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

8,276 views

Published on

2013/10/12 に大阪で開催された「第8回HTML5など勉強会」で利用した資料です

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

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

×