🐧

CSSでページを拡大縮小する

2022/04/16に公開

本記事ではブラウザのズーム機能をシミュレートした機能の実装を紹介します。

デモサイト:https://ryota0222.github.io/browser-zoom-inout-simulate/

デモ動画
demo

サンプルのソースコード:Githubのリポジトリ

バージョン情報

"react": "17.0.2"

🚨 実装をする前に注意点 🚨

コンテンツの拡大、もしくは縮小をできるということは、アクセシビリティの向上につながる可能性があります。一方でコンテンツ(もしくはページ)全体の拡大、縮小の機能を自前で実装することは、アクセシビリティが必ずしも向上するとは限りません。 実装をする必要があるか、再度検討をしてみてください。

  • 視力の低いユーザーのための機能であれば、フォントサイズの単位に相対的な単位を用いる、画像に拡大プレビュー機能を追加するなどを検討してみてはいかがでしょうか。
  • ページのビューに収まる内容を動的に変更したいのであればレイアウトの構成を変えてみてはどうでしょうか。
  • ページの拡大縮小が必要であれば、ブラウザの提供する機能[1][2][3]を利用するようにユーザーに提案してみてはどうでしょうか。

ブラウザが拡大縮小の機能を持ち、そのAPIを開発者に対して提供していない以上、その機能をアプリケーションに組み込むことは現状推奨されていないとも言えるかと思います。

実装

リサイズはCSSの変更のみで行うことができます。以下、サンプルソースのsrc/hooks/useZoom.tsの抜粋です。

useZoom.ts

useEffect(() => {
  document.documentElement.style.transformOrigin = "top left";
}, []);

/**
  * リサイズ処理を行う
  *
  * @param {number} resizedSizePercentage リサイズ後の幅(%)
  */
 const resize = (resizedSizePercentage: number) => {
   // リサイズ後のディスプレイサイズの割合
   const resizedDispSizeRatio = resizedSizePercentage / 100;
   // リサイズ後のディスプレイサイズの割合の逆数
   const reciprocalResizedDispSizeRatio = 1 / resizedDispSizeRatio;
   // html要素のCSSを変更
   document.documentElement.style.transform = `scale(${resizedDispSizeRatio})`;
   document.documentElement.style.width = `${
     reciprocalResizedDispSizeRatio * 100
   }vw`;
   // !リサイズができない箇所がある場合は適宜追加(以下、例)
   // const main = document.getElementsByTagName("main");
   // main[0].style.width = `${reciprocalResizedDispSizeRatio * 100}vw`;
 };

まず、以下の部分についてみていきます。

useEffect(() => {
  document.documentElement.style.transformOrigin = "top left";
}, []);

こちらはReact.jsの副作用フックであり、コンポーネントのマウント時に実行されます。
ここでは、htmlタグに対してtransform-origin: top left;のCSSを追加しています。これによって、ページのリサイズの原点をページの左上に設定しています。

次のresize関数は、リサイズ後の幅(120%, 80%など)を引数で受け取り、htmlタグのstyleを変更しています。更新しているCSSのプロパティは2つで、1つはtransformプロパティです。

document.documentElement.style.transform = `scale(${resizedDispSizeRatio})`;

scale()を用いてページのscaleを変更しています。例えば、引数で120という値がきた場合、scale(1.2)となります。

ただし、このままだと横幅も通常の幅の1.2倍となるので、はみでた部分がスクロールされる状態となります。そのため、次の行で横幅の補正を行います。

document.documentElement.style.width = `${reciprocalResizedDispSizeRatio * 100}vw`;

例えば、通常のサイズの1.2倍に拡大している場合、横幅を通常の幅に戻すためには、1 / 1.2倍すれば良いので、width: 83.333vwとすると横幅が収まります。

残りのコメントアウトをしている部分については、上記の実装をした上でUIが崩れてしまった場合に、適宜その要素に対して値を上書きする必要があります。

// !リサイズができない箇所がある場合は適宜追加(以下、例)
// const main = document.getElementsByTagName("main");
// main[0].style.width = `${reciprocalResizedDispSizeRatio * 100}vw`;

おそらくwidthの指定にvwを使っていると崩れてしまうと思うので、上記の例を参考に追加してみてください。

基本的な実装は以上です。

ページのリサイズに影響しない要素の実装方法

デモページのリサイズするためのコンポーネントのように、リサイズ後も大きさを変えたくないケースがあるかと思います。そのような場合も、CSSのみで実装することができます。

src/styles/zoom.css.ts
transform: scale(${(props) => 1 / props["scale-correction"]});
margin-top: ${(props) => 2 / props["scale-correction"]}vw;
margin-right: ${(props) => 2 / props["scale-correction"]}vw;

サンプルソースではstyled-componentsを用いています。

"styled-components": "^5.3.5"

実装内容は、大きく分けて2つです。
1つ目は、transformプロパティで大きさを補正しています。
2つ目は、transformプロパティでは変更できないmarginプロパティなどを個別に補正しています。

おまけ

お気づきの方もいるかもしれないですが、デモページのリサイズするコンポーネントは、Chromeの提供するUIを真似て作成してみました。

https://twitter.com/RyoTa___0222/status/1514615124266733570?s=20&t=cFkwkgsnhPa1O5T_gfuTTA

ちょっとしたサンプルソース作るだけでも凝ってしまう...😅

以上です。

脚注
  1. safariのページのリサイズ方法について: https://support.apple.com/ja-jp/guide/safari/ibrw1068/mac ↩︎

  2. Chromeのページのリサイズ方法について: https://support.google.com/chrome/answer/96810?hl=en ↩︎

  3. firefoxのページのリサイズ方法について: https://support.mozilla.org/en-US/kb/font-size-and-zoom-increase-size-of-web-pages ↩︎

Discussion