Как сбросить масштаб / масштаб веб-приложения при изменении ориентации на iPhone? - PullRequest
93 голосов
/ 01 апреля 2010

Когда я запускаю свое приложение в портретном режиме, оно работает нормально. Затем я поворачиваюсь в пейзаж, и он масштабируется. Чтобы заставить его масштабироваться правильно для ландшафтного режима, мне нужно дважды нажать на что-то дважды, сначала полностью увеличить (обычное поведение двойного нажатия) и снова полностью уменьшить (опять же, нормальное поведение двойного нажатия) , Когда оно уменьшается, оно масштабируется до правильного НОВОГО масштаба для ландшафтного режима.

Переключение обратно в портретный режим, кажется, работает более последовательно; то есть он обрабатывает масштаб, чтобы масштаб был правильным при изменении ориентации назад на портретную.

Я пытаюсь выяснить, если это ошибка? или это можно исправить с помощью JavaScript?

С мета-содержимым области просмотра я устанавливаю начальный масштаб на 1,0, и я НЕ устанавливаю минимальный или максимальный масштаб (и не хочу). Я устанавливаю ширину на ширину устройства.

Есть идеи? Я знаю, что многие люди были бы благодарны за решение, так как оно кажется постоянной проблемой.

Ответы [ 11 ]

85 голосов
/ 17 июня 2011

Джереми Кейт ( @ adactio ) нашел хорошее решение для этого в своем блоге Ориентация и масштаб

Сохраняйте масштабируемость разметки, не устанавливая максимальный масштаб разметки.

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

Затем отключите масштабируемость с помощью javascript при загрузке до geststart , когда вы снова разрешите масштабируемость с помощью этого сценария:

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);
    }
}

Обновление 22-12-2014:
На iPad 1 это не работает, на EventListener происходит сбой. Я обнаружил, что удаление .body исправляет это:

document.addEventListener('gesturestart', function() { /* */ });
18 голосов
/ 10 мая 2012

Скотт Джел предложил фантастическое решение, которое использует акселерометр для прогнозирования изменений ориентации. Это решение очень отзывчиво и не мешает жестам масштабирования.

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

Как это работает: это исправление работает при прослушивании устройства акселерометр для прогнозирования, когда должно произойти изменение ориентации. Когда он считает изменение ориентации неизбежным, скрипт отключает пользователя масштабирование, позволяющее правильно изменить ориентацию, с масштабирование отключено. Сценарий снова восстанавливает масштаб, когда устройство либо ориентирован близко к вертикальному, либо после того, как его ориентация изменилось. Таким образом, пользовательское масштабирование никогда не отключается, пока страница находится в использовать.

Сокращенный источник:

/*! 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);
14 голосов
/ 30 апреля 2010

У меня была такая же проблема, и установка максимального масштаба = 1,0 работала для меня.

Edit: Как упомянуто в комментариях, это отключает масштабирование пользователя, за исключением случаев, когда содержимое превышает разрешение по ширине. Как уже упоминалось, это не может быть мудрым. Это также может быть желательно в некоторых случаях.

Код вида:

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0;">
3 голосов
/ 13 октября 2010

Если в окне просмотра установлена ​​ширина:

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

А затем измените ориентацию, она иногда будет случайным образом увеличиваться (особенно если вы перетаскиваете экран), чтобы исправить это, не устанавливайте ширину, которую я здесь использовал:

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

Это исправляет масштаб, что бы ни происходило, тогда вы можете использовать любое событие window.onorientationchange или, если вы хотите, чтобы оно было независимым от платформы (удобно для тестирования), метод window.innerWidth .

1 голос
/ 27 февраля 2015

Я использую эту функцию в своем проекте.

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

так что просто добавьте EventListener:

if( /iPad|iPhone|iPod|Android/i.test(navigator.userAgent) ){
    window.addEventListener("orientationchange", function() { 
        changeViewPort("maximum-scale", 1);
        changeViewPort("maximum-scale", 10);
    }
}
1 голос
/ 01 декабря 2011

Я создал рабочую демонстрацию макета ландшафта / портрета, но для того, чтобы он работал без JavaScript, необходимо отключить увеличение:

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

1 голос
/ 01 апреля 2010

MobileSafari поддерживает событие orientationchange для объекта window. К сожалению, похоже, нет способа напрямую управлять масштабированием с помощью JavaScript. Возможно, вы могли бы динамически написать / изменить тег meta, который контролирует область просмотра, но я сомневаюсь, что это сработает, это повлияет только на начальное состояние страницы. Возможно, вы могли бы использовать это событие для фактического изменения размера вашего контента с помощью CSS. Удачи!

0 голосов
/ 23 октября 2016

Нашел очень легко внедряемое исправление. Установите фокус на текстовый элемент, размер шрифта которого составляет 50 пикселей при заполнении формы. Кажется, это не работает, если текстовый элемент скрыт, но скрыть этот элемент легко, установив свойства цвета элементов, чтобы они не были прозрачными.

0 голосов
/ 19 июня 2015

Вот еще один способ сделать это, который, кажется, работает хорошо.

  1. Установите метатег, чтобы ограничить область просмотра масштабом = 1, что предотвращает масштабирование:

  2. С помощью javascript измените метатег через 1/2 секунды на разрешить масштабирование:

    setTimeout (function () {document.querySelector ("meta [name = viewport]"). SetAttribute ('content', 'width = device-width, initial-scale = 1');}, 500);

  3. Снова с javascript, при изменении ориентации, перезагрузите страницу:

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

Каждый раз, когда вы переориентируете устройство, страница перезагружается, изначально без масштабирования. Но спустя 1/2 секунды способность к зуму восстанавливается.

0 голосов
/ 09 августа 2012

Я нашел новый обходной путь, отличный от любого другого, который я видел, отключив встроенное масштабирование iOS и вместо этого реализовав функцию масштабирования в JavaScript.

Отличный фон для различных других решенийПроблема масштабирования / ориентации принадлежит Сержио Лопесу: Исправлена ​​известная ошибка масштабирования в iOS при изменении ориентации на портретную .

<!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 /323178/kak-sdelat-div-ne-bolshe-ego-soderzhimogo */
            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>

Это можно улучшить, но для моих нужд это позволяет избежатьосновные недостатки, которые возникают со всеми другими решениями, которые я видел.До сих пор я тестировал его только с помощью мобильного Safari на iPad 2 с iOS4.

Focus () / blur () - это обходной путь для предотвращения случайной блокировки функции масштабирования, которая может возникнуть после изменения ориентации имасштабирование несколько раз.

Установка document.body.style вызывает перерисовку на весь экран, что позволяет избежать случайных периодических проблем, при которых перерисовка не удается после масштабирования.

...