ベクターイメージはWebの世界でますます一般的になりつつあります。SVGは標準的なイメージに代わる、スケーラブルでレスポンシブかつ高速な代替手段を提供します。SVGの主なメリットは、どのデバイスからでもくっきり鮮明に見え、通常はファイルサイズを小さく抑えられるという点です。
いまやSVGは特定のブラウザーに依存せず、サポートは広範囲であり、すべてのモダンブラウザーはイメージタグで指定されたSVGや、CSSのbackgroundプロパティの一部として指定されたSVGをサポートしています。
WordPressを使用しているなら、メディアライブラリーがどのようにメディアアセットの中央リポジトリとして動作するかはよく知っていることでしょう。ファイルのアップロードを処理するマネージャーはもともと許可されたファイル形式のリストに制限されています。
本記事ではSVGメディアのサポートを追加する方法、またWordPressによるSVG全般の処理、取り扱い、表示を全面的に改善する方法に注目します。
裏話——WordPressでSVGがサポートされないのはなぜ?
みなさんはおそらく、なぜSVGがWordPressのサポートファイル形式のリストに載らないのか疑問に思っているでしょう。その答えのすべてはセキュリティの観点によるものです。
SVGは実のところベクターイメージであり、ほかのフォーマット(png、jpg、gifなど)のようなラスターイメージとは異なります。そのため、悪質なJavaScriptの実行に使用される可能性があります。Bjørn Johansenの記事では実際どのようなことが起こるか指摘しています。簡単に言うと、JavaScriptがSVGに埋め込まれる可能性があり、SVGの表示時に実行されてしまうということです。
WordPressの中心的な開発者たちは3~4年もの長い間SVGの導入について議論していますが、争点となっているのはSVGによって引き起こされる可能性がある潜在的なセキュリティリスクについてです。メディアマネージャーはサイト全体の管理者からエディターやコントリビューターまで、さまざまなレベルで使用されます。つまり、(無意識にあるいは意図的に)悪質なSVGファイルをサイトへアップロードできる可能性のあるメンバーがたくさんいるということです。
しばらくはこれに関する動きは特にないと考えられます。つまり残念ながらSVGは当分デフォルトのアップロードオプションにはなりません。
SVGフォーマットを許可済みのファイル型式に追加する
幸いWordPressは柔軟性が高いので、メディアライブラリーへのアップロードを許可するファイル形式を編集できます。upload_mimesフィルターに接続し、SVGのサポートを追加するだけです。このコードを子テーマ内のfunctions.phpファイルに追加するか、プラグインとして生成できます。
//add SVG to allowed file uploads
function add_file_types_to_uploads($file_types){
$new_filetypes = array();
$new_filetypes['svg'] = 'image/svg+xml';
$file_types = array_merge($file_types, $new_filetypes );
return $file_types;
}
add_action('upload_mimes', 'add_file_types_to_uploads');
上のコードによって、許可済みのファイル形式の配列へ接続し、許可済みのファイル形式としてSVGの拡張子(image/svg+xml)を追加します。
WordPressでSVGイメージのアップロードを有効にするために必要なことは以上です。メディアライブラリーに移動すればSVGをライブラリーへ直接ドラッグ&ドロップでき、さらにSVGが標準ファイルとして表示されるようになっています。
メディアライブラリー内のSVGの表示の改善
SVGは標準のファイル形式ではないため、表示や処理は「そのまま」です。つまり、SVGはすべて管理ダッシュボード内で動作しますが、必ずしも最適なユーザーエクスペリエンスが得られるわけではありません。
SVG最適化に関して欠けている点の1つはライブラリーの「グリッド表示」です。
グリッド表示でSVGを表示すると、それぞれのSVGは一般的なプレースホルダーとして、デフォルトアイコンとファイル名が表示されます(前の図のように表示されます)。このような動作では、SVGのファイル名を覚えていない限りどれがどのイメージなのか分からずあまり役立ちません。表示されるべきはイメージ自身であり、プレースホルダーではありません。
もっと見た目に分かりやすいものが得られるように、これから「グリッド」の最適化について説明します。
グリッド表示におけるSVGの手動更新
WordPressのメディアライブラリーは動的に構築されており、ページの読み込みに応じて非同期にアップロードされたファイルをすべて取得し、スクロールに応じてさらにファイルを取得します。ファイルの表示方法を調整するために接続可能なフィルターやアクションは存在しません。JavaScriptを使用して手動で変更する必要があります。
いま探しているのはSVG用に表示されたデフォルトアイコンとテキストをイメージそのものに置き換えるためのものです。置き換えることでSVGのライブラリーの管理がもっと簡単になります。
この方法では、メディアライブラリー内の各アタッチメントで関数を実行します。この関数は適切なSVGイメージに対してのみ実行され、AJAXがカスタムPHP関数を呼び出すトリガーになります。この関数内部でデフォルトアイコンを置き換えるためのSVGイメージのURLを返します。
Mutation Observer
メディアライブラリーに関する最初のやっかいな点の1つはすべてが動的に構築されているということです。ページの読み込み時または確実にsetTimeoutに合わせて関数を実行できません。なぜなら、いつ準備できるか分からないからです。DOMが更新されページにアタッチメントが追加されたときに関数を実行する必要があります。
Mutation Observerを実行する代表的なユースケースです。
「Mutation Observer」によって、開発者がDOMの変更に反応し、そのノードで発生したすべての変更点(Mutation)のリストを取得する手段が得られます。Mutation Observerは廃止予定のmutation eventsを置き換え、平等かつ広範囲にブラウザーをサポートし、IE11までのブラウザーを含むすべてのモダンシステム上で動作します。
管理CSSを微調整してSVGの表示を改善する
WordPressのバックエンド全体でSVGイメージの見栄えを良くするために少し整形が必要な箇所がいくつかあります。実際に注意が必要なのは、旧式のブラウザー(IE8~IE10)でSVGがリサイズされることは好ましくなく、イメージとして表示される際にheightおよびwidth属性を設定する必要があることです。表示目的のため、また多くのブラウザー向けに、このようにCSSを微調整します。
よくある問題は、heightやwidthの値が割り当てられていないSVGが壊れてしまう(表示されなくなる)ことです。SVGのwidthとheightを100%に設定すれば問題は解決します。
メディアライブラリーのグリッドレイアウト
新しいデフォルトのグリッドレイアウトによって、インターフェイスを使ってアタッチメントを管理したり閲覧したりするのがより簡単になります。とても便利で役に立つのですが、SVGに関してはあまり有効ではなく、SVGはファイル名のついたアイコンが表示されてしまいます。
プレビューとしてSVGイメージ自身を表示させるには、SVGのコレクションの管理をもっと簡単にしなければなりません。
問題点は、ライブラリーが動的に構築されていること、つまり思い通りに動作させるためにはJavaScript内ですべてを調整する必要があるということです。ここで、Mutation Observerが役に立ちます。新しく追加されたすべてのDOMノードを探して、それらがアタッチメントかどうか、意図した方法でそれらを操作できるのかチェック可能です。
解決策は、アタッチメントのURL全体を取得するためにJavaScriptを用いてアタッチメントを検出しIDをPHPに返す作業が含まれます。そのため、WordPressのAJAX機能に接続し、以下のようにカスタム関数を登録する作業が必要になります。
//call our function when initiated from JavaScript
add_action('wp_AJAX_svg_get_attachment_url', 'get_attachment_url_media_library');
wp_AJAX_{name_of_action}フックはカスタムのAJAX呼び出しを生成するのに使用されます。つまり、WordPress AJAXが呼び出されたときは常に、このフックに登録された同じ名前のactionを探します。あとでAJAX関数を呼び出す場合は、svg_get_attachment_urlと呼ばれるアクションを渡し、次にget_attachment_url_media_library関数を呼び出します。
WordPress AJAXを目にするのがはじめてであれば、Codexのドキュメントから読むことをお勧めします。
アタッチメントのURLを返す関数は以下のようにとてもシンプルです。
//called via AJAX. returns the full URL of a media attachment (SVG)
function get_attachment_url_media_library(){
$url = '';
$attachmentID = isset($_REQUEST['attachmentID']) ? $_REQUEST['attachmentID'] : '';
if($attachmentID){
$url = wp_get_attachment_url($attachmentID);
}
echo $url;
die();
}
すでにPHPの要素はソートされているので、JavaScriptコンポーネントに入って次のようにします。動作させるため、キューに入れられた一般向けのスクリプトに以下を追加する必要があります。
動作させるには、待機状態の公開されたスクリプトに以下を追加する必要があります。
//create a mutation observer to look for added 'attachments' in the media uploader
var observer = new MutationObserver(function(mutations){
// look through all mutations that just occured
for (var i=0; i < mutations.length; i++){
// look through all added nodes of this mutation
for (var j=0; j < mutations[i].addedNodes.length; j++){
//get the applicable element
element = $(mutations[i].addedNodes[j]);
//execute only if we have a class
if(element.attr('class')){
elementClass = element.attr('class');
//find all 'attachments'
if (element.attr('class').indexOf('attachment') != -1){
//find attachment inner (which contains subtype info)
attachmentPreview = element.children('.attachment-preview');
if(attachmentPreview.length != 0){
//only run for SVG elements
if(attachmentPreview.attr('class').indexOf('subtype-svg+xml') != -1){
//bind an inner function to element so we have access to it.
var handler = function(element){
//do a WP AJAX call to get the URL
$.AJAX({
url: AJAXurl,
data: {
'action' : 'svg_get_attachment_url',
'attachmentID' : element.attr('data-id')
},
success: function(data){
if(data){
//replace the default image with the SVG
element.find('img').attr('src', data);
element.find('.filename').text('SVG Image');
}
}
});
}(element);
}
}
}
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
上のコードがどのように動作するか概要を説明します。
- 新しい「Mutation Observer」を生成してbody要素上のすべての変更箇所を認識する。また、動的にロードしたアタッチメントを探す(最初はライブラリーとして開き、その後スクロールしてさらにロードする)
- 追加されたすべてのDOM要素を調査し、elementを割り当てる。この要素で、現在の要素が正しいかどうか確認するために比較する
- 要素がクラスを持っているかどうか(標準のテキスト要素をループするなどして)チェックして、そのクラスを使用してattachmentというクラス名を持つかどうか確認する。各アタッチメントはそのクラス名を持っているので、それが探している要素ということになる
- アタッチメントにいる時点でもう1つチェックを実施し、いま単一のアタッチメントにいるかどうか確認する(たまたまアタッチメントに関連するがアタッチメント自身ではないほかの要素上にいるということもあるかもしれない)。そして'attachment-previewクラスを使用して子を検索すると、いま正しい場所にいるかどうかが分かる
- 次に、アタッチメントプレビューがsubtype-svg+xmlクラスを持つかどうかチェックする。SVGイメージのみに対して機能を実行するため
- ここは重要。各アタッチメントに対して新しい関数を生成し、参照としてelementに渡す。そして内部でAJAX呼び出しを生成する。ここでdata-idを取り出し、以前登録したPHP関数を呼び出す。このアクションはsvg_get_attachment_urlと呼ばれ、wp_AJAX_svg_get_attachment_urlアクションに接続された関数へ関連付けられる
- AJAX関数はPHPオブジェクトのURLを取得してアタッチメントIDをPHPへ返す。AJAX呼び出しを返すと、それがelementに付加され表示が更新される
ここに美しく動作するパーツがいくつかありますが、多くは関数を実行するための正しいDOM要素を見つける処理を実施します。最終的に、SVGイメージがグリッドで表示されるのが確認できます。
■追記
- 良い点は、最初にライブラリーがロード、スクロールされるとき(新しいアタッチメントがロードされるとき)、またライブラリーへ新しいアタッチメントを追加するとき、アップロードするときなど、DOMが変更され次第いつでも実行されること
- SVGイメージが表示されるスピードは完全にWordPressに依存する。各SVGアタッチメントが自身のAJAX呼び出しを実行し、さらにURLを返す必要があり、ブラウザーに新しいイメージをロードさせるためだ。実際のところ、2回速度が落ちるのを待つことになる。こうした動作も含めて、全部が表示されるのに数秒かかる場合がある
- もう1つの興味深い「特徴」(問題)は新しいイメージをメディアライブラリーに追加したときに、すべてのアタッチメントが再読み込みされる、つまりAJAXの取得およびイメージのレンダリングが再度実行される。メディアライブラリーの動的処理の一部のため、調整不可能
メディアライブラリーのコラムレイアウト
従来のコラムレイアウトを使用してメディアライブラリーを表示する場合も、SVGが正しく表示されないことに注意してください。
このように表示される理由はそれらを整形するためのheightやCSSを保持していないためです。以下のように、先ほど取り上げたイメージセクションと同様に調整する必要があります。
/*adjust SVG images when displayed inside media library column view*/
table.media .column-title .media-icon img[src*='.svg']{
width: 100%;
height: auto;
}
このコードでSVGプレビューが整形され、ほかの画像と一列に並んで表示されます。それらがなにかひと目で正確に理解できるようになります。
メディアアタッチメントプレビュー
メディアライブラリーの「グリッド」レイアウトにいる場合、項目をクリックしてアタッチメントメタデータを表示するモーダルウィンドウが表示可能です。それは調整が必要なもう1つの領域であり、SVGが表示されます。
これがデフォルトの表示です。デフォルトアイコンが使用されていることに注意してください。
ほかの領域とは異なり、このモーダルウィンドウの一部としてすでにSVGへのURL全体を保持しており、URLフィールドの下に表示されます。ここで難しいのは情報が手元にあるにもかかわらず動的であるために、アタッチメントプレビューが開かれた(またはナビゲートされた)ときに探すもう1つのリスナーを生成し、適切なSVGをメディアウィンドウに追加する必要があるという点です。
ここで、JavaScriptに戻って以下を追加します。
//Observer to adjust the media attachment modal window
var attachmentPreviewObserver = new MutationObserver(function(mutations){
// look through all mutations that just occured
for (var i=0; i < mutations.length; i++){
// look through all added nodes of this mutation
for (var j=0; j < mutations[i].addedNodes.length; j++){
//get element
var element = $(mutations[i].addedNodes[j]);
//check if this is the attachment details section or if it contains the section
//need this conditional as we need to trigger on initial modal open (creation) + next and previous navigation through media items
var onAttachmentPage = false;
if( (element.hasClass('attachment-details')) || element.find('.attachment-details').length != 0){
onAttachmentPage = true;
}
if(onAttachmentPage == true){
//find the URL value and update the details image
var urlLabel = element.find('label[data-setting="url"]');
if(urlLabel.length != 0){
var value = urlLabel.find('input').val();
element.find('.details-image').attr('src', value);
}
}
}
}
});
attachmentPreviewObserver.observe(document.body, {
childList: true,
subtree: true
});
上のコードがどのように動作するかを以下に示します。
- 新しい「Mutation Observer」を生成してbodyに添付し、すべての変更箇所(子ノードおよびそれらの子すべてを含みます)を探す
- すべてのMutationでループし、ノードを追加する。attachment-detailsクラスを持つか、子孫の1つとして持つ要素を探す。この条件があるわけは、モーダルウィンドウが最初に開かれたときや(nextやpreviousボタンがクリックされて)モーダルウィンドウが更新されるたびに実行されるため。これはトリガー要素自身がattachment-details DOM要素になるよう誘導するからだ
- 正しい場所にいると分かった時点で、URL要素を探してそれを取得する必要がある。それを入手したら、プレビューにSVGを追加する
すべて完了すると、アタッチメントの詳細ウィンドウが次のように表示されます。
フロントエンドでの出力
ソースとしてSVGコンテンツを持つイメージをWordPressが出力する場合、表示されているものがなにも見当たらないことがあります。これはSVGが属性としてまたはCSS内部に正しいwidthおよびheightのセットを保持していない(SVGがサポートされていないと見なされる)ことによって起こります。
widthおよびheightのセットを取得して動作させるにはSVG要素のレスポンシブを適用、または生成するほかのスタイルを追加する必要があります。
/*sets all SVG's to be responsive. displaying at full width*/
img[src*='.svg']{
width: 100%;
height: auto;
}
style.cssに上のコードを追加すると、すべてのSVGイメージは横幅全体に引き延ばされ、(モダンブラウザーにおける)アスペクト比を維持します。
新しいスタイルによって標準SVGイメージがどのように表示されるかを例示しています。
意識すべきことの1つは、セレクターはテーマやほかのプラグインによってオーバーライドされてしまうことです。これらがフロントエンドで適切かどうかを確かめるのはあなた次第です。
最後に
このような変更によってほかのイメージと同様にSVGを使用したりSVGとやりとりしたりできるようになり、どのSVGがどれなのか悩むことなくSVGを素早く閲覧したり取り扱ったりできるようになります。
うまくいけばいくつかの点はSVGがWordPressコアへ導入され、バックエンドでシームレスに処理されるようになります。しかしそれまではこれらの回避方法を実施する必要があります。
参考文献として、Alex Walkersの記事『The Designer’s Guide to Working with SVG – Pt 1』、Maria Antonietta Pernaの記事『Canvas vs. SVG: Choosing the Right Tool for the Job』を参照してください。
(原文:WordPress SVG Support: How to Enable SVGs in WordPress)
[翻訳:市川千枝/編集:Livit