ダークモード対応を考えた時、シンプルな SVG はインライン化するか、use タグで利用するのが一般的です。 なぜなら文字色に合わせて色を反転できる fill="currentColor" を適用するためには、上記の方法を取るしかないからです。 しかし外部の DTD などを読み込む複雑な SVG は、上記の方法だとさすがに扱いにくい問題があります。 下手にインライン化すると ID 重複が起きたりするし…。 そこで仕方なく、一部の SVG は以下のように object タグで読み込むことになります。 ※他にも CSS の filter を使うなどの方法があるかも知れませんが、普通はそんな超絶技巧はしないほうが良いです。
<object type="image/svg+xml" data="test.svg" width="64" height="64">
</object>
ただ object タグで読み込むと、(1) fill="currentColor" が効かない問題と、(2) 読み込みが完了したかのチェックが難しい問題があります。
1 に関しては JavaScript で自分で設定すればどうにかなるのですが、これまで 2 の解決方法がわかりませんでした。
SVG の読み込みを HTML 上で 実装せず JavaScript で実装するしかないのかなと思っていました。
ただそれをやってしまうと JavaScript を OFF にした時の実装が非常に面倒になってしまう問題があります。
また JavaScript のパース前に SVG をロードできるなら、高速化のためにもぜひ事前ロードしたいですよね。
この問題に関して、StackOverflow からヒントが得られました。 そのままだと使いにくいので、使いやすく書き直したものが以下です。 HTML 側で data 属性を事前に書いていると、後から JavaScript 側で onload 属性を付与してチェックしても、既に読み込み完了になっている可能性があります。 そこで getCurrentTime() を使うことで、SVG が読み込み完了かどうかチェックできます。
function isLoaded(object) {
const doc = object.contentDocument;
if (!doc) return false;
const svg = doc.querySelector("svg");
if (!svg) return false;
if (svg.getCurrentTime() < 0) return false;
return true;
}
doc.querySelector("svg") の部分は、doc.documentElement でも良いのではと思ったのですが、駄目みたいです。 svg.getCurrentTime() は Chrome だと読み込み完了してから何秒経ったかを返却するようですが、 Firefox は読み込み完了すると常に 0 になるようなので、0 以上かどうかチェックするのが良さそうです。
この関数を使って object タグで読み込んだ SVG をダークモード対応する時は、たとえば以下のように実装します。 具体例 (タッチde書き順):
if (isLoaded(object)) {
const svg = object.contentDocument.documentElement;
svg.style.background = "#212529";
svg.firstElementChild.style.stroke = "#fff";
} else {
object.onload = () => {
const svg = object.contentDocument.documentElement;
svg.style.background = "#212529";
svg.firstElementChild.style.stroke = "#fff";
};
}
この例では background と stroke の色を変えることでダークモードに対応しています。
background が transparent のとき、色の設定はいらないように思うのですが、Chrome の挙動が怪しいので付けています。
今までは お手軽にダークモード対応 (+処方箋) で誤魔化していたので楽だったのですが、 真面目にダークモード対応をするのは大変なんだなと、今さら感じています。 特に SVG の扱いが大変ですね。
0 件のコメント:
コメントを投稿