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上失败了。我发现移除
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支持
找到了一个非常容易实现的修复程序。完成表单后,将焦点设置为字体大小为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