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

96

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

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

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

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

Любые идеи? Я знаю, что многие люди были бы благодарны за решение, потому что это постоянная проблема.

Элизабет
источник
1
Идеальное решение: без javascript! stackoverflow.com/a/8727440/805787
Steeven 05

Ответы:

89

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

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

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

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

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 это не работает, это не работает в слушателе событий. Я обнаружил, что удаление .bodyисправлений, которые:

document.addEventListener('gesturestart', function() { /* */ });
снобожохан
источник
4
Неужто это лучше, чем отключение зума ?! Лучшее исправление, которое я нашел :)
danwellman
Хм, это все равно отключает возможность масштабирования. У кого-нибудь есть простое решение, которое этого не делает?
Брэд Свердфегер,
Это работает, однако я заметил, что проблема возникает снова, если я использую жест масштабирования, а затем вращаю экран. Не знаю, как это исправить.
Nilesh 05
3
Оно работает. Тем не менее, я заметил, что пользователю приходится дважды зажимать пальцы, чтобы увеличить масштаб. Я предполагаю, что это потому, что maximum-scale=1.0действует после начала жеста. Есть ли способ исправить это?
LandonSchropp
3
Это не работает по двум причинам: 1) он отключает жест с номером 1, из-за чего пользователю нужно дважды сделать жест. 2) он прерывается после того, как пользователь удваивает первый жест, поэтому он действительно работает только в том случае, если пользователь вообще никогда не жестикулирует. - всем стоит посмотреть на решение Эндрю Эшбахера ниже. Это действительно работает.
tmsimont 01
18

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

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);
Эндрю Ашбахер
источник
Ницца! Похоже на элегантное решение.
Элизабет
1
это должен быть принятый ответ !!!! Мне жаль, что я не увидел это первым, прежде чем тратить час на решения выше :)
tmsimont 01
1
после дальнейшего тестирования это своего рода ненадежное решение :( оно непоследовательно, и, просмотрев код, я могу понять, почему ... определенный "порог" движения не всегда достигается, особенно если вы держите ipad в угол при вращении
tmsimont 06
Это может иметь неприятные последствия для любого, кто использует блокировку вращения ... они могут держать телефон под определенным углом и терять возможность масштабирования - пользователь понятия не имеет, почему
1owk3y
14

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

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

Код области просмотра:

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0;">
ракалоф
источник
Хорошее решение. Хорошо справляется с поддержанием постоянного уровня масштабирования страницы (относительно ширины устройства) за счет изменения ориентации. Спасибо, что поделились этим!
Люк Стивенсон
17
Обратной стороной является то, что пользователи с ограниченными возможностями не могут увеличивать масштаб вашего сайта!
Джесс Джейкобс
Я заметил, что все эти методы, похоже, не позволяют CSS на основе медиа-запросов правильно регистрировать новую ширину устройства (например, @media all и (max-width: 479px)
mheavers
2
Убивать пользователя зумом - очень плохая идея. см. решение Эндрю Эшбахера ниже
tmsimont 01
Не уверен насчет iPhone, но на iPad это не решает проблему, это просто не позволяет пользователю вручную уменьшать масштаб, когда браузер увеличивает масштаб при изменении ориентации.
Alejo
3

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

<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

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

Ави Лен
источник
3
Спасибо! Да, я пробовал динамически изменять значения области просмотра метатега, но ничего не сделал. Мне кажется, что если вы поворачиваете в альбомную ориентацию, вы хотите, чтобы он правильно масштабировался, чтобы сохранить масштаб, чтобы страница вписывалась в окно Safari. Мне кажется очень странным, что это не поведение по умолчанию!
Элизабет
1

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

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

так что просто addEventListener:

if( /iPad|iPhone|iPod|Android/i.test(navigator.userAgent) ){
    window.addEventListener("orientationchange", function() { 
        changeViewPort("maximum-scale", 1);
        changeViewPort("maximum-scale", 10);
    }
}
Джеймс Янг
источник
0

Я нашел новый обходной путь, отличный от любого другого, что я видел, путем отключения встроенного масштабирования 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 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>

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

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

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

робот
источник
0

Элизабет, вы можете динамически изменять содержимое области просмотра, добавляя свойство id в метатег:

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

Тогда вы просто можете позвонить по javascript:

document.getElementById("view").setAttribute('content','user-scalable=yes, width=device-width, minimum-scale=1, maximum-scale=10');
M Penades
источник
@bridgestew, если вы хотите изменить масштаб или окно просмотра динамически, используйте прокрутку subview, содержащуюся в uiwebview. Я добавил образец фрагмента в другую ветку: ссылка
M Penades
4
@ Элизабет, у тебя это работает? У меня не сбрасывает зум при переключении в альбомный режим.
instanceof me
0

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

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

    <meta name = "viewport" content = "width = ширина устройства, начальный масштаб = 1, минимальный масштаб = 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 = функция () {window.location.reload ();};

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

Марк Минога
источник
6
Ответить на вопрос через 5 лет после того, как он был задан, - это кое-что… К сожалению, в 2015 году Интернет не работает. Вы НЕ перезагружаете страницу, когда пользователь поворачивает свое устройство.
Пьер
0

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

Деллсмаш
источник