web-dev-qa-db-fra.com

Comment réinitialiser l'échelle/le zoom d'une application Web lors d'un changement d'orientation sur l'iPhone?

Lorsque je lance mon application en mode portrait, cela fonctionne bien. Ensuite, je tourne dans le paysage et il est agrandi. Pour qu'il s'adapte correctement au mode paysage, je dois taper deux fois sur quelque chose, d'abord pour effectuer un zoom avant (comportement normal en mode double frappe) et de nouveau pour effectuer un zoom avant (encore une fois, le comportement normal en mode double frappe) . Lorsqu'il effectue un zoom arrière, il effectue un zoom arrière sur la nouvelle échelle correcte pour le mode paysage.

Revenir au portrait semble fonctionner de manière plus cohérente; c’est-à-dire qu’il gère le zoom de sorte que l’échelle soit correcte lorsque l’orientation redevient Portrait.

J'essaie de savoir s'il s'agit d'un bug? ou si c'est quelque chose qui peut être corrigé avec JavaScript?

Avec le méta-contenu de la fenêtre d'affichage, je règle l'échelle initiale sur 1.0 et je ne fixe PAS l'échelle minimale ou maximale (et je ne le veux pas). Je règle la largeur sur device-width.

Des idées? Je sais que beaucoup de gens seraient reconnaissants d'avoir une solution car cela semble être un problème persistant.

92
Elisabeth

Jeremy Keith ( @adactio ) a une bonne solution pour cela sur son blog Orientation et échelle

Conservez le marquage évolutif en ne définissant pas une échelle maximale dans le marquage.

<meta name="viewport" content="width=device-width, initial-scale=1">

Ensuite, désactivez l’évolutivité avec javascript au chargement jusqu’à gesturestart lorsque vous autoriserez à nouveau l’évolutivité avec ce script:

if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
    var viewportmeta = document.querySelector('meta[name="viewport"]');
    if (viewportmeta) {
        viewportmeta.content = 'width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0';
        document.body.addEventListener('gesturestart', function () {
            viewportmeta.content = 'width=device-width, minimum-scale=0.25, maximum-scale=1.6';
        }, false);
    }
}

Mise à jour 22-12-2014:
Sur un iPad 1, cela ne fonctionne pas, il échoue sur eventlistener. J'ai constaté que la suppression de .body corrige cela:

document.addEventListener('gesturestart', function() { /* */ });
85
snobojohan

Scott Jehl est venu avec une solution fantastique qui utilise l'accéléromètre pour anticiper les changements d'orientation. Cette solution est très réactive et n'interfère pas avec les gestes de zoom.

https://github.com/scottjehl/iOS-Orientationchange-Fix

Comment ça marche: Ce correctif fonctionne en écoutant le .__ de l'appareil. accéléromètre pour prédire quand un changement d’orientation est sur le point de se produire . Lorsqu'il estime qu'un changement d'orientation est imminent, le script désactive l'utilisateur zoom, permettant au changement d’orientation de se produire correctement, avec zoom désactivé. Le script restaure le zoom une fois que l'appareil est devenu soit orienté proche de la verticale, soit après son orientation a modifié. De cette manière, le zoom de l'utilisateur n'est jamais désactivé tant que la page est insérée utilisation.

Source minifiée:

/*! A fix for the iOS orientationchange zoom bug. Script by @scottjehl, rebound by @wilto.MIT License.*/(function(m){if(!(/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>-1)){return}var l=m.document;if(!l.querySelector){return}var n=l.querySelector("meta[name=viewport]"),a=n&&n.getAttribute("content"),k=a+",maximum-scale=1",d=a+",maximum-scale=10",g=true,j,i,h,c;if(!n){return}function f(){n.setAttribute("content",d);g=true}function b(){n.setAttribute("content",k);g=false}function e(o){c=o.accelerationIncludingGravity;j=Math.abs(c.x);i=Math.abs(c.y);h=Math.abs(c.z);if(!m.orientation&&(j>7||((h>6&&i<8||h<8&&i>6)&&j>5))){if(g){b()}}else{if(!g){f()}}}m.addEventListener("orientationchange",f,false);m.addEventListener("devicemotion",e,false)})(this);
18
Andrew Ashbacher

J'ai eu le même problème et régler l'échelle maximum = 1.0 a fonctionné pour moi.

Edit: Comme mentionné dans les commentaires, cela désactive le zoom utilisateur, sauf lorsque le contenu dépasse la résolution en largeur. Comme mentionné, cela pourrait ne pas être sage. Cela peut également être souhaité dans certains cas.

Le code de la fenêtre d'affichage:

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0;">
14
rakaloof

Si vous avez la largeur définie dans la fenêtre:

<meta name = "viewport" content = "width=device-width; initial-scale=1.0;
 maximum-scale=1.0;" />

Et puis changez l’orientation il fera un zoom au hasard parfois (surtout si vous faites glisser sur l’écran) pour résoudre ce problème, ne définissez pas de largeur ici, j’ai utilisé:

<meta id="viewport" name="viewport" content="initial-scale=1.0; user-scalable=0;
minimum-scale=1.0; maximum-scale=1.0" />

Cela corrige le zoom quoi qu'il arrive, vous pouvez utiliser soit l'événement window.onorientation ou, si vous voulez qu'il soit indépendant de la plate-forme (pratique pour tester) la méthode window.innerWidth .

3
psyder

J'utilise cette fonction dans mon projet.

function changeViewPort(key, val) {
    var reg = new RegExp(key, "i"), oldval = document.querySelector('meta[name="viewport"]').content;
    var newval = reg.test(oldval) ? oldval.split(/,\s*/).map(function(v){ return reg.test(v) ? key+"="+val : v; }).join(", ") : oldval+= ", "+key+"="+val ;
    document.querySelector('meta[name="viewport"]').content = newval;
}

alors ajoutez simplementEventListener:

if( /iPad|iPhone|iPod|Android/i.test(navigator.userAgent) ){
    window.addEventListener("orientationchange", function() { 
        changeViewPort("maximum-scale", 1);
        changeViewPort("maximum-scale", 10);
    }
}
1
James Yang

MobileSafari prend en charge l'événement orientationchange sur l'objet window. Malheureusement, il ne semble pas y avoir de moyen de contrôler directement le zoom via JavaScript. Vous pourriez peut-être écrire/modifier dynamiquement la balise meta qui contrôle la fenêtre d'affichage - mais je doute que cela fonctionne, cela affecte uniquement l'état initial de la page. Vous pourriez peut-être utiliser cet événement pour redimensionner votre contenu à l'aide de CSS. Bonne chance!

1
Avi Flax

J'ai créé une démonstration de travail d'une mise en page paysage/portrait mais le zoom doit être désactivé pour que cela fonctionne sans JavaScript:

http://matthewjamestaylor.com/blog/ipad-layout-with-landscape-portrait-modes

1

J'ai trouvé une nouvelle solution de contournement, différente de toutes les solutions que j'ai déjà vues, en désactivant le zoom iOS natif et en implémentant la fonctionnalité de zoom en JavaScript.

Sérgio Lopes décrit très bien les diverses autres solutions au problème de zoom/orientation: Un correctif du fameux bug de zoom iOS sur le changement d’orientation en portrait .

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" id="viewport" content="user-scalable=no,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
    <title>Robocat mobile Safari zoom fix</title>
    <style>
        body {
            padding: 0;
            margin: 0;
        }
        #container {
            -webkit-transform-Origin: 0px 0px;
            -webkit-transform: scale3d(1,1,1);
            /* shrink-to-fit needed so can measure width of container http://stackoverflow.com/questions/450903/make-css-div-width-equal-to-contents */
            display: inline-block;
            *display: inline;
            *zoom: 1;
        }
        #zoomfix {
            opacity: 0;
            position: absolute;
            z-index: -1;
            top: 0;
            left: 0;
        }
    </style>
</head>

<body>
    <input id="zoomfix" disabled="1" tabIndex="-1">
    <div id="container">
        <style>
            table {
                counter-reset: row cell;
                background-image: url(http://upload.wikimedia.org/wikipedia/commons/3/38/JPEG_example_JPG_RIP_010.jpg);
            }
            tr {
                counter-increment: row;
            }
            td:before {
                counter-increment: cell;
                color: white;
                font-weight: bold;
                content: "row" counter(row) ".cell" counter(cell);
            }
        </style>
        <table cellspacing="10">
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
        </table>
    </div>

    <script>
    (function() {
        var viewportScale = 1;
        var container = document.getElementById('container');
        var scale, originX, originY, relativeOriginX, relativeOriginY, windowW, windowH, containerW, containerH, resizeTimer, activeElement;
        document.addEventListener('gesturestart', function(event) {
            scale = null;
            originX = event.pageX;
            originY = event.pageY;
            relativeOriginX = (originX - window.pageXOffset) / window.innerWidth;
            relativeOriginY = (originY - window.pageYOffset) / window.innerHeight;
            windowW = window.innerWidth;
            windowH = window.innerHeight;
            containerW = container.offsetWidth;
            containerH = container.offsetHeight;
        });
        document.addEventListener('gesturechange', function(event) {
            event.preventDefault();
            if (originX && originY && event.scale && event.pageX && event.pageY) {
                scale = event.scale;
                var newWindowW = windowW / scale;
                if (newWindowW > containerW) {
                    scale = windowW / containerW;
                }
                var newWindowH = windowH / scale;
                if (newWindowH > containerH) {
                    scale = windowH / containerH;
                }
                if (viewportScale * scale < 0.1) {
                    scale = 0.1/viewportScale;
                }
                if (viewportScale * scale > 10) {
                    scale = 10/viewportScale;
                }
                container.style.WebkitTransformOrigin = originX + 'px ' + originY + 'px';
                container.style.WebkitTransform = 'scale3d(' + scale + ',' + scale + ',1)';
            }
        });
        document.addEventListener('gestureend', function() {
            if (scale && (scale < 0.95 || scale > 1.05)) {
                viewportScale *= scale;
                scale = null;
                container.style.WebkitTransform = '';
                container.style.WebkitTransformOrigin = '';
                document.getElementById('viewport').setAttribute('content', 'user-scalable=no,initial-scale=' + viewportScale + ',minimum-scale=' + viewportScale + ',maximum-scale=' + viewportScale);
                document.body.style.WebkitTransform = 'scale3d(1,1,1)';
                // Without zoomfix focus, after changing orientation and zoom a few times, the iOS viewport scale functionality sometimes locks up (and completely stops working).
                // The reason I thought this hack would work is because showing the keyboard is the only way to affect the viewport sizing, which forces the viewport to resize (even though the keyboard doesn't actually get time to open!).
                // Also discovered another amazing side effect: if you have no meta viewport element, and focus()/blur() in gestureend, zoom is disabled!! Wow!
                var zoomfix = document.getElementById('zoomfix');
                zoomfix.disabled = false;
                zoomfix.focus();
                zoomfix.blur();
                setTimeout(function() {
                    zoomfix.disabled = true;
                    window.scrollTo(originX - relativeOriginX * window.innerWidth, originY - relativeOriginY * window.innerHeight);
                    // This forces a repaint. repaint *intermittently* fails to redraw correctly, and this fixes the problem.
                    document.body.style.WebkitTransform = '';
                }, 0);
            }
        });
    })();
    </script>
</body>
</html>

Cela pourrait être amélioré, mais pour mes besoins, cela évite les inconvénients majeurs qui se produisent avec toutes les autres solutions que j'ai vues. Jusqu'à présent, je ne l'ai testé qu'avec Safari mobile sur un iPad 2 avec iOS4.

Focus ()/blur () est une solution de contournement destinée à éviter le blocage occasionnel de la fonctionnalité de zoom, qui peut se produire après plusieurs changements d’orientation et de zooms.

Définir document.body.style oblige à repeindre en plein écran, ce qui évite les problèmes intermittents occasionnels dans les cas où le repeint échoue après le zoom.

0
robocat

Elisabeth, vous pouvez modifier le contenu de la fenêtre de manière dynamique en ajoutant la propriété "id" à la métabalise:

<meta name="viewport" id="view" content="user-scalable=yes, width=device-width minimum-scale=1, maximum-scale=1" />

Ensuite, vous pouvez simplement appeler en javascript: 

document.getElementById("view").setAttribute('content','user-scalable=yes, width=device-width, minimum-scale=1, maximum-scale=10');
0
M Penades

Voici une autre façon de le faire, qui semble bien fonctionner.

  1. Définissez la balise meta pour limiter la fenêtre d'affichage à l'échelle = 1, ce qui empêche le zoom:

    <meta name = "viewport" content = "width = device-width, initial-scale = 1, minimum-scale = 1, maximum-scale = 1">

  2. Avec javascript, changez la balise META une demi-seconde plus tard en Autoriser le zoom:

    setTimeout (function () {document.querySelector ("meta [nom = viewport]"). setAttribute ('contenu', 'largeur = largeur du périphérique, initial-scale = 1'); 500);

  3. Encore une fois avec javascript, lors du changement d'orientation, rechargez la page:

    window.onorientationchange = function () {window.location.reload ();};

Chaque fois que vous réorientez l'appareil, la page se recharge, initialement sans zoom. Mais 1/2 seconde plus tard, la possibilité de zoomer est restaurée. 

0
Mark Lamprey

Trouvé un correctif très facilement mis en œuvre. Définissez le focus sur un élément de texte ayant une taille de police de 50 px à la fin du formulaire. Cela ne semble pas fonctionner si l'élément de texte est masqué, mais le masquage de cet élément se fait facilement en définissant les propriétés de couleur de l'élément de manière à ce qu'elles ne soient pas opaques. 

0
Dellsmash