关于javascript:如何在iPhone上更改方向更改时重置Web应用程序的缩放/缩放?

How do I reset the scale/zoom of a web app on an orientation change on the iPhone?

当我以肖像模式启动应用程序时,它工作正常。然后我旋转成景观,它被放大了。要使其正确缩放为横向模式,我必须双击某个对象两次,首先放大一路(正常的双击行为),然后再次缩放一路(再次,正常的双击行为)。当它缩小时,它会缩小到景观模式的正确新比例。

切换回"纵向"似乎工作更为一致;也就是说,它处理缩放,以便在方向切换回"纵向"时比例正确。

我想知道这是否是一个bug?或者如果这是可以用JavaScript修复的东西?

对于视区元内容,我将初始比例设置为1.0,而不设置最小或最大比例(我也不想设置)。我正在将宽度设置为设备宽度。

有什么想法吗?我知道很多人会感激有一个解决方案,因为它似乎是一个持久的问题。


Jeremy Keith(@adactio)在他的博客定位和规模上有一个很好的解决方案。

通过不在标记中设置最大比例来保持标记的可伸缩性。

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

然后在加载时禁用JavaScript的可伸缩性,直到GestureStart再次允许使用此脚本进行可伸缩性:

1
2
3
4
5
6
7
8
9
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);
    }
}

2014年12月22日更新:在ipad 1上,这不起作用,它在EventListener上失败了。我发现移除.body可以修复:

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


斯科特·杰尔想出了一个奇妙的解决方案,利用加速度计来预测方向的变化。此解决方案响应迅速,不会干扰缩放手势。

https://github.com/scottjehl/ios-oritionchange-fix

How it works: This fix works by listening to the device's
accelerometer to predict when an orientation change is about to occur.
When it deems an orientation change imminent, the script disables user
zooming, allowing the orientation change to occur properly, with
zooming disabled. The script restores zoom again once the device is
either oriented close to upright, or after its orientation has
changed. This way, user zooming is never disabled while the page is in
use.

缩小源:

1
/*! 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.0对我来说很有用。

编辑:如注释中所述,这将禁用用户缩放,除非内容超出宽度分辨率。如前所述,这可能不明智。在某些情况下也可能需要。

视区代码:

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


如果在视区中设置了宽度:

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

然后改变方向,它有时会随机放大(特别是当你在屏幕上拖动时),以解决这个问题。不要在这里设置我使用的宽度:

1
2
<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
2
3
4
5
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;
}

所以只需添加事件侦听器:

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

我创建了一个横向/纵向布局的工作演示,但必须禁用缩放,才能在没有javascript的情况下工作:

http://matthewjamestaylor.com/blog/ipad-layout-with-landscape-pitalt-modes/博客


MobileSafari支持window对象上的orientationchange事件。不幸的是,似乎没有一种方法可以通过javascript直接控制缩放。也许您可以动态地编写/更改控制视区的meta标记—但我怀疑这是否可行,它只影响页面的初始状态。也许您可以使用这个事件来实际地使用CSS调整内容的大小。祝你好运!


找到了一个非常容易实现的修复程序。完成表单后,将焦点设置为字体大小为50px的文本元素。如果文本元素是隐藏的,它似乎不起作用,但是通过将元素颜色属性设置为不透明度,很容易隐藏该元素。


这是另一种方法,看起来效果很好。

  • 将meta标记设置为将视区限制为scale=1,这将阻止缩放:

  • 使用javascript,在1/2秒后将meta标记更改为允许缩放:

    settimeout(function()document.queryselector("meta[name=viewport]").setattribute("content","width=device width,initial scale=1");,500);

  • 再次使用javascript,在方向更改时,重新加载页面:

    window.onOrientationChange=function()window.location.reload();

  • 每次重新调整设备方向时,页面都会重新加载,最初不进行缩放。但1/2秒后,缩放功能恢复。


    我发现了一种新的解决方法,与我见过的任何其他解决方法不同,它是禁用本机iOS缩放,而不是在javascript中实现缩放功能。

    S_rgio Lopes是解决变焦/定向问题的各种其他解决方案的优秀背景:修复了著名的iOS变焦错误,即方向改变为肖像。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    <!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" />
        Robocat mobile Safari zoom fix
        <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">
       
            <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>
       

       
        (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);
                }
            });
        })();
       
    </body>
    </html>

    它可以改进,但是为了我的需要,它避免了我所看到的所有其他解决方案中出现的主要缺点。到目前为止,我只在带有iOS4的iPad2上使用MobileSafari测试过它。

    focus()/blur()是一种解决方法,可以防止在更改方向和缩放几次后偶尔锁定缩放功能。

    设置document.body.style将强制进行全屏重新绘制,这样可以避免在缩放后重新绘制严重失败时偶尔出现间歇性问题。


    Elisabeth您可以通过向metatag添加"id"属性来动态更改视区内容:

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

    然后您可以通过javascript调用:

    4