【问题标题】:How do I reset the scale/zoom of a web app on an orientation change on the iPhone?如何在 iPhone 上更改方向时重置 Web 应用程序的缩放/缩放?
【发布时间】:2011-02-03 04:59:53
【问题描述】:

当我以纵向模式启动我的应用程序时,它运行良好。然后我旋转成横向,它被放大了。为了让它在横向模式下正确缩放,我必须双击某物两次,首先一直放大(正常的双击行为),然后再次放大(再次,正常的双击行为) .当它缩小时,它会缩小到适用于横向模式的正确新比例。

切换回纵向似乎更稳定;也就是说,它会处理缩放,以便在方向变回纵向时缩放正确。

我想知道这是否是一个错误?或者这是否可以通过 JavaScript 解决?

对于视口元内容,我将初始比例设置为 1.0,并且我没有设置最小或最大比例(我也不想)。我将宽度设置为设备宽度。

有什么想法吗?我知道很多人会很高兴有一个解决方案,因为这似乎是一个长期存在的问题。

【问题讨论】:

标签: javascript iphone ipad orientation


【解决方案1】:

Jeremy Keith (@adactio) 在他的博客 Orientation and scale 上有一个很好的解决方案@

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

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

然后在加载时使用 javascript 禁用可扩展性,直到 gesturestart 当您再次使用此脚本允许可扩展性时:

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 年 22 月 12 日更新:
在 iPad 1 上这不起作用,它在事件监听器上失败。我发现删除 .body 可以解决以下问题:

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

【讨论】:

  • 这肯定比禁用缩放更好?!我发现的最好的解决方法:)
  • 嗯,这仍然会禁用缩放功能。有没有人有一个不这样做的简单解决方案?
  • 它可以工作,但是我观察到如果我使用捏缩放手势然后旋转屏幕,问题会再次出现。不知道如何解决。
  • 它有效。但是,我注意到用户必须捏开两次才能进行缩放。我猜这是因为maximum-scale=1.0 在手势开始后仍然有效。有没有办法解决这个问题?
  • 这不起作用有两个原因:1)它禁用了第 1 号手势,导致用户需要手势两次。 2)它在用户加倍第一个手势后中断,所以它真的只有在用户根本不做手势的情况下才有效。 - 每个人都应该看看下面 Andrew Ashbacher 的解决方案。真的很管用。
【解决方案2】:

Scott Jehl 提出了一个出色的解决方案,该解决方案使用加速度计来预测方向变化。此解决方案反应灵敏,不会干扰缩放手势。

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

【讨论】:

  • 不错!看起来是一个优雅的解决方案。
  • 这应该是公认的答案!!!!我希望我先看到这一点,然后再浪费一个小时在上面的解决方案上 :)
  • 经过进一步测试,这是一种不可靠的解决方案:(它是不一致的,在查看代码后我可以看到为什么......并不总是达到定义的运动“阈值”,尤其是如果您在旋转时以一定角度握住 ipad
  • 可能对使用旋转锁定的任何人造成严重后果......他们可能会以一定角度握住手机并失去缩放能力 - 用户不知道为什么
【解决方案3】:

我遇到了同样的问题,设置 maximum-scale=1.0 对我有用。

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

视口代码:

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

【讨论】:

  • 不错的解决方案。通过方向更改将页面保持在恒定的缩放级别(相对于设备的宽度)做得很好。感谢分享!
  • 缺点是禁用用户无法放大您的网站!
  • 我注意到所有这些方法似乎都阻止了基于媒体查询的 CSS 正确注册新的设备宽度(例如:@media all and (max-width: 479px)
  • 杀死用户缩放是一个非常糟糕的主意。请参阅下面的 Andrew Ashbacher 的解决方案
  • 不确定 iPhone,但在 iPad 上这并不能解决问题,它只是阻止用户在浏览器放大方向更改时手动缩小。
【解决方案4】:

如果您在视口中设置了宽度:

<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 方法。

【讨论】:

  • 这是否也会阻止用户手动放大和缩小?
【解决方案5】:

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

【讨论】:

  • 谢谢!是的,我尝试动态更改元标记视口值,但它什么也没做。在我看来,如果您旋转到横向,您希望它正确缩放以保持比例,以便页面适合 Safari 窗口。对我来说这不是默认行为似乎很奇怪!
【解决方案6】:

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

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

【讨论】:

    【解决方案7】:

    我一直在我的项目中使用这个功能。

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

    【讨论】:

      【解决方案8】:

      我找到了一种新的解决方法,它与我所见过的任何其他方法都不同,它禁用原生 iOS 缩放,而是在 JavaScript 中实现缩放功能。

      Sérgio Lopes 提供了有关缩放/方向问题的各种其他解决方案的出色背景:A fix to the famous iOS zoom bug on orientation change to portrait

      <!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>
      

      它可以改进,但根据我的需要,它避免了我见过的所有其他解决方案出现的主要缺点。到目前为止,我只在带有 iOS4 的 iPad 2 上使用移动 Safari 对其进行了测试。

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

      设置 document.body.style 会强制全屏重绘,这样可以避免缩放后重绘严重失败的偶发间歇性问题。

      【讨论】:

        【解决方案9】:

        Elisabeth,您可以通过将“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');
        

        【讨论】:

        • @bridgestew 如果您想动态更改缩放或视口,请使用包含在 uiwebview 中的子视图滚动视图。我在其他线程上添加了一个示例片段:link
        • @Elisabeth 对你有用吗?为我切换横向模式时,它不会重置缩放。
        【解决方案10】:

        找到了一个非常容易实现的修复程序。在表单完成时将焦点设置为字体大小为 50 像素的文本元素。如果文本元素被隐藏,它似乎不起作用,但是通过将元素颜色属性设置为没有不透明度可以轻松地隐藏这个元素。

        【讨论】:

          【解决方案11】:

          这是另一种方法,似乎效果很好。

          1. 设置元标记以将视口限制为 scale=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 秒后,变焦能力恢复。

          【讨论】:

          • 在被问到 5 年后才回答一个问题。不幸的是,这不是 2015 年网络的运作方式。当用户旋转他的设备时,您不会重新加载页面。
          猜你喜欢
          • 2015-11-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多