【问题标题】:Mobile viewport height after orientation change方向更改后的移动视口高度
【发布时间】:2022-02-13 13:23:50
【问题描述】:

我正在为orientationchange 事件附加一个监听器:

window.addEventListener('orientationchange', function () {
    console.log(window.innerHeight);
});

我需要在orientationchange 之后获取文档的高度。但是,该事件在轮换完成之前触发。因此,记录的高度反映了实际方向变化之前的状态。

如何注册一个事件,以便在完成方向更改后捕获元素尺寸?

【问题讨论】:

  • 它适用于 iOS 上的 Safari,但不适用于 Android 上的 Chrome。

标签: javascript html mobile viewport orientation-changes


【解决方案1】:

使用调整大小事件

resize 事件将在 orientationchange 之后包含适当的宽度和高度,但您不想监听所有的 resize 事件。因此,我们在方向改变后添加一次性调整大小事件监听器:

Javascript

window.addEventListener('orientationchange', function() {
    // After orientationchange, add a one-time resize event
    var afterOrientationChange = function() {
        // YOUR POST-ORIENTATION CODE HERE
        // Remove the resize event listener after it has executed
        window.removeEventListener('resize', afterOrientationChange);
    };
    window.addEventListener('resize', afterOrientationChange);
});

jQuery:

$(window).on('orientationchange', function() {
    // After orientationchange, add a one-time resize event
    $(window).one('resize', function() {
        // YOUR POST-ORIENTATION CODE HERE
    });
});

不要使用超时

超时不可靠 - 某些设备将无法在您的硬编码超时内捕捉到它们的方向变化;这可能是由于不可预见的原因,或者是因为设备运行缓慢。速度快的设备反而会在代码中产生不必要的延迟。

【讨论】:

  • 这在我的 iPad 3 上不起作用,因为方向更改永远不会触发调整大小事件。
  • @kbriggs 感谢您的反馈。您能否确认是否触发了调整大小事件,或者根本没有在方向更改事件之后触发?这可能对其他人有所帮助。
  • @CB 有时是有时不是,我后来发现。如果我的视口尺寸在旋转后没有改变,则调整大小根本不会触发。这可能取决于元标记视口设置以及应用程序的缩放方式。我现在在方向更改事件期间将我的应用程序缩小到小于视口。当从纵向变为横向时,这允许视口高度缩小,然后 onresize 会触发,我可以在其中放大到全屏。 Android 没有这个问题。
  • 忽略我上面所说的一切。我的应用程序代码位于 iframe 中,因此不应附加到 window.onresize 它应该是 parent.window.onresize。
  • 这是迄今为止最好的答案。
【解决方案2】:

Gajus 和 burtelli 的解决方案功能强大,但开销很高。这是 2017 年相当快的超薄版本,使用 requestAnimationFrame

// Wait until innerheight changes, for max 120 frames
function orientationChanged() {
  const timeout = 120;
  return new window.Promise(function(resolve) {
    const go = (i, height0) => {
      window.innerHeight != height0 || i >= timeout ?
        resolve() :
        window.requestAnimationFrame(() => go(i + 1, height0));
    };
    go(0, window.innerHeight);
  });
}

像这样使用它:

window.addEventListener('orientationchange', function () {
    orientationChanged().then(function() {
      // Profit
    });
});

【讨论】:

  • 绝妙的解决方案!
  • 我在 Safari 上遇到了这个问题。看起来当页面第一次加载时 Safari 并不总是立即请求动画帧。有时事件在轮换后根本不会触发。
  • 当方向改变时检查我的实际宽度时遇到问题,因为宽度没有及时更新。这些 sn-ps 为我做到了。谢谢!
  • 抛出错误orientationChanged is not defined
【解决方案3】:

无法捕获方向更改事件的结束,因为对方向更改的处理因浏览器而异。在检测方向变化结束的最可靠和最快方法之间取得平衡需要比赛间隔和超时。

监听器附加到orientationchange。调用侦听器会启动一个间隔。间隔跟踪window.innerWidthwindow.innerHeight 的状态。 orientationchangeend 事件在 noChangeCountToEnd 后续迭代次数未检测到值突变时或在 noEndTimeout 毫秒后触发,以先发生者为准。

var noChangeCountToEnd = 100,
    noEndTimeout = 1000;

window
    .addEventListener('orientationchange', function () {
        var interval,
            timeout,
            end,
            lastInnerWidth,
            lastInnerHeight,
            noChangeCount;

        end = function () {
            clearInterval(interval);
            clearTimeout(timeout);

            interval = null;
            timeout = null;

            // "orientationchangeend"
        };

        interval = setInterval(function () {
            if (global.innerWidth === lastInnerWidth && global.innerHeight === lastInnerHeight) {
                noChangeCount++;

                if (noChangeCount === noChangeCountToEnd) {
                    // The interval resolved the issue first.

                    end();
                }
            } else {
                lastInnerWidth = global.innerWidth;
                lastInnerHeight = global.innerHeight;
                noChangeCount = 0;
            }
        });
        timeout = setTimeout(function () {
            // The timeout happened first.

            end();
        }, noEndTimeout);
    });

我正在维护一个基于上述逻辑的orientationchangeend 实现。

【讨论】:

    【解决方案4】:

    方向更改需要延迟才能适应新的高度和宽度。这在 80% 的时间里都有效。

    window.setTimeout(function() {
        //insert logic with height or width calulations here.
    }, 200);
    

    【讨论】:

    • 延迟是丑陋的,但有效.. 如果延迟足够。有没有更好的解决方案?
    • 请注意,200 毫秒远远不够(iOS、Safari)。实际时间在 600-1000 毫秒之间。导致这种变化的原因对我来说仍然未知。请参阅我的答案,它依赖于 setIntervalsetTimeout 的组合,并解释了一种确定方向变化真正结束的技术。
    • 使用相同的逻辑:p
    • 一开始我也用这个方法,最后老板说200ms太慢了
    • 这是一个非常糟糕的黑客解决方案。如果互联网连接速度不同的人使用您的网站,这将不可避免地产生问题
    【解决方案5】:

    我使用了一段时间由 Gajus Kuizunas 提出的解决方法,虽然有点慢,但它是可靠的。谢谢,无论如何,它完成了工作!

    如果您使用的是 Cordova 或 Phonegap,我找到了一个更快的解决方案 - 以防其他人将来遇到这个问题。此插件会立即返回正确的宽度/高度值:https://github.com/pbakondy/cordova-plugin-screensize

    返回的高度和宽度反映了实际分辨率,因此您可能必须使用 window.devicePixelRatio 来获取视口像素。标题栏(电池、时间等)也包含在返回的高度中。我最初使用了这个回调函数(onDeviceReady)

    var successCallback = function(result){
        var ratio = window.devicePixelRatio;            
        settings.titleBar = result.height/ratio-window.innerHeight; 
        console.log("NEW TITLE BAR HEIGHT: " + settings.titleBar);
    };
    

    然后您可以在方向更改事件处理程序中使用:

    height = result.height/ratio - settings.titleBar;
    

    立即获得innerHeight。希望这对某人有帮助!

    【讨论】:

      【解决方案6】:

      我结合上述几个解决方案解决了这个问题。调整大小会触发 4-5 次。使用 .one,它启动得太早了。在 Christopher Bull 的解决方案中添加一个短暂的暂停就可以解决问题。

      $(window).on('orientationchange', function () {
        $(window).one('resize', function () {
          setTimeout(reference_to_function, 100);
        });
      });
      

      【讨论】:

        【解决方案7】:

        我也遇到了同样的问题。所以我最终使用了:

        window.onresize = function(){ getHeight(); }
        

        【讨论】:

        • 你的函数 getHeight() 做了什么?
        • windows.resize 的问题是,iOS 设备在滚动时会触发,因此它对于检测屏幕大小是无用的。
        • getHeight 函数是做什么的
        • Christopher Bull's answer。仅在方向更改后检查一次窗口调整大小,这样当用户滚动时它不会再次触发,并且当浏览器工具栏消失时视口会发生变化。
        【解决方案8】:

        这是一个实验性功能,在屏幕方向改变后给出正确的innerHeight和innerWidth。

        window.screen.orientation.addEventListener('change', function(){
            console.log(window.innerHeight)
        })
        

        我认为听窗口调整大小是实现屏幕方向更改逻辑的最安全方法。

        【讨论】:

        • 在哪些操作系统上实验哪些浏览器??
        • nm 我在 mdn 上找到了支持表。太糟糕了,现在还没有在任何地方实施
        【解决方案9】:

        需要注意的是orientationchange不会得到改变后的高度,而是之前的。使用调整大小来完成此操作。

        $(window).bind('orientationchange', function (e) {
            var windowHeight = $(window).innerHeight();
            console.log('Before Orientation: Height = ' + windowHeight);
        
            $(window).resize(function () {
                windowHeight = $(window).innerHeight();
                console.log('After Orientation: Height = ' + windowHeight);
            });
        });
        

        【讨论】:

          【解决方案10】:

          对于在orientationchange 之后只想要窗口对象的innerHeight 的人,有一个简单的解决方案。使用window.innerWidthorientationchange事件期间的innerWidthinnerHeight完成后的orientationchange

          window.addEventListener('orientationchange', function () {
              var innerHeightAfterEvent = window.innerWidth;
              console.log(innerHeightAfterEvent);
              var innerWidthAfterEvent = window.innerHeight;
              console.log(innerWidthAfterEvent);
              console.log(screen.orientation.angle);
          });
          

          我不确定 180 度的变化是否以两个 90 度的步长完成。无法在开发者工具的帮助下在浏览器中模拟。但是,如果您想防止完整的 180、270 或 360 旋转可能性,则应该可以使用screen.orientation.angle 来计算角度并使用正确的高度和宽度。事件中的screen.orientation.angleorientationchange事件的目标角度。

          【讨论】:

          • 它并不总是那么简单...例如,在 ios 的 chrome 上,横向模式下不存在浏览器栏之一,因此横向模式的高度大于宽度纵向模式。
          猜你喜欢
          • 2012-09-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多