将视口设置为缩放以适应宽度和高度

我正在一个适合特定宽度和高度的网站(885×610 div,1px边框和3px顶部边距)。 我希望用户不必滚动或缩放以查看整个div; 它应该始终是完全可见的。 由于设备具有各种分辨率和宽高比,所以想到的想法是用JavaScriptdynamic设置“视口”元标记。 这样,div将始终是相同的尺寸,不同的设备将不得不以不同的方式进行放大,以便将整个div放在视口中。 我尝试了我的想法,并得到了一些奇怪的结果。

以下代码适用于首页加载(在Android 4.4.0的Chrome 32.0.1700.99中testing),但在刷新时,缩放级别会发生变化。 另外,如果我注释掉alert ,即使在首页加载时也不起作用。

小提琴

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0"> <script type="text/javascript"> function getViewportWidth() { if (window.innerWidth) { return window.innerWidth; } else if (document.body && document.body.offsetWidth) { return document.body.offsetWidth; } else { return 0; } } function getViewportHeight() { if (window.innerHeight) { return window.innerHeight; } else if (document.body && document.body.offsetHeight) { return document.body.offsetHeight; } else { return 0; } } if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) { var actual_width = getViewportWidth(); var actual_height = getViewportHeight(); var min_width = 887; var min_height = 615; var ratio = Math.min(actual_width / min_width, actual_height / min_height); if (ratio < 1) { document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + ratio + ', maximum-scale=' + ratio + ', minimum-scale=' + ratio + ', user-scalable=yes, width=' + actual_width); } } alert(document.querySelector('meta[name="viewport"]').getAttribute('content')); </script> <title>Test</title> <style> body { margin: 0; } div { margin: 3px auto 0; width: 885px; height: 610px; border: 1px solid #f00; background-color: #fdd; } </style> </head> <body> <div> This div is 885x610 (ratio is in between 4:3 and 16:10) with a 1px border and 3px top margin, making a total of 887x615. </div> </body> </html> 

我可以做些什么来使这个网站适合宽度和高度?

有可能获得一致的行为。 但不幸的是很复杂。 我正在研究检测欺骗代理的脚本,并相应地dynamic地将视口重新缩放到桌面或其他欺骗代理。 我也面临Android / Chrome以及iOS模拟器的缩放问题…

为了解决它,你需要禁用缩放和/或设置视口两次。 在第一遍中,最好像现在一样在<head>内联,您可以设置您的比例并暂时禁用用户缩放,以防止缩放问题,对所有3个比例使用相同的固定值,如:

 document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale='+scale+',maximum-scale='+scale+',initial-scale='+scale); 

然后,要恢复缩放,您需要在DOMContentLoaded重新设置视口,除此之外,您可以设置正常的最小/最大比例值来恢复用户缩放:

 document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale=0,maximum-scale=10'); 

在你的上下文中,因为布局是固定的,比视口大,所以对于DOMContentLoaded来说,可能需要initial-scale='+scale

 document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale=0,maximum-scale=10,initial-scale='+scale); 

这应该让视口在Webkit浏览器中按照您的喜好进行重新缩放,而不会缩小问题。 我只在webkit中说过,因为可悲的是IE和Firefox不支持改变视口,因为视口,尺寸和设备像素比率的浏览器兼容表显示为: http : //www.quirksmode.org/mobile/tableViewport.html

IE有自己的方式来dynamic改变视口,这是IE快照模式实际需要响应的。 http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/

因此,对于IEMobile和IE SnapMode(IE10&11),您需要在<head>dynamic地插入内联<style>

 <script> var s = document.createElement('style'); s.appendChild(document.createTextNode('@-ms-viewport{width:'+width+'px')+';}')); document.head.appendChild(s); </script> 

不幸的是,Firefox既没有:视口被设置为一劳永逸,如上面的兼容性表格所示。 目前,由于缺乏其他方法,使用CSS Transform(如@imcg指出的)是改变Android或Gecko OS上的FireFox Mobile视口的唯一方法。 我刚刚testing过它,它在一个固定大小的devise的情况下工作。 (在“响应式devise上下文”中,可以通过CSS变换重新调整布局,比如手机上的桌面大小,但是Firefox仍然会读取手机大小的MQ,所以在RWD上下文中应该注意一下。 )

虽然,我注意到一些随机的Webkit崩溃CSSTransform在Android上,所以我build议Safari / Chrome / Opera的视口方法更可靠。

此外,为了获得视窗宽度的跨浏览器可靠性,您还必须面对/修复innerWidth浏览器之间的整体不一致性(请注意, documentElement.clientWidth通过innerWidth获得准确的布局像素宽度要可靠得多)而且还必须处理quirksmode.org表中指出的devicePixelRatio差异。

更新:实际上,在为Firefox自己的问题想了一些解决scheme之后,我刚刚find了一种方法来覆盖Firefox Mobile中的视口,使用document.write() ,只应用一次:

 document.write('<meta name="viewport" content="width='+width+'px,initial-scale='+scale+'">'); 

在Webkit和Firefox中都成功testing过,没有缩放问题。 我不能testing窗口手机,所以我不知道itf document.write适用于IEMobile …

如果通过更改视口元标记不能在设备上获得一致的行为,则可以使用CSS3变换进行缩放而不更改尺寸:

 if (ratio < 1) { var box = document.getElementsByTagName('div')[0]; box.style.webkitTransform = 'scale('+ratio+')'; box.style.webkitTransformOrigin = '0 0'; } console.log(box.offsetWidth); // always original width console.log(box.getBoundingClientRect().width); // new width with scaling applied 

注意为了保持简单,我省略了除webkit之外的任何供应商前缀。

要将缩放的div居中,您可以使用translate转换:

 var x = (actual_width - min_width * ratio) / 2; var y = (actual_height - min_height * ratio) / 2; box.style.webkitTransform = 'translateX('+x+'px) translateY('+y+'px) scale('+ratio+')'; box.style.webkitTransformOrigin = '0 0'; 

用这个replace你的视口:

<META NAME="viewport" CONTENT="width=device-width, height=device-height, initial-scale=1, user-scalable=no"/>

这里的用户可扩展= 0应该为你做这项工作。

这将适用于你。如果它仍然不起作用,我们将不得不扩展视口,以取代你的视口,并添加:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, target-densitydpi=medium-dpi, user-scalable=0" />

为你的javascript错误看看这个链接:

使用视口元标记来缩放移动网页内容

我知道这是晚了两年,但我花了很多时间在这个问题上,并希望分享我的解决scheme。 我发现原来的问题是非常有帮助的,所以我觉得发表这个答案是我回馈的方式。 我的解决scheme在iPhone6和7“Galaxy Tab上工作,我不知道它是如何在其他设备上运行的,但是我猜测它应该是主要的行为。

我将视口逻辑分隔成一个外部文件,这样很容易重用。 首先,你将如何使用代码:

 <HTML> <HEAD> <SCRIPT type="text/javascript" src="AutoViewport.js"></SCRIPT> </HEAD> <BODY> <SCRIPT> AutoViewport.setDimensions(yourNeededWidth, yourNeededHeight); </SCRIPT> <!-- The rest of your HTML goes here --> </BODY> </HTML> 

实际上,我填充了我想要的宽度和高度的一点点(15像素左右),以便我的预期显示框架很好。 另外请注意,使用代码中不必在HTML中指定视口标签。 如果不存在的话,我的图书馆会自动创build一个。 这里是AutoViewport.js:

 /** Steven Yang, July 2016 Based on http://stackoverflow.com/questions/21419404/setting-the-viewport-to-scale-to-fit-both-width-and-height , this Javascript code allows you to cause the viewport to auto-adjust based on a desired pixel width and height that must be visible on the screen. This code has been tested on an iPhone6 and a 7" Samsung Galaxy Tab. In my case, I have a game with the exact dimensions of 990 x 660. This script allows me to make the game render within the screen, regardless of whether you are in landscape or portrait mode, and it works even when you hit refresh or rotate your device. Please use this code freely. Credit is appreciated, but not required! */ function AutoViewport() {} AutoViewport.setDimensions = function(requiredWidth, requiredHeight) { /* Conditionally adds a default viewport tag if it does not already exist. */ var insertViewport = function () { // do not create if viewport tag already exists if (document.querySelector('meta[name="viewport"]')) return; var viewPortTag=document.createElement('meta'); viewPortTag.id="viewport"; viewPortTag.name = "viewport"; viewPortTag.content = "width=max-device-width, height=max-device-height,initial-scale=1.0"; document.getElementsByTagName('head')[0].appendChild(viewPortTag); }; var isPortraitOrientation = function() { switch(window.orientation) { case -90: case 90: return false; } return true; }; var getDisplayWidth = function() { if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) { if (isPortraitOrientation()) return screen.width; else return screen.height; } return screen.width; } var getDisplayHeight = function() { if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) { if (isPortraitOrientation()) return screen.height; else return screen.width; } // I subtract 180 here to compensate for the address bar. This is imperfect, but seems to work for my Android tablet using Chrome. return screen.height - 180; } var adjustViewport = function(requiredWidth, requiredHeight) { if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)){ var actual_height = getDisplayHeight(); var actual_width = getDisplayWidth(); var min_width = requiredWidth; var min_height = requiredHeight; var ratio = Math.min(actual_width / min_width, actual_height / min_height); document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + ratio + ', maximum-scale=' + ratio + ', minimum-scale=' + ratio + ', user-scalable=yes, width=' + actual_width); } }; insertViewport(); adjustViewport(requiredWidth, requiredHeight); window.addEventListener('orientationchange', function() { adjustViewport(requiredWidth, requiredHeight); }); }; 

如果您将我的代码与问题中的原始代码进行比较,您会注意到一些差异。 例如,我从不依赖视口的宽度或高度。 相反,我依靠屏幕对象。 这很重要,因为在刷新页面或旋转屏幕时,视口的宽度和高度可能会改变,但screen.width和screen.height不会改变。 接下来的事情你会注意到,我不检查(比例<1)。 当刷新或旋转屏幕,检查造成不一致,所以我删除它。 另外,我还包括一个屏幕旋转处理程序。

最后,我只想对那个创造这个问题的人表示感谢,为我奠定基础,这为我节省了时间!

将div的父元素( htmlbody )的高度和宽度设置为100%,并将边距设置为零。

 html, body { margin: 0; height: 100%; width: 100%; } 

接下来,因为你需要div上的边框,所以当你指定元素的宽度/高度时,需要确保包含边框宽度,在div上使用box-sizing: border-box

因为你想要div上的3px的上边距div的相对位置会导致3px的高度太高。 为了解决这个问题,使用div的绝对定位,并将top,left和bottom设置为0。

 div { position: absolute; top: 3px; left: 0; bottom: 0; box-sizing: border-box; width: 100%; border: 1px solid #f00; background-color: #fdd; } 

这是一个工作的例子 。

更新:我想我误解了这个问题。 以下是根据设备的视口调整“缩放”的示例代码

http://jpanagsagan.com/viewport/index2.html

请注意,我使用jquery添加元标记,因为我有问题使用香草append。

我注意到的一件事是,如果您在HTML中进行硬编码并通过JS进行更改,则文档将不会应用更正(需要validation)。 如果在HTML中没有以前的标签,我可以通过JS来更改它,因此我使用了append。

你可以玩的比例,但在例子中,我用视口的宽度除以div的宽度。

希望这可以帮助。

更新:我想我误解了这个问题。 以下是根据设备的视口调整“缩放”的示例代码

http://jpanagsagan.com/viewport/index2.html

请注意,我使用jquery添加元标记,因为我有问题使用香草append。

我注意到的一件事是,如果您在HTML中进行硬编码并通过JS进行更改,则文档将不会应用更正(需要validation)。 如果在HTML中没有以前的标签,我可以通过JS来更改它,因此我使用了append。

你可以玩的比例,但在例子中,我用视口的宽度除以div的宽度。

希望这可以帮助。

我认为史蒂文应该是被接受的答案。

如果对别人有帮助,我会在Steven的AutoViewport.js中添加以下两件事,以便在用户处于横向视图时将视图居中在视口中:

添加“var viewport_margin = 0;” 作为第一行代码(因为它是自己的行之前“函数AutoViewport(){}”)。

添加“viewport_margin = Math.abs(actual_width-(ratio * requiredWidth))/(actual_width * 2)* 100;” 在读取“document.querySelector('meta [name =”viewport“]”)。setAttribute('content','initial-scale ='+ ratio +',maximum-scale ='+ ratio +', -scale ='+ ratio +',user-scalable = yes,width ='+ actual_width);“

感谢所有发布这个解决scheme的人。

更新:在Android中,我的添加只能使用API​​ 24+。 不知道为什么他们不使用API​​ 19-23。 有任何想法吗?