Réglage de la vue vers l'échelle pour correspondre à la largeur et à la hauteur

Je travaille sur un site Web qui correspond à une largeur et une hauteur spécifiques (une division 885×610 avec une bordure 1px et une marge supérieure de 3px). J'aimerais que l'utilisateur ne puisse jamais faire défiler ou faire un zoom afin de voir la division entière; Il doit toujours être entièrement visible. Étant donné que les appareils ont une grande variété de résolutions et de rapports d'aspect, l'idée qui est venue à l'esprit était de configurer la méta-balise "viewport" de manière dynamique avec JavaScript. De cette façon, le div sera toujours de la même dimension, différents appareils devront être zoomés différemment afin de s'adapter à la division entière dans leur fenêtre. J'ai essayé mon idée et obtenu des résultats étranges.

Le code suivant fonctionne sur le chargement de la première page (testé dans Chrome 32.0.1700.99 sur Android 4.4.0), mais lors du rafraîchissement, le niveau de zoom change. De plus, si je fais des commentaires sur l' alert , cela ne fonctionne pas même sur le chargement de la première page.

Violon

 <!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 correspond à la largeur et à la hauteur?

Il est possible d'avoir un comportement constant. Mais il est malheureusement très complexe. Je travaille sur un script qui détecte les agents falsifiés et rééche ensuite dynamiquement la fenêtre vers le bureau ou d'autres agents falsifiés en conséquence. J'ai également fait face au problème de zoom avec Android / Chrome ainsi que l'émulateur iOS …

Pour le contourner, vous devez désactiver le zoom et / ou régler la fenêtre deux fois. Sur la première passe, de préférence en ligne dans <head> comme vous le faites maintenant, vous définissez votre échelle et désactivez la mise à l'échelle temporaire 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, réglez la fenêtre sur DOMContentLoaded , avec la même échelle, sauf que cette fois vous définissez des valeurs d'échelle min / max normales pour restaurer la mise à l'échelle des utilisateurs:

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

Dans votre contexte, car la mise en page est fixe et plus grande que la fenêtre, initial-scale='+scale est peut-être nécessaire pour une alternative plus sonore 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 de rééchelonner comme vous le souhaitez dans les navigateurs Webkit sans problèmes de zoom. Je dis seulement dans le webkit car malheureusement, IE et Firefox ne prennent pas en charge la modification de la fenêtre selon cette table de compatibilité du navigateur pour les montées des vues, des dimensions et des pixels du périphérique, montre: http://www.quirksmode.org/mobile/tableViewport.html

IE a sa propre façon de modifier dynamiquement la fenêtre de configuration qui est réellement nécessaire pour que les modes snap IE soient réactifs. http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/

Donc, pour IEMobile et IE SnapMode (IE10 et 11), vous devez insérer dynamiquement un <style> en ligne dans <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: La fenêtre est définie pour une fois comme 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 la seule façon de modifier la fenêtre dans FireFox Mobile sur Android ou Gecko OS. Je viens de le tester et cela fonctionne dans le contexte d'une conception de taille fixe. (Dans «Contexte de conception réactif», la mise en page peut être redimensionnée par Transformation CSS, par exemple à la taille du bureau sur un téléphone, mais Firefox lit encore les MQ de taille du téléphone. C'est donc quelque chose qui doit être pris en compte dans le contexte RWD. )

Cependant, j'ai remarqué que certains crashs Webkit accidentels avec CSSTransform sur Android, donc je recommanderais la méthode viewport pour Safari / Chrome / Opera comme plus fiable.

De plus, afin d'obtenir une fiabilité croisée du navigateur pour la largeur de la fenêtre, vous devez également affronter / réparer l'incohérence générale entre les navigateurs pour innerWidth (notez que documentElement.clientWidth est beaucoup plus fiable pour obtenir la largeur de pixel de disposition précise sur innerWidth) Et vous devez également faire face aux divergences de devicePixelRatio comme indiqué sur la table quirksmode.org.

Mise à jour: en fait, après avoir pensé plus à une solution pour mon propre problème avec Firefox, je viens de trouver un moyen de remplacer la fenêtre dans Firefox Mobile, en utilisant document.write() , appliqué une seule fois:

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

Juste testé avec succès dans Webkit et Firefox sans problèmes de zoom. Je ne peux pas tester sur les téléphones Window, donc je ne suis pas sûr que c'est document.write fonctionne pour IEMobile …

Remplacez votre compteur avec ceci:

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

L'utilisateur-évolutif = 0 ici doit faire le travail pour vous.

Cela fonctionnera pour vous. Si cela ne fonctionne toujours pas, nous devrons étendre la fenêtre de visualisation afin de remplacer votre fenêtre et l'ajouter:

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

Contenu Web mobile adapté à l'échelle à l'aide de la méta-étiquette de la fenêtre

Je sais que c'est deux ans de retard, mais j'ai passé beaucoup de temps à travailler sur ce problème et je voudrais partager ma solution. J'ai trouvé la question originale pour être très utile, alors je pense que publier cette réponse est ma façon de redonner. Ma solution fonctionne sur un iPhone6 ​​et un Galaxy Tab de 7 ". Je ne sais pas comment cela se passe sur d'autres appareils, mais je suppose que cela devrait se comporter en grande partie.

J'ai séparé la logique de la fenêtre de visualisation en un fichier externe afin qu'il soit facile à réutiliser. Tout d'abord, voici comment utiliser 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 remis la largeur et la hauteur souhaitées d'une légère quantité (15 pixels environ) afin que mon affichage prévu soit bien encadré. Notez également le code d'utilisation que vous ne devez pas spécifier d'étiquette de fenêtre dans votre code HTML. Ma bibliothèque créera automatiquement une pour vous si elle n'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 avec le code original trouvé dans la question, vous remarquerez quelques différences. Par exemple, je ne dépend jamais de la largeur ou de la hauteur de la fenêtre. Au lieu de cela, je compte sur l'objet d'écran. Ceci est important car, lorsque vous actualisez votre page ou que vous faites pivoter votre écran, la largeur et la hauteur de la fenêtre peuvent changer, mais la largeur de l'écran et l'écran ne changent jamais. La prochaine chose que vous remarquerez, c'est que je ne fais pas le contrôle pour (ratio <1). Lors du rafraîchissement ou de la rotation de l'écran, cette vérification entraînait une incohérence, alors je l'ai retiré. En outre, j'ai inclus un gestionnaire pour la rotation de l'écran.

Enfin, j'aimerais simplement remercier la personne qui a créé cette question pour avoir jeté les bases, ce qui m'a sauvé du temps!

Si vous ne parvenez pas à obtenir un comportement cohérent à travers les périphériques en modifiant la méta-étiquette de la fenêtre de visualisation, il est possible de zoomer sans modifier les dimensions à l'aide de transformées 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 tous les préfixes de fournisseurs autres que le WebKit ici afin de le simplifier.

Pour centrer le div divisé, vous pouvez utiliser les transformes 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'; 

Réglez la hauteur et la largeur des éléments parents de div ( html et body ) à 100% et zéro sur la marge.

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

Ensuite, parce que 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 la box-sizing: border-box sur la div.

Parce que vous voulez une marge supérieure de 3px sur le positionnement div relatif du div, vous obtiendrez une hauteur de 3px trop grande. Pour réparer cela, utilisez le positionnement absolu sur le div et réglez le top, left et bottom to 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 .

MISE À JOUR: Je pense que j'ai mal compris la question. Voici un exemple de code qui règle le «zoom» en fonction de la fenêtre d'affichage de l'appareil

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

Prenez note que j'ai utilisé jquery pour ajouter la méta-étiquette car j'ai l'impression d'utiliser l'ajout de vanille.

Une chose que j'ai remarquée, c'est que si vous codiez le code HTML et le modifiez via JS, le document n'appliquera pas la correction (vérification des besoins). J'ai pu le changer via JS s'il n'y a pas de tag précédent dans le HTML, donc j'ai utilisé ajouté.

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.

MISE À JOUR: Je pense que j'ai mal compris la question. Voici un exemple de code qui règle le «zoom» en fonction de la fenêtre d'affichage de l'appareil

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

Prenez note que j'ai utilisé jquery pour ajouter la méta-étiquette car j'ai l'impression d'utiliser l'ajout de vanille.

Une chose que j'ai remarquée, c'est que si vous codiez le code HTML et le modifiez via JS, le document n'appliquera pas la correction (vérification des besoins). J'ai pu le changer via JS s'il n'y a pas de tag précédent dans le HTML, donc j'ai utilisé ajouté.

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.

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

Dans le cas où cela vous aidera, j'ajouterais les 2 choses suivantes à l'AutoViewport.js de Steven, afin de centrer la vue dans la fenêtre lorsque l'utilisateur est en mode paysage:

Ajouter "var viewport_margin = 0;" Comme première ligne de code (comme ligne propre avant "fonction AutoViewport () {}".

Ajouter "viewport_margin = Math.abs (current_width- (ratio * requiredWidth)) / (current_width * 2) * 100;" Après la ligne qui lit "document.querySelector ('meta [name =" viewport "]'). SetAttribute ('content', 'initial-scale =' + ratio + ', maximum-scale =' + ratio + ', minimum -scale = '+ ratio +', user-scalable = yes, width = '+ real_width); "

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

MISE À JOUR: Dans Android, mes ajouts ne semblent fonctionner qu'avec API 24+. Je ne sais pas pourquoi ils ne fonctionnent pas avec les API 19-23. Des idées?