web-dev-qa-db-ja.com

幅と高さの両方に合うようにビューポートをスケーリングに設定する

特定の幅と高さ(1pxの境界線と3pxの上部マージンを持つ885x610のdiv)に収まるWebサイトで作業しています。 div全体を表示するために、ユーザーがスクロールまたはズームする必要がないようにしたいと思います。常に完全に表示されている必要があります。デバイスにはさまざまな解像度とアスペクト比があるため、「ビューポート」メタタグをJavaScriptで動的に設定するというアイデアが思い浮かびました。このように、divは常に同じ寸法になります。div全体をビューポートに合わせるために、異なるデバイスを異なる方法でズームする必要があります。私は自分の考えを試してみて、奇妙な結果を得ました。

次のコードは最初のページの読み込みで機能します(Chrome 32.0.1700.99 on Android 4.4.0)でテストされています)、更新するとズームレベルが変化しますまた、alertをコメントアウトすると、最初のページの読み込みでも機能しません。

フィドル

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0">
        <script type="text/javascript">
            function getViewportWidth() {
                if (window.innerWidth) {
                    return window.innerWidth;
                }
                else if (document.body && document.body.offsetWidth) {
                    return document.body.offsetWidth;
                }
                else {
                    return 0;
                }
            }

            function getViewportHeight() {
                if (window.innerHeight) {
                    return window.innerHeight;
                }
                else if (document.body && document.body.offsetHeight) {
                    return document.body.offsetHeight;
                }
                else {
                    return 0;
                }
            }

            if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) {
                var actual_width = getViewportWidth();
                var actual_height = getViewportHeight();

                var min_width = 887;
                var min_height = 615;

                var ratio = Math.min(actual_width / min_width, actual_height / min_height);

                if (ratio < 1) {
                    document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + ratio + ', maximum-scale=' + ratio + ', minimum-scale=' + ratio + ', user-scalable=yes, width=' + actual_width);
                }
            }

            alert(document.querySelector('meta[name="viewport"]').getAttribute('content'));
        </script>
        <title>Test</title>
        <style>
            body {
                margin: 0;
            }

            div {
                margin: 3px auto 0;
                width: 885px;
                height: 610px;
                border: 1px solid #f00;
                background-color: #fdd;
            }
        </style>
    </head>
    <body>
        <div>
            This div is 885x610 (ratio is in between 4:3 and 16:10) with a 1px border and 3px top margin, making a total of 887x615.
        </div>
    </body>
</html>

このウェブサイトを幅と高さの両方に合わせるにはどうすればよいですか?

11
Nick

一貫した動作が得られる可能性があります。しかし、残念ながら非常に複雑です。私はスプーフィングされたエージェントを検出し、それに応じてビューポートをデスクトップまたは他のスプーフィングされたエージェントに動的に再スケーリングするスクリプトに取り組んでいます。 Android/ChromeとiOSエミュレーターのズームの問題にも直面していました...

これを回避するには、ズームを無効にするか、ビューポートを2回設定する必要があります。最初のパスで、できれば_<head>_にインラインでインラインする場合は、スケールを設定し、ユーザースケーリングを一時的に無効にして、ズームの問題を回避します。次のような3つのスケールすべてに同じ固定値を使用します。

_document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale='+scale+',maximum-scale='+scale+',initial-scale='+scale);
_

次に、ズームを復元するには、DOMContentLoadedに同じ縮尺でビューポートを再度設定しますが、今回は通常の最小/最大縮尺値を設定してユーザースケーリングを復元します。

_document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale=0,maximum-scale=10');
_

あなたのコンテキストでは、レイアウトは固定されており、ビューポートよりも大きいため、DOMContentLoadedのより適切な代替手段として_initial-scale='+scale_がおそらく必要です。

_document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale=0,maximum-scale=10,initial-scale='+scale);
_

これにより、ズームの問題なくWebkitブラウザーで望むようにビューポートを再スケーリングできます。悲しいことにIEとFirefoxは、ビューポート、寸法、およびデバイスピクセル比のこのブラウザー互換性テーブルが示すようにビューポートの変更をサポートしていないため、私はwebkitでのみ言います: http:// www .quirksmode.org/mobile/tableViewport.html

IEにはビューポートを動的に変更する独自の方法があり、これは実際にIEスナップモードが応答するために必要です。 http://timkadlec.com/2012/10/ie10-snap -mode-and-sensitive-design /

したがって、IEMobileとIE SnapMode(IE10&11)の場合、_<style>_にインライン_<head>_を動的に挿入する必要があります。

_<script>
 var s = document.createElement('style');
 s.appendChild(document.createTextNode('@-ms-viewport{width:'+width+'px')+';}'));
 document.head.appendChild(s);
</script>
_

そして残念なことに、Firefoxにはどちらもありません。上記の互換性の表が示すように、ビューポートは1度だけ設定されます。現時点では、他のメソッドがないため、CSS変換(@imcgが指摘)を使用することが、AndroidまたはGecko OSでFireFox Mobileのビューポートを変更する唯一の方法です。テストしたところです。固定サイズのデザインのコンテキストで機能します(「レスポンシブデザインコンテキスト」では、CSS変換を使用してレイアウトを再スケーリングできます。たとえば、電話のデスクトップサイズでレイアウトできますが、Firefoxは依然として電話サイズのMQを読み取ります。つまり、 RWDのコンテキストで気を付けるべきこと/ webkit以外)

ただし、AndroidでCSSTransformを使用すると、ランダムなWebkitがクラッシュすることに気付いたので、Safari/Chrome/Operaのビューポートメソッドを信頼性の高いものとしてお勧めします。

さらに、ビューポートの幅についてブラウザー間の信頼性を確保するために、innerWidthのブラウザー間の全体的な不整合に直面/修正する必要があります(_documentElement.clientWidth_は、正確なレイアウトピクセルの幅を取得するための信頼性がはるかに高いことに注意してください) innerWidth)と、quirksmode.orgテーブルに示されているdevicePixelRatioの不一致にも対処する必要があります。

更新:実際に、Firefoxでの自分自身の問題の解決策を検討した後、document.write()を使用して、Firefox Mobileのビューポートをオーバーライドする方法を見つけました。

_document.write('<meta name="viewport" content="width='+width+'px,initial-scale='+scale+'">');
_

これをWebkitとFirefoxの両方でテストして、ズームの問題はありませんでした。 Window Phoneではテストできないので、IEMobileでdocument.writeが機能するかどうかはわかりません...

8
hexalys

私はこれが2年遅れであることを知っていますが、私はこの問題に取り組むのに多くの時間を費やし、私の解決策を共有したいと思います。私は元の質問が非常に役に立ったと思ったので、この回答を投稿することが私の返答方法だと思います。私のソリューションはiPhone6と7インチGalaxy Tabで動作します。他のデバイスでどのように動作するのかはわかりませんが、ほとんどの場合動作するはずです。

再利用しやすいように、ビューポートロジックを外部ファイルに分離しました。まず、コードの使用方法は次のとおりです。

<HTML>
<HEAD>    
  <SCRIPT type="text/javascript" src="AutoViewport.js"></SCRIPT>
</HEAD>
<BODY>
  <SCRIPT>
  AutoViewport.setDimensions(yourNeededWidth, yourNeededHeight);
  </SCRIPT>
  <!-- The rest of your HTML goes here -->
</BODY>
</HTML>

実際には、希望の幅と高さをわずかに(15ピクセル程度)埋め込んで、意図したディスプレイが適切にフレーム化されるようにしました。また、HTMLでビューポートタグを指定する必要がないことも、使用方法のコードから注意してください。ライブラリが存在しない場合は、自動的に作成されます。 AutoViewport.jsは次のとおりです。

/** Steven Yang, July 2016
Based on http://stackoverflow.com/questions/21419404/setting-the-viewport-to-scale-to-fit-both-width-and-height , this Javascript code allows you to 
cause the viewport to auto-adjust based on a desired pixel width and height
that must be visible on the screen.

This code has been tested on an iPhone6 and a 7" Samsung Galaxy Tab.
In my case, I have a game with the exact dimensions of 990 x 660.  This
script allows me to make the game render within the screen, regardless 
of whether you are in landscape or portrait mode, and it works even
when you hit refresh or rotate your device.

Please use this code freely.  Credit is appreciated, but not required!
*/

function AutoViewport() {}

AutoViewport.setDimensions = function(requiredWidth, requiredHeight) {

/* Conditionally adds a default viewport tag if it does not already exist. */
var insertViewport = function () {

  // do not create if viewport tag already exists
  if (document.querySelector('meta[name="viewport"]'))
    return;

  var viewPortTag=document.createElement('meta');
  viewPortTag.id="viewport";
  viewPortTag.name = "viewport";
  viewPortTag.content = "width=max-device-width, height=max-device-height,initial-scale=1.0";
  document.getElementsByTagName('head')[0].appendChild(viewPortTag);
};

var isPortraitOrientation = function() {
  switch(window.orientation) {  
  case -90:
  case 90:
  return false;
  }

  return true;
 };

var getDisplayWidth = function() {
  if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) {
    if (isPortraitOrientation())
      return screen.width;
    else
      return screen.height;
  }

  return screen.width;
}

var getDisplayHeight = function() {
  if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) {
    if (isPortraitOrientation())
      return screen.height;
    else
      return screen.width;
  }

  // I subtract 180 here to compensate for the address bar.  This is imperfect, but seems to work for my Android tablet using Chrome.
  return screen.height - 180;
}

var adjustViewport = function(requiredWidth, requiredHeight) {

  if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)){

    var actual_height = getDisplayHeight();
    var actual_width = getDisplayWidth();

    var min_width = requiredWidth;
    var min_height = requiredHeight;

    var ratio = Math.min(actual_width / min_width, actual_height / min_height);

    document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + ratio + ', maximum-scale=' + ratio + ', minimum-scale=' + ratio + ', user-scalable=yes, width=' + actual_width);
}    
};

  insertViewport();
  adjustViewport(requiredWidth, requiredHeight);
  window.addEventListener('orientationchange', function() {
    adjustViewport(requiredWidth, requiredHeight);      
  });
};

私のコードを質問で見つかった元のコードと密接に比較すると、いくつかの違いに気付くでしょう。たとえば、ビューポートの幅や高さに依存することはありません。代わりに、私は画面オブジェクトに依存しています。ページを更新したり、画面を回転したりすると、ビューポートの幅と高さが変化する可能性がありますが、screen.widthとscreen.heightは決して変化しないため、これは重要です。次に気づくのは、私が(ratio <1)のチェックを行わないことです。画面を更新したり回転したりすると、そのチェックが矛盾を引き起こしていたため、削除しました。また、画面回転用のハンドラーを含めました。

最後に、この質問を作成してくれて、時間を節約してくれた方に感謝したいと思います。

4
Steven

ビューポートメタタグを変更してもデバイス間で一貫した動作が得られない場合は、CSS3変換を使用してサイズを変更せずにズームできます。

if (ratio < 1) {
    var box = document.getElementsByTagName('div')[0];
    box.style.webkitTransform = 'scale('+ratio+')';
    box.style.webkitTransformOrigin = '0 0';
}

console.log(box.offsetWidth); // always original width
console.log(box.getBoundingClientRect().width); // new width with scaling applied

簡単にするため、ここではwebkit以外のベンダープレフィックスを省略しています。

スケーリングされたdivを中央に配置するには、変換変換を使用できます。

var x = (actual_width - min_width * ratio) / 2;
var y = (actual_height - min_height * ratio) / 2;
box.style.webkitTransform = 'translateX('+x+'px) translateY('+y+'px) scale('+ratio+')';
box.style.webkitTransformOrigin = '0 0';
3
imcg

これでビューポートを置き換えます:

<META NAME="viewport" CONTENT="width=device-width, height=device-height, initial-scale=1, user-scalable=no"/>

ここでuser-scalable = 0があなたのために仕事をします。

これはあなたのために働くでしょう。それでもうまくいかない場合はビューポートを拡張する必要があるので、ビューポートを置き換えてこれを追加します:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, target-densitydpi=medium-dpi, user-scalable=0" />

あなたのjavascriptエラーについては、このリンクを見てください:

ビューポートメタタグを使用してモバイルウェブコンテンツにフィット

2
user3274745

私はスティーブンの答えが受け入れられるべきだと思います。

他の誰かが役に立った場合は、ユーザーが横向きビューのときにビューポート内のビューを中央に配置するために、次の2つをStevenのAutoViewport.jsに追加します。

「var viewport_margin = 0;」を追加しますコードの最初の行として( "function AutoViewport(){}"の前の独自の行として)。

「viewport_margin = Math.abs(actual_width-(ratio * requiredWidth))/(actual_width * 2)* 100;」を追加します"document.querySelector( 'meta [name =" viewport "]')。setAttribute( 'content'、 'initial-scale =' + ratio + '、maximum-scale =' + ratio + '、minimumと書かれた行の後-scale = '+ ratio +'、user-scalable = yes、width = '+ actual_width); "

このソリューションを明らかにするために投稿してくれたすべての人に感謝します。

更新:Androidでは、私の追加機能はAPI 24+でのみ機能するようです。彼らがAPI 19〜23で機能しない理由がわかりません。何か案は?

0
Strongdog

Divの親要素(htmlおよびbody)の高さと幅を100%に設定し、マージンをゼロにします。

html, body {
  margin: 0;
  height: 100%;
  width: 100%;
}

次に、divにボーダーが必要なため、要素の幅/高さを指定するときにボーダーの幅が含まれていることを確認する必要があります。これを行うには、divでbox-sizing: border-boxを使用します。

Divの上に3pxの上部マージンが必要なため、divの相対的な配置により、高さが3pxになりすぎます。これを修正するには、divで絶対位置を使用し、上、左、下を0に設定します。

div {
  position: absolute;
  top: 3px;
  left: 0;
  bottom: 0;
  box-sizing: border-box;
  width: 100%;
  border: 1px solid #f00;
  background-color: #fdd;
}

これが 実用的な例 です。

更新:私は質問を誤解したと思います。デバイスのビューポートに応じて「ズーム」を調整するサンプルコードを次に示します

http://jpanagsagan.com/viewport/index2.html

バニラアペンドの使用に問題があるため、jqueryを使用してメタタグをアペンドしたことに注意してください。

私が気づいたことの1つは、HTMLでハードコーディングしてJS経由で変更した場合、ドキュメントは修正を適用しない(検証が必要)ことです。 HTMLに以前のタグがない場合は、JSを使用して変更できたため、追加を使用しました。

比率をいじってみてもかまいませんが、この例では、divの幅で割ったビューポートの幅を使用しました。

お役に立てれば。

0
Scott Bartell

更新:私は質問を誤解したと思います。デバイスのビューポートに応じて「ズーム」を調整するサンプルコードを次に示します

http://jpanagsagan.com/viewport/index2.html

バニラアペンドの使用に問題があるため、jqueryを使用してメタタグをアペンドしたことに注意してください。

私が気づいたことの1つは、HTMLにハードコーディングしてJS経由で変更した場合、ドキュメントは修正を適用しない(検証が必要)ことです。 HTMLに以前のタグがない場合は、JSを使用して変更できたため、追加を使用しました。

比率をいじってみてもかまいませんが、この例では、divの幅で割ったビューポートの幅を使用しました。

お役に立てれば。

0
Dan1121