ChatGPT解决这个技术问题 Extra ChatGPT

如何在 iPhone 上更改方向时重置 Web 应用程序的比例/缩放?

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

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

我想弄清楚这是否是一个错误?或者如果这是可以用 JavaScript 修复的东西?

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

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

一个完美的解决方案:没有 javascript! stackoverflow.com/a/8727440/805787

M
Martijn

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

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

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

然后在加载时使用 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);
    }
}

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

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

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

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旋转时的角度
可能对使用旋转锁定的任何人产生令人讨厌的后果......他们可能会将手机保持在某个角度并失去缩放能力 - 用户不知道为什么
r
rakaloof

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

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

视口代码:

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

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

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

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


这也会阻止用户手动放大和缩小吗?
A
Avi Flax

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


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

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

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


J
James Yang

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

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

r
robocat

我发现了一种新的解决方法,它与我所见过的任何其他方法都不同,方法是禁用本机 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 会强制进行全屏重绘,这样可以避免在缩放后重绘严重失败的偶发间歇性问题。


C
Christophe Roussy

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 对你有用吗?为我切换横向模式时,它不会重置缩放。
D
Dellsmash

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


M
Mark Lamprey

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

设置元标记以将视口限制为 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 , 初始尺度=1');}, 500);再次使用 javascript,在方向更改时,重新加载页面: window.onorientationchange = function(){window.location.reload();};

每次重新定位设备时,页面都会重新加载,最初没有缩放。但 1/2 秒后,变焦能力恢复。


在被问到 5 年后回答一个问题是很重要的。不幸的是,这不是 2015 年网络的工作方式。当用户旋转他的设备时,你不会重新加载页面。