先日、Vue.jsでSVGファイルを表示した際に、全然違う見た目で表示される現象に出くわしました。
こんな感じです(左:元のSVG、右:表示されたSVG)。
原因がよくわからないので、表示されているSVGのHTMLを見てみました。
こっちが元のSVG
<svg xmlns="http://www.w3.org/2000/svg" width="17.95" height="17.95" viewBox="0 0 17.95 17.95">
<defs>
<style>.a{fill:none;stroke:#7c7c7c;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style>
</defs>
<g transform="translate(1 1)">
<path class="a" d="M6.272,4.5H18.678A1.772,1.772,0,0,1,20.45,6.272V18.678a1.772,1.772,0,0,1-1.772,1.772H6.272A1.772,1.772,0,0,1,4.5,18.678V6.272A1.772,1.772,0,0,1,6.272,4.5Z" transform="translate(-4.5 -4.5)"/>
<path class="a" d="M18,12v7.089" transform="translate(-10.025 -7.569)"/>
<path class="a" d="M12,18h7.089" transform="translate(-7.569 -10.025)"/>
</g>
</svg>
こっちが表示されているSVG
<svg data-v-469af010="" xmlns="http://www.w3.org/2000/svg" width="17.95" height="17.95" class="">
<defs data-v-469af010=""></defs>
<path data-v-469af010="" d="M2.772 1h12.406a1.772 1.772 0 011.772 1.772v12.406a1.772 1.772 0 01-1.772 1.772H2.772A1.772 1.772 0 011 15.178V2.772A1.772 1.772 0 012.772 1zM8.975 5.431v7.089M5.431 8.975h7.089" class="a"></path>
</svg>
全然違います。
Webで参考になる情報を探していたところ、似たような現象が起きてそれを解決している記事を発見。
しかし、同じように対応しても問題は解消しませんでした。
ただ一つ分かったのは、SVGを表示するのにvue-svg-loaderを使っていたのですが、どうやらvue-svg-loaderがsvgoを使ってSVGを圧縮していたため、HTMLの内容が変わっていたようです。
とりあえず、vue.config.jsにsvgoを無効化するオプションを追加してみました。
module.exports = {
chainWebpack: (config) => {
const svgRule = config.module.rule('svg');
svgRule.uses.clear();
svgRule
.use('babel-loader')
.loader('babel-loader')
.end()
.use('vue-svg-loader')
.loader('vue-svg-loader')
.options({
svgo: false // これを追加してsvgoを無効化
})
},
}
その結果のHTMLがこれです。
元のSVG
<svg xmlns="http://www.w3.org/2000/svg" width="17.95" height="17.95" viewBox="0 0 17.95 17.95">
<defs>
<style>.a{fill:none;stroke:#7c7c7c;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style>
</defs>
<g transform="translate(1 1)">
<path class="a" d="M6.272,4.5H18.678A1.772,1.772,0,0,1,20.45,6.272V18.678a1.772,1.772,0,0,1-1.772,1.772H6.272A1.772,1.772,0,0,1,4.5,18.678V6.272A1.772,1.772,0,0,1,6.272,4.5Z" transform="translate(-4.5 -4.5)"/>
<path class="a" d="M18,12v7.089" transform="translate(-10.025 -7.569)"/>
<path class="a" d="M12,18h7.089" transform="translate(-7.569 -10.025)"/>
</g>
</svg>
表示されているSVG
<svg data-v-469af010="" xmlns="http://www.w3.org/2000/svg" width="17.95" height="17.95" viewBox="0 0 17.95 17.95" class="">
<defs data-v-469af010=""></defs>
<g data-v-469af010="" transform="translate(1 1)">
<path data-v-469af010="" d="M6.272,4.5H18.678A1.772,1.772,0,0,1,20.45,6.272V18.678a1.772,1.772,0,0,1-1.772,1.772H6.272A1.772,1.772,0,0,1,4.5,18.678V6.272A1.772,1.772,0,0,1,6.272,4.5Z" transform="translate(-4.5 -4.5)" class="a"></path>
<path data-v-469af010="" d="M18,12v7.089" transform="translate(-10.025 -7.569)" class="a"></path>
<path data-v-469af010="" d="M12,18h7.089" transform="translate(-7.569 -10.025)" class="a"></path>
</g>
</svg>
かなり元のファイルのHTMLに近い内容になりましたが、style要素が綺麗になくなってます。当然、SVGの表示も正しくなってません。
ということでsvgoはおそらく無罪なので、svgoの無効化はやめました。
なぜstyleが消えるのかを調査していたところ、stack overflowにこんなやり取りを発見。
なんと、Vue.jsはstyleタグを認識してくれないらしい。なので、styleタグではなく、svg:styleタグを使えと書いてあります。
SVGファイルをいちいち書き換えるのは嫌だなと思いつつ、書き換えてみると、見事に求めていた表示になりました。
上記のやり取りを読み進めると、Vue.jsではSVGのstyleタグは除去して、vueファイルのstyleで定義しなさいと書いてありました(あ〜、もう全部のSVGファイルのstyleタグをsvg:styleに書き換えてしまったよ)。
書かれている通りにstyleをvueファイルのstyleで定義しても、うまく表示されました(SVGのstyleタグの除去は不要)。こっちの方がSVGファイルに手を入れなくて済むのでスマートですね(svg:styleタグ全部戻さないと…)。
styleタグ(要素)がダメなら、styleを下記のように属性にすれば良いのでは?と思い試してみました。
<svg xmlns="http://www.w3.org/2000/svg" width="17.95" height="17.95" viewBox="0 0 17.95 17.95">
<defs>
<!-- <style>.a{fill:none;stroke:#7c7c7c;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style> -->
</defs>
<g transform="translate(1 1)">
<path style="fill:none;stroke:#7c7c7c;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;" d="M6.272,4.5H18.678A1.772,1.772,0,0,1,20.45,6.272V18.678a1.772,1.772,0,0,1-1.772,1.772H6.272A1.772,1.772,0,0,1,4.5,18.678V6.272A1.772,1.772,0,0,1,6.272,4.5Z" transform="translate(-4.5 -4.5)"/>
<path style="fill:none;stroke:#7c7c7c;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;" d="M18,12v7.089" transform="translate(-10.025 -7.569)"/>
<path style="fill:none;stroke:#7c7c7c;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;" d="M12,18h7.089" transform="translate(-7.569 -10.025)"/>
</g>
</svg>
うまくいきますね。HTMLも圧縮されて良い感じです。
<svg data-v-469af010="" xmlns="http://www.w3.org/2000/svg" width="17.95" height="17.95" class="">
<g data-v-469af010="" fill="none" stroke="#7c7c7c" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path data-v-469af010="" d="M2.772 1h12.406a1.772 1.772 0 011.772 1.772v12.406a1.772 1.772 0 01-1.772 1.772H2.772A1.772 1.772 0 011 15.178V2.772A1.772 1.772 0 012.772 1zM8.975 5.431v7.089M5.431 8.975h7.089"></path>
</g>
</svg>
ということは、あれこれ頑張らずに、デザイナーにstyleを要素ではなく属性で出力して貰えば良いということになります。
結論
styleタグを含むSVGファイルを正しく表示させるには、以下の方法があります。
- styleタグをsvg:styleタグに書き換える(vueファイルにベタにSVGを書く場合はこれで良い)
- SVGファイル内のstyle定義をvueファイルに定義する
- デザイナーにお願いして、styleは要素でなく属性にしてもらう
実は、もう一つここでは触れていない問題が残ってますが、それは次回紹介します。