web-dev-qa-db-fra.com

Réglage de la fenêtre d'affichage pour qu'elle s'adapte à la largeur et à la hauteur

Je travaille sur un site Web qui correspond à une largeur et une hauteur spécifiques (une div de 885x610 avec une bordure de 1px et une marge supérieure de 3px). Je voudrais que l'utilisateur n'ait jamais à faire défiler ou zoomer pour voir l'intégralité de la div; il devrait toujours être entièrement visible. Étant donné que les appareils ont une grande variété de résolutions et de formats d’image, l’idée qui leur est venue à l’esprit était de définir la balise méta «viewport» de manière dynamique avec JavaScript. De cette façon, les div auront toujours les mêmes dimensions, mais il faudra zoomer différemment sur différents appareils pour qu'ils puissent afficher l'intégralité de la div dans leur fenêtre. J'ai essayé mon idée et obtenu des résultats étranges.

Le code suivant fonctionne lors du premier chargement de la page (testé dans Chrome 32.0.1700.99 sur Android 4.4.0), mais au fur et à mesure de l'actualisation, le niveau de zoom change autour. De plus, si je commente la alert, cela ne fonctionne même pas lors du chargement de la première page.

Fiddle

<!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>

Que puis-je faire pour que cette échelle de site Web s'adapte à la fois à la largeur et à la hauteur?

10
Nick

Il est possible d'avoir un comportement cohérent. Mais c'est malheureusement très complexe. Je travaille sur un script qui détecte les agents usurpés et redimensionne de manière dynamique la fenêtre d'affichage sur le bureau ou d'autres agents usurpés en conséquence. J'étais également confronté au problème de zoom avec Android/Chrome ainsi qu'à l'émulateur iOS ...

Pour le contourner, vous devez désactiver le zoom et/ou définir la fenêtre d'affichage deux fois. Lors du premier passage, de préférence intégré dans le <head> comme vous le faites maintenant, vous définissez votre échelle et désactivez temporairement la mise à l'échelle de l'utilisateur pour éviter le problème de zoom, en utilisant la même valeur fixe pour les 3 échelles, comme:

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

Ensuite, pour restaurer le zoom, définissez à nouveau la fenêtre sur DOMContentLoaded, avec la même échelle, sauf que cette fois-ci, vous définissez des valeurs d'échelle min/max normales pour restaurer la mise à l'échelle de l'utilisateur:

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

Dans votre contexte, étant donné que la présentation est fixe et plus grande que la fenêtre d'affichage, initial-scale='+scale est peut-être nécessaire pour une alternative plus saine pour DOMContentLoaded:

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

Cela devrait permettre à la fenêtre d'affichage de redimensionner comme vous le souhaitez dans les navigateurs Webkit sans problèmes de zoom. Je dis seulement dans webkit car malheureusement IE et Firefox ne prennent pas en charge la modification de la fenêtre d'affichage conformément à ce tableau de compatibilité de navigateur pour les fenêtres de visualisation, les dimensions et les rapports de pixels de l'appareil: http://www.quirksmode.org /mobile/tableViewport.html

IE a sa propre manière de changer la fenêtre de manière dynamique, ce qui est réellement nécessaire pour que les modes IE snap soient réactifs . http://timkadlec.com/2012/10/ie10-snap- mode-and-responsive-design / _

Donc, pour IEMobile et IE SnapMode (IE10 & 11), vous devez insérer dynamiquement un <style> en ligne dans le <head> avec quelque chose comme.

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

Et malheureusement, Firefox n’a ni l’un ni l’autre: La fenêtre d’affichage est définie pour une fois pour toutes, comme le montre le tableau de compatibilité ci-dessus. À l’heure actuelle, faute d’autres méthodes, l’utilisation de CSS Transform (comme @imcg l’a souligné) est le seul moyen de modifier la fenêtre dans FireFox Mobile sous Android ou Gecko OS. Je viens de le tester et cela fonctionne dans le contexte d'une conception de taille fixe. (Dans le contexte "Responsive Design", la mise en page peut être agrandie via CSS Transform, par exemple à la taille d'un ordinateur sur un téléphone, mais Firefox lit toujours les MQ de la taille d'un téléphone. Il faut donc être vigilant dans un contexte RWD. )

Bien que j’ai remarqué des plantages aléatoires de Webkit avec CSSTransform sur Android, je recommanderais donc la méthode de visualisation pour Safari/Chrome/Opera plus fiable.

En outre, afin d’obtenir une fiabilité multi-navigateur pour la largeur de la fenêtre, vous devez également faire face à la contradiction générale entre les navigateurs pour innerWidth (notez que documentElement.clientWidth est beaucoup plus fiable pour obtenir une largeur de pixel de présentation précise également avoir à traiter avec les écarts devicePixelRatio comme indiqué dans le tableau quirksmode.org.

Mise à jour: après avoir réfléchi à une solution à mon propre problème avec Firefox, je viens de découvrir un moyen de remplacer la fenêtre d'affichage dans Firefox Mobile, en utilisant document.write(), appliqué une seule fois:

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

Je viens de tester cela avec succès dans Webkit et Firefox, sans aucun problème de zoom. Je ne peux pas tester sur les téléphones Windows, je ne suis donc pas sûr que document.write fonctionne pour IEMobile ...

8
hexalys

Je sais que nous avons deux ans de retard, mais j'ai passé beaucoup de temps à travailler sur ce problème et j'aimerais partager ma solution. J’ai trouvé la question initiale très utile, j’estime donc que poster cette réponse est ma façon de redonner. Ma solution fonctionne sur un iPhone6 ​​et un Galaxy Tab 7 ". Je ne sais pas comment il se comporte sur d'autres appareils, mais j'imagine qu'il devrait se comporter principalement.

J'ai séparé la logique de la fenêtre d'affichage dans un fichier externe afin qu'il soit facile à réutiliser. Tout d'abord, voici comment vous utiliseriez le code:

<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>

En réalité, j'ai légèrement rembourré la largeur et la hauteur souhaitées (environ 15 pixels), de sorte que le format d'affichage souhaité soit bien cadré. Notez également, dans le code d'utilisation, qu'il n'est pas nécessaire de spécifier une balise viewport dans votre code HTML. Ma bibliothèque en créera automatiquement un pour vous s'il n'en existe pas déjà . Voici 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);      
  });
};

Si vous comparez mon code étroitement avec le code original trouvé dans la question, vous remarquerez quelques différences. Par exemple, je ne compte jamais sur la largeur ou la hauteur de la fenêtre. Au lieu de cela, je compte sur l’objet écran. Ceci est important car, si vous actualisez votre page ou faites pivoter votre écran, la largeur et la hauteur de la fenêtre d'affichage peuvent changer, mais screen.width et screen.height ne changent jamais. La prochaine chose que vous remarquerez est que je ne vérifie pas (rapport <1). Lors de l'actualisation ou de la rotation de l'écran, cette vérification provoquait une incohérence, je l'ai donc supprimée. En outre, j'ai inclus un gestionnaire pour la rotation de l'écran. 

Enfin, je voudrais juste remercier la personne qui a créé cette question pour avoir jeté les bases, ce qui m'a fait gagner du temps!

4
Steven

Remplacez votre fenêtre d'affichage par ceci:

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

L'utilisateur-scalable = 0 ici fera le travail pour vous.

Cela fonctionnera pour vous. Si cela ne fonctionne toujours pas, nous devrons étendre la fenêtre d'affichage. Remplacez-la et ajoutez ceci: 

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

Pour votre erreur javascript, regardez ce lien:

Adaptez le contenu Web mobile à l’aide de la balise méta Viewport

3
user3274745

Si vous ne parvenez pas à obtenir un comportement cohérent sur les appareils en modifiant la balise méta de la fenêtre d'affichage, il est possible de zoomer sans modifier les dimensions à l'aide de transformations 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

Remarque J'ai omis ici les préfixes de fournisseurs autres que webkit afin de simplifier les choses.

Pour centrer la div mise à l'échelle, vous pouvez utiliser les transformations de traduction:

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';
2
imcg

Je pense que Steven devrait être la réponse acceptée.

Si cela peut aider quelqu'un d'autre, j'ajouterais les deux éléments suivants au fichier AutoViewport.js de Steven, afin de centrer la vue dans la fenêtre d'affichage lorsque l'utilisateur est en mode paysage: 

Ajoutez "var viewport_margin = 0;" en tant que première ligne de code (car c'est sa propre ligne avant "function AutoViewport () {}".

Ajoutez "viewport_margin = Math.abs (actual_width- (ratio * requiredWidth))// actual_width * 2) * 100;" après la ligne "document.querySelector ('meta [name =" viewport "]") ". setAttribute (' content ',' initial-scale = '+ ratio +', maximum-scale = '+ ratio +', minimum -scale = '+ ratio +', utilisateur-scalable = yes, width = '+ actual_width); "

Merci à tous ceux qui ont posté pour apporter cette solution à la lumière.

MISE À JOUR: Sous Android, mes ajouts semblent fonctionner uniquement avec API 24+. Vous ne savez pas pourquoi ils ne travaillent pas avec les API 19-23. Des idées?

0
Strongdog

Définissez la hauteur et la largeur des éléments parents de la div (html et body) à 100% et mettez à zéro la marge.

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

Ensuite, comme vous voulez une bordure sur la div, vous devez vous assurer que la largeur de la bordure est incluse lorsque vous spécifiez la largeur/hauteur de l'élément. Pour ce faire, utilisez box-sizing: border-box sur la div.

Parce que vous voulez une marge supérieure de 3 pixels sur le div, le positionnement relatif du div entraînera une hauteur trop élevée de 3 pixels. Pour résoudre ce problème, utilisez le positionnement absolu sur la div et réglez le haut, la gauche et le bas sur 0.

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

Voici un exemple de travail .

UPDATE: Je pense avoir mal compris la question. Voici un exemple de code qui ajuste le "zoom" en fonction de la fenêtre d'affichage du périphérique.

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

Notez que jQuery a été ajouté pour ajouter la balise méta car j’ai un problème avec l’ajout de Vanilla.

Une chose que j’ai remarquée est que si vous codez en dur dans le code HTML et le modifiez via JS, le document n’appliquera pas la correction (nécessite une vérification). J'ai pu le changer via JS s'il n'y avait pas de balise précédente dans le code HTML, j'ai donc utilisé append.

Vous pouvez jouer avec le ratio, mais dans l'exemple, j'ai utilisé la largeur de la fenêtre divisée par la largeur de la div. 

j'espère que cela t'aides.

0
Scott Bartell

UPDATE: Je pense avoir mal compris la question. Voici un exemple de code qui ajuste le "zoom" en fonction de la fenêtre d'affichage du périphérique.

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

Notez que jQuery a été ajouté pour ajouter la balise méta car j’ai un problème avec l’ajout de Vanilla.

Une chose que j'ai remarquée est que si vous codez en dur dans le code HTML et le modifiez via JS, le document n'appliquera pas la correction (nécessite une vérification). J'ai pu le changer via JS s'il n'y avait pas de balise précédente dans le code HTML, j'ai donc utilisé append.

Vous pouvez jouer avec le ratio, mais dans l'exemple, j'ai utilisé la largeur de la fenêtre divisée par la largeur de la div. 

j'espère que cela t'aides.

0
Dan1121