Steamのウィジェットをスマホ表示に最適化させる

Steamのゲームを紹介する時にストアページのウィジェットを設置しています。こんな感じに↓

ただ、そのままのコードではこのように↓スマホで表示した時に文字が飛び出てしまうことが多いんですよね…
スマホでSteamウィジェットを表示するとこうなる

どうにかしたくてコードを弄ったのですが、これが結構大変だったので試行錯誤の様子を残しておこうと思います。

外部サイトのiframe内は弄れない

まず最初に躓いたのがこれです。
自分のサイトなら文字サイズなり空白スペースなりをHTMLやCSSで好きに変更できるのですが、外部サイトの場合はそれができない仕様になっているようです。
(そこに気付くまでにも時間がかかってしまいました💦)

確かにセキュリティ的にはそうなってないといけないのは分かるのですが…
文字がはみ出してはなんて書いてあるか読めず、良くないと思うんですよね。というか、見た目が悪い…!(こっちが本音)

iframe内にCSSが適用できないなら、何とか自サイト側(iframeの外側)で操作できないか考えました。

transform scaleを使う

参考になりそうなサイトを調べていると、「transform scale」というパラメータで「iframe内のサイトを縮小して表示」することで解決しそうな予感…!

ただし、単純な数値を入れて縮小表示するだけではダメなんですよね…。スマホの画面サイズによって最適な大きさが変わってくるので、定数ではなく変数を入れなくちゃいけない。そうなると、最適な変数をどうやって求めれば良いのか…

ここまで辿り着いたのですが、最適な設定方法が分からずお手上げ状態になりました。画面サイズを取得して計算する必要がありそう…というところまでは考えられたのですが、そこから先の具体的な方法が思いつかず…

ここで、最近話題のChatGPTに頼ってみることにしました。

助けて!ChatGPT!

サイトに登録してさっそくいくつか質問を投げてみると、スラスラとコードが表示されてる!すごい!

「でもそのコードの意味がわからない…」という時は続けて「〇〇ってどういう意味ですか?」と打つとそれについて教えてくれます。

要領を得ない回答のときもありますが、その時はGoogle検索で既存のサイトに頼ってみたり。とにかく解決の糸口を提示してもらえるのはすごく有り難い…!

そしてやりとりの結果、一番参考になったのが以下の回答でした。

指定された`iframe`要素の`transform: scale`の値を画面サイズに合わせて適切に設定するには、JavaScriptを使用して動的に計算する必要があります。ただし、`iframe`要素の内容は外部ドキュメントであり、セキュリティ上の制限があるため、`iframe`要素自体のスケーリングは制限されています。そのため、代替的なアプローチを検討する必要があります。

1. まず、`iframe`要素に適用される親要素を作成します。

<div class="iframe-container">
  <iframe class="steam" src="your-source-url" width="512" height="190"></iframe>
</div>

上記の例では、`iframe`要素を含む親要素として`<div class="iframe-container">`を追加しています。

2. CSSを使用して、親要素を画面サイズに合わせてスケーリングします。

.iframe-container {
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: calc(190 / 512 * 100%);
  overflow: hidden;
 }

上記のCSSルールでは、親要素(`.iframe-container`)を画面に合わせてスケーリングし、アスペクト比を維持します。`padding-bottom`プロパティを使用してアスペクト比を計算し、`overflow: hidden`を設定して不要な部分を隠します。

3. JavaScriptを使用して、親要素の幅と高さに基づいて`iframe`要素のスケールを計算します。

  const iframeContainer = document.querySelector('.iframe-container');
  const iframe = iframeContainer.querySelector('.steam');
  function updateIFrameScale() {
  const containerWidth = iframeContainer.offsetWidth;
  const containerHeight = iframeContainer.offsetHeight;
  const scaleX = containerWidth / iframe.width;
  const scaleY = containerHeight / iframe.height;
  const scaleValue = Math.min(scaleX, scaleY);
  iframe.style.transform = `scale(${scaleValue})`;
  }
  // ウィンドウのリサイズ時にスケールを更新する
  window.addEventListener('resize', updateIFrameScale);
  // ページのロード時に初回のスケールを適用する
  window.addEventListener('load', updateIFrameScale);

上記のJavaScriptコードでは、`updateIFrameScale`関数を定義して、親要素の幅と高さに基づいて`iframe`要素のスケールを計算し、適用しています。また、ウィンドウのリサイズイベントとページのロードイベントで`updateIFrameScale`関数を呼び出してスケールを更新します。

この方法により、`iframe`要素が画面サイズに合わせて適切にスケーリングされます。ただし、`iframe`要素自体の内容のスケーリング・・・・・・・

※最後の方は制限?で読めませんでした💦

なるほど…iframeタグをdivで囲んで、それぞれにclassを設定。そしてstyleとJavaScriptで縮小表示を実装… JavaScriptの部分はよく分からないけど、これならできそう!と早速試してみることに。

できた!…でも余分な空白ができちゃう

言われたコードを入れて動かしてみると、確かに縮小表示できた!やったー!

ただこれだとパソコン画面で見た時にウィジェットのサイズが大きくなりすぎるので、最大横幅を533pxで設定することに。

そうすると、今度は下側に余分な空白ができるようになりました。(スマホ画面ぐらいだと余白は問題ない)
どうやら、ウィジェットの横幅がMax533pxになった後もpadding-bottomの計算を行うことで余分なスペースが出てしまう模様。

「それならpadding-bottomの最大値を決めればいいじゃん」とChatGPTに方法を尋ねたところ、「max-heightを指定すればいいですよ」とのこと。

「box-sizing」の設定をしているとheightの値にmarginやpaddingの値も含まれるそうで、max-heightで最大値が設定できるらしい。

よし!じゃあ早速!
と思ってやってみたけどmax-heightが効いてない…

なんで?とコードをChatGPTに見せたら「overflow: hidden;」の設定があるから効いてないらしい。えー!?このパラメータ消せないのに💦

他に方法がないか、今度はGoogleで検索してみると見つけました! 『min()』!

これを使うことで「最大値は210px、計算結果がそれよりも小さかったらそちらを使う」という設定ができました!

完成

これでコードの完成です!長かった!

正直ChatGPTが出してくれたスクリプトのコードについては、知識不足のため正確には理解できていませんが…とりあえず動いてるからヨシ!

ちなみに出来上がったコードがこちら↓

<script>
  const iframeContainer = document.querySelector('.steam-wrap');
  const iframe = iframeContainer.querySelector('.steam');
  const maxWidth = 533; // 最大幅を設定してください
  const maxHeight = 190;

  function updateIFrameScale() {
    const containerWidth = iframeContainer.offsetWidth;
    const containerHeight = iframeContainer.offsetHeight;
    const scaleX = containerWidth / iframe.width;
    const scaleY = containerHeight / iframe.height;
    const scaleValue = Math.min(scaleX, scaleY);
    const scaledWidth = iframe.width * scaleValue;
    const scaledHeight = iframe.height * scaleValue;

    const limitedWidth = Math.min(scaledWidth, maxWidth);
    const limitedHeight = Math.min(scaledHeight, maxHeight);

    iframe.style.transform = `scale(${limitedWidth / iframe.width}, ${limitedHeight / iframe.height})`;
  }
  window.addEventListener('DOMContentLoaded', updateIFrameScale);
  window.addEventListener('resize', updateIFrameScale);
  window.addEventListener('load', updateIFrameScale);
</script>

ウィジェットを置く場所に貼るコードはこちら↓

<div class="steam-wrap">
  <iframe class="steam" src="SteamウィジェットのURL" frameborder="0" width="533" height="190"></iframe>
</div>

CSSはこんな感じ↓

.steam-wrap {
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: min(210px, 190 / 533 * 113%);
  overflow: hidden;
}

iframe.steam {
  margin: 21px 0 0;
  padding-right: 5px;
  padding-left: 5px;
  transform-origin: 0 0;
}

ちょこちょこ記事に書いていないことも追加していますが、知識がなさすぎて文章にまとめられないので、気になる方は部分部分でググってください😅

最後に

丸2日かかってしまいましたが、最終的に上手く実装できたので良かったです✨ 見栄えが良くなって大満足🤗

本当は公式サイトさんの方でスマホ表示に対応してもらえたら嬉しいんですけどね… 最近はスマホでサイト見てる人が多いですから。
…でもSteamってパソコンでやる前提だから、今の状態でも理にかなってる…のかも?😅

とはいえ、今回の作業内容は今後iframeを使う時にも応用できそうなので、大きな収穫になりました。

【追記】複数のウィジェット設置に対応

【2023/12/3 追記】
ページに2つウィジェットを設置してみたところ、2つ目のウィジェットに対してスケーリングが効いていなかったので、ChatGPTに質問してサクッとコードを修正してもらいました。

<script>
  // 全ての .steam-wrap 要素を取得
  const iframeContainers = document.querySelectorAll('.steam-wrap');

  const maxWidth = 533; // 最大幅を設定してください
  const maxHeight = 190;

  function updateIFrameScale(iframeContainer) {
    const iframe = iframeContainer.querySelector('.steam');
    const containerWidth = iframeContainer.offsetWidth;
    const containerHeight = iframeContainer.offsetHeight;
    const scaleX = containerWidth / iframe.width;
    const scaleY = containerHeight / iframe.height;
    const scaleValue = Math.min(scaleX, scaleY);
    const scaledWidth = iframe.width * scaleValue;
    const scaledHeight = iframe.height * scaleValue;

    const limitedWidth = Math.min(scaledWidth, maxWidth);
    const limitedHeight = Math.min(scaledHeight, maxHeight);

    iframe.style.transform = `scale(${limitedWidth / iframe.width}, ${limitedHeight / iframe.height})`;
  }

  function updateAllIFrameScales() {
    iframeContainers.forEach(updateIFrameScale);
  }

  window.addEventListener('DOMContentLoaded', updateAllIFrameScales);
  window.addEventListener('resize', updateAllIFrameScales);
  window.addEventListener('load', updateAllIFrameScales);
</script>

この修正では、.steam-wrap 要素が複数ある場合にそれぞれの要素に対して処理を行います。iframeContainers を使用して全ての .steam-wrap 要素を取得し、forEach メソッドを使用してそれぞれの要素に対して updateIFrameScale 関数を呼び出します。

一発で修正が完了してビックリしました!やはりプログラミングについてはChatGPTに聞くのが一番早いですね。

関連記事
新着記事
ブログ内検索