Javascript 如何在 iPhone 上的方向更改时重置 Web 应用程序的比例/缩放?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2557801/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I reset the scale/zoom of a web app on an orientation change on the iPhone?
提问by Elisabeth
When I start my app in portrait mode, it works fine. Then I rotate into landscape and it's scaled up. To get it to scale correctly for the landscape mode I have to double tap on something twice, first to zoom all the way in (the normal double tap behavior) and again to zoom all the way out (again, the normal double tap behavior). When it zooms out, it zooms out to the correct NEW scale for landscape mode.
当我以纵向模式启动我的应用程序时,它工作正常。然后我旋转到横向并按比例放大。为了让它在横向模式下正确缩放,我必须双击某些东西,首先放大(正常的双击行为)然后再一次放大(再次,正常的双击行为) . 当它缩小时,它会缩小到正确的横向模式新比例。
Switching back to portrait seems to work more consistently; that is, it handles the zoom so that the scale is correct when the orientation changes back to portrait.
切换回纵向似乎更一致;也就是说,它处理缩放,以便当方向变回纵向时比例是正确的。
I am trying to figure out if this is a bug? or if this is something that can be fixed with JavaScript?
我想弄清楚这是否是一个错误?或者这是否可以用 JavaScript 修复?
With the viewport meta content, I am setting the initial-scale to 1.0 and I am NOT setting minimum or maximum scale (nor do I want to). I am setting the width to device-width.
对于视口元内容,我将初始比例设置为 1.0,并且我没有设置最小或最大比例(我也不想)。我将宽度设置为设备宽度。
Any ideas? I know a lot of people would be grateful to have a solution as it seems to be a persistent problem.
有任何想法吗?我知道很多人会很高兴有一个解决方案,因为它似乎是一个长期存在的问题。
采纳答案by snobojohan
Jeremy Keith (@adactio) has a good solution for this on his blog Orientation and scale
Jeremy Keith ( @adactio) 在他的博客Orientation and scale上有一个很好的解决方案
Keep the Markup scalable by not setting a maximum-scale in markup.
通过不在标记中设置最大比例来保持标记的可扩展性。
<meta name="viewport" content="width=device-width, initial-scale=1">
Then disable scalability with javascript on load until gesturestartwhen you allow scalability again with this script:
然后在加载时使用 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);
}
}
Update 22-12-2014:
On an iPad 1 this doesnt work, it fails on the eventlistener. I've found that removing .bodyfixes that:
2014 年 12 月 22 日更新:在 iPad 1 上这不起作用,它在事件监听器上失败。我发现删除.body修复了:
document.addEventListener('gesturestart', function() { /* */ });
回答by Andrew Ashbacher
Scott Jehlcame up with a fantastic solution that uses the accelerometer to anticipate orientation changes. This solution is very responsive and does not interfere with zoom gestures.
Scott Jehl提出了一个奇妙的解决方案,它使用加速度计来预测方向变化。此解决方案响应迅速且不会干扰缩放手势。
https://github.com/scottjehl/iOS-Orientationchange-Fix
https://github.com/scottjehl/iOS-Orientationchange-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.
工作原理:此修复程序通过侦听设备的加速度计来预测即将发生方向变化的时间。当它认为即将发生方向更改时,脚本会禁用用户缩放,允许正确发生方向更改,同时禁用缩放。一旦设备的方向接近直立,或者在其方向发生变化后,脚本将再次恢复缩放。这样,当页面正在使用时,用户缩放永远不会被禁用。
Minified source:
缩小的来源:
/*! 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);
回答by rakaloof
I had the same problem, and setting the maximum-scale=1.0 worked for me.
我遇到了同样的问题,设置maximum-scale=1.0 对我有用。
Edit: As mentioned in the comments this does disable user zoom except when the content exceeds the width-resolution. As mentioned, this might not be wise. It might also be desired in some cases.
编辑:如评论中所述,这会禁用用户缩放,除非内容超过宽度分辨率。如前所述,这可能不明智。在某些情况下也可能需要它。
The viewport code:
视口代码:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0;">
回答by psyder
If you have the width set in the viewport :
如果您在视口中设置了宽度:
<meta name = "viewport" content = "width=device-width; initial-scale=1.0;
maximum-scale=1.0;" />
And then change the orientation it will randomly zoom in sometimes (especially if you are dragging on the screen) to fix this don't set a width here I used :
然后改变方向,它有时会随机放大(特别是如果你在屏幕上拖动)来解决这个问题,不要在这里设置宽度我使用:
<meta id="viewport" name="viewport" content="initial-scale=1.0; user-scalable=0;
minimum-scale=1.0; maximum-scale=1.0" />
This fixes the zoom whatever happens then you can use either window.onorientationchange event or if you want it to be platform independant (handy for testing) the window.innerWidthmethod.
无论发生什么,这都会修复缩放,然后您可以使用 window.onorientationchange 事件,或者如果您希望它与平台无关(便于测试)window.innerWidth方法。
回答by Avi Flax
MobileSafari supports the orientationchangeevent on the windowobject. Unfortunately there doesn't seem to be a way to directly control the zoom via JavaScript. Perhaps you could dynamically write/change the metatag which controls the viewport — but I doubt that would work, it only affects the initial state of the page. Perhaps you could use this event to actually resize your content using CSS. Good luck!
MobileSafari 支持对象orientationchange上的事件window。不幸的是,似乎没有一种方法可以通过 JavaScript 直接控制缩放。也许您可以动态编写/更改meta控制视口的标签——但我怀疑这是否可行,它只会影响页面的初始状态。也许您可以使用此事件使用 CSS 实际调整内容的大小。祝你好运!
回答by Matthew James Taylor
I created a working demo of a landscape/portrait layout but the zoom must be disabled for it to work without JavaScript:
我创建了一个横向/纵向布局的工作演示,但必须禁用缩放才能在没有 JavaScript 的情况下工作:
http://matthewjamestaylor.com/blog/ipad-layout-with-landscape-portrait-modes
http://matthewjamestaylor.com/blog/ipad-layout-with-landscape-portrait-modes
回答by James Yang
I've been using this function in my project.
我一直在我的项目中使用这个功能。
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;
}
so just addEventListener:
所以只需添加EventListener:
if( /iPad|iPhone|iPod|Android/i.test(navigator.userAgent) ){
window.addEventListener("orientationchange", function() {
changeViewPort("maximum-scale", 1);
changeViewPort("maximum-scale", 10);
}
}
回答by Mark Lamprey
Here's another way to do it, which seems to work well.
这是另一种方法,它似乎运作良好。
Set the meta tag to restrict the viewport to scale=1, which prevents zooming:
< meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
With javascript, change the meta tag 1/2 second later to allow zooming:
setTimeout(function(){ document.querySelector("meta[name=viewport]").setAttribute('content','width=device-width, initial-scale=1');}, 500);
Again with javascript, on orientation change, reload the page:
window.onorientationchange = function(){window.location.reload();};
设置元标记以将视口限制为 scale=1,从而防止缩放:
< meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
使用 javascript,在 1/2 秒后更改元标记以允许缩放:
setTimeout(function(){ document.querySelector("meta[name=viewport]").setAttribute('content','width=device-width, initial-scale=1');}, 500);
再次使用 javascript,在方向改变时,重新加载页面:
window.onorientationchange = function(){window.location.reload();};
Every time you reorient the device, the page reloads, initially without zoom. But 1/2 second later, ability to zoom is restored.
每次您重新定向设备时,页面都会重新加载,最初没有缩放。但 1/2 秒后,缩放能力恢复。
回答by Dellsmash
Found a very easily implemented fix. Set the focus to a text element that has a font size of 50px on completion of the form. It does not seem to work if the text element is hidden but hiding this element is easily done by setting the elements color properties to have no opacity.
找到了一个非常容易实现的修复程序。在完成表单时将焦点设置为字体大小为 50px 的文本元素。如果文本元素被隐藏,它似乎不起作用,但通过将元素颜色属性设置为没有不透明度,可以轻松隐藏此元素。
回答by robocat
I have found a new workaround, different from any other that I have seen, by disabling the native iOS zoom, and instead implementing zoom functionality in JavaScript.
我找到了一种新的解决方法,与我见过的任何其他方法不同,通过禁用本机 iOS 缩放,而是在 JavaScript 中实现缩放功能。
An excellent background on the various other solutions to the zoom/orientation problem is by Sérgio Lopes: A fix to the famous iOS zoom bug on orientation change to portrait.
关于缩放/方向问题的各种其他解决方案的优秀背景是 Sérgio Lopes:修复了著名的 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>
It could be improved, but for my needs it avoids the major drawbacks that occur with all the other solutions I have seen. So far I have only tested it using mobile Safari on an iPad 2 with iOS4.
它可以改进,但对于我的需要,它避免了我见过的所有其他解决方案出现的主要缺点。到目前为止,我只在带有 iOS4 的 iPad 2 上使用移动 Safari 对其进行了测试。
The focus()/blur() is a workaround to prevent the occasional lockup of the zoom functionality which can occur after changing orientation and zooming a few times.
focus()/blur() 是一种解决方法,可防止在更改方向和缩放几次后偶尔会发生缩放功能的锁定。
Setting the document.body.style forces a full screen repaint, which avoids an occasional intermittent problems where the repaint badly fails after zoom.
设置 document.body.style 强制全屏重绘,这避免了偶尔出现的间歇性问题,即缩放后重绘严重失败。

