Android 将视口设置为缩放以适应宽度和高度
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21419404/
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
Setting the Viewport to Scale to Fit Both Width and Height
提问by Nick
I'm working on a website that fits within a specific width and height (an 885x610 div with a 1px border and 3px top margin). I would like the user to never have to scroll or zoom in order to see the entire div; it should always be fully visible. Since devices have a wide variety of resolutions and aspect ratios, the idea that came to mind was to set the "viewport" meta tag dynamically with JavaScript. This way, the div will always be the same dimensions, different devices will have to be zoomed differently in order to fit the entire div in their viewport. I tried out my idea and got some strange results.
我正在开发一个适合特定宽度和高度的网站(885x610 div,边框为 1px,上边距为 3px)。我希望用户永远不必滚动或缩放才能看到整个 div;它应该始终是完全可见的。由于设备具有多种分辨率和纵横比,因此想到的想法是使用 JavaScript 动态设置“视口”元标记。这样,div 将始终具有相同的尺寸,不同的设备必须以不同的方式缩放,以便将整个 div 放入其视口中。我尝试了我的想法并得到了一些奇怪的结果。
The following code works on the first page load (tested in Chrome 32.0.1700.99 on Android 4.4.0), but as I refresh, the zoom level changes around. Also, if I comment out the alert
, it doesn't work even on the first page load.
以下代码适用于第一页加载(在 Android 4.4.0 上的 Chrome 32.0.1700.99 中测试),但当我刷新时,缩放级别会发生变化。另外,如果我注释掉alert
,它甚至在第一页加载时也不起作用。
Fiddle
小提琴
<!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>
What can I do to have this website scale to fit both the width and the height?
我该怎么做才能让这个网站缩放以适应宽度和高度?
回答by hexalys
It's possible to get a consistent behavior. But it's unfortunately very complex. I am working on a script that detects spoofed agents and dynamically rescale the viewport to desktop or other spoofed agents accordingly. I was also facing the zooming issue with Android/Chrome as well as the iOS emulator...
有可能获得一致的行为。但不幸的是,它非常复杂。我正在编写一个脚本,用于检测欺骗代理并相应地将视口动态重新缩放到桌面或其他欺骗代理。我还面临着 Android/Chrome 以及 iOS 模拟器的缩放问题......
To get around it, you need to disable zooming and/or set the viewport twice. On the first pass, preferably inline in the <head>
as you do now, you set your scale and disable user-scaling temporarily to prevent the zoom issue, using the same fixed value for all 3 scales like:
要绕过它,您需要禁用缩放和/或设置视口两次。在第一遍时,最<head>
好像现在一样内联,设置比例并暂时禁用用户缩放以防止缩放问题,对所有 3 个比例使用相同的固定值,例如:
document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale='+scale+',maximum-scale='+scale+',initial-scale='+scale);
Then to restore zooming you set the viewport again on DOMContentLoaded
, with the same scale, except that this time you set normal min/max scale values to restore user-scaling:
然后要恢复缩放,您将视口再次设置为DOMContentLoaded
相同的比例,除了这次您设置正常的最小/最大比例值以恢复用户缩放:
document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale=0,maximum-scale=10');
In your context, because the layout is fixed and larger than the viewport, initial-scale='+scale
is perhaps needed for a more sound alternative for DOMContentLoaded
:
在您的上下文中,由于布局是固定的并且比视口大,initial-scale='+scale
因此可能需要更合理的替代方案DOMContentLoaded
:
document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale=0,maximum-scale=10,initial-scale='+scale);
That should get the viewport to rescale as you would like in Webkit browsers without zooming problems. I say only in webkit because sadly IE and Firefox do not support changing the viewport as per this Browser Compatibility Table for Viewports, Dimensions and Device Pixel Ratios shows: http://www.quirksmode.org/mobile/tableViewport.html
这应该可以让视口像您在 Webkit 浏览器中一样重新缩放而不会出现缩放问题。我只在 webkit 中说,因为遗憾的是 IE 和 Firefox 不支持根据此浏览器兼容性表的视口、尺寸和设备像素比显示更改视口:http: //www.quirksmode.org/mobile/tableViewport.html
IE has its own way to change the viewport dynamically which is actually needed for IE snap modes to be responsive. http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/
IE 有自己的方式来动态更改视口,这实际上是 IE 捕捉模式响应所必需的。 http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/
So for IEMobile and IE SnapMode (IE10&11) you need to dynamically insert an inline <style>
in the <head>
with something like.
因此,对于 IEMobile 和 IE SnapMode (IE10&11),您需要<style>
在<head>
类似的内容中动态插入内联。
<script>
var s = document.createElement('style');
s.appendChild(document.createTextNode('@-ms-viewport{width:'+width+'px')+';}'));
document.head.appendChild(s);
</script>
And unfortunately, Firefox has neither: The viewport is set for once and for all as the above compatibility table shows. At the moment, for lack of other methods, using CSS Transform (as @imcg pointed out) is the only way to alter the viewport in FireFox Mobile on Android or Gecko OS. I have just tested it and it works in the context of a fixed size design. (In "Responsive Design context", the layout can be rescaled larger via CSS Transform, say at desktop size on a phone, but Firefox still read the phone size MQs. So that's something to be mindful off in RWD context. /aside from webkit)
不幸的是,Firefox 两者都没有:视口设置为一劳永逸,如上述兼容性表所示。目前,由于缺乏其他方法,使用 CSS Transform(如@imcg 所指出的)是在 Android 或 Gecko OS 上更改 FireFox Mobile 视口的唯一方法。我刚刚测试了它,它在固定尺寸设计的背景下工作。(在“响应式设计上下文”中,布局可以通过 CSS Transform 重新缩放,比如手机上的桌面大小,但 Firefox 仍然读取手机大小的 MQ。所以在 RWD 上下文中需要注意这一点。/除了 webkit )
Though, I have noticed some random Webkit crashes with CSSTransform on Android so I would recommend the viewport method for Safari/Chrome/Opera as more reliable one.
不过,我注意到一些随机的 Webkit 在 Android 上使用 CSSTransform 崩溃,因此我建议将 Safari/Chrome/Opera 的视口方法作为更可靠的方法。
In addition, in order to get cross-browser reliability for the viewport width, you also have to face/fix the overall inconsistency between browsers for innerWidth (note that documentElement.clientWidth
is much more reliable to get the accurate layout pixel width over innerWidth) and you also have to deal with devicePixelRatio
discrepancies as indicated on the quirksmode.org table.
此外,为了获得视口宽度的跨浏览器可靠性,您还必须面对/修复浏览器之间对于innerWidth的整体不一致(请注意,在innerWidthdocumentElement.clientWidth
上获得准确的布局像素宽度要可靠得多),并且您还必须devicePixelRatio
按照 quirksmode.org 表中的指示处理差异。
Update: Actually after some more thought into a solution for my own problem with Firefox, I just found out a way to override the viewport in Firefox Mobile, using document.write()
, applied just once:
更新:实际上,在对我自己的 Firefox 问题的解决方案进行了更多思考之后,我刚刚找到了一种方法来覆盖 Firefox Mobile 中的视口,使用document.write()
, 仅应用一次:
document.write('<meta name="viewport" content="width='+width+'px,initial-scale='+scale+'">');
Just tested this successfully in both Webkit and Firefox with no zooming issues. I can't test on Window Phones, so I am not sure itf document.write works for IEMobile...
刚刚在 Webkit 和 Firefox 中成功测试,没有缩放问题。我无法在 Window Phones 上进行测试,所以我不确定 document.write 是否适用于 IEMobile...
回答by Steven
I know this is two years late, but I spent a lot of time working on this problem, and would like to share my solution. I found the original question to be very helpful, so I feel that posting this answer is my way of giving back. My solution works on an iPhone6 and a 7" Galaxy Tab. I don't know how it fares on other devices, but I'm guessing it should mostly behave.
我知道这晚了两年,但我花了很多时间解决这个问题,并想分享我的解决方案。我发现最初的问题非常有帮助,所以我觉得发布这个答案是我回馈的方式。我的解决方案适用于 iPhone6 和 7" Galaxy Tab。我不知道它在其他设备上的表现如何,但我猜它应该主要表现。
I separated the viewport logic into an external file so that it is easy to reuse. First, here's how you would use the code:
我将视口逻辑分离到一个外部文件中,以便于重用。首先,您将如何使用代码:
<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>
In actuality, I padded my desired width and height by a slight amount (15 pixels or so) so that my intended display was framed nicely. Also please note from the usage code that you do not have to specify a viewport tag in your HTML. My library will automatically create one for you if one does not already exist. Here is AutoViewport.js:
实际上,我将所需的宽度和高度填充了少量(15 像素左右),以便我想要的显示效果很好。另请注意使用代码中您不必在 HTML 中指定视口标签。如果一个图书馆不存在,我的图书馆会自动为你创建一个。这是 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);
});
};
If you compare my code closely with the original code found in the question, you will notice a few differences. For example, I never rely on the viewport width or height. Instead, I rely on the screen object. This is important because as you refresh your page or rotate your screen, the viewport width and height can change, but screen.width and screen.height never change. The next thing you will notice is that I don't do the check for (ratio<1). When refreshing or rotating the screen, that check was causing inconsistency, so I removed it. Also, I included a handler for screen rotation.
如果您将我的代码与问题中找到的原始代码密切比较,您会注意到一些差异。例如,我从不依赖视口宽度或高度。相反,我依赖于屏幕对象。这很重要,因为当您刷新页面或旋转屏幕时,视口的宽度和高度可能会改变,但 screen.width 和 screen.height 永远不会改变。你会注意到的下一件事是我不检查 (ratio<1)。刷新或旋转屏幕时,该检查导致不一致,因此我将其删除。此外,我还包含了一个用于屏幕旋转的处理程序。
Finally, I'd just like to say thank you to the person who created this question for laying the groundwork, which saved me time!
最后,我要感谢创建这个问题的人打下了基础,这为我节省了时间!
回答by imcg
If you can't get consistent behavior across devices by changing the viewport meta tag, it's possible to zoom without changing the dimensions using CSS3 transforms:
如果您无法通过更改视口元标记在设备之间获得一致的行为,则可以使用 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
Note I've omitted any vendor prefixes other than webkit here in order to keep it simple.
注意,为了简单起见,我在这里省略了除 webkit 之外的任何供应商前缀。
To center the scaled div you could use the translate tranforms:
要将缩放的 div 居中,您可以使用翻译变换:
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';
回答by user3274745
Replace your viewport with this :
用这个替换你的视口:
<META NAME="viewport" CONTENT="width=device-width, height=device-height, initial-scale=1, user-scalable=no"/>
<META NAME="viewport" CONTENT="width=device-width, height=device-height, initial-scale=1, user-scalable=no"/>
The user-scalable=0 here shall do the job for you.
此处的 user-scalable=0 将为您完成这项工作。
This shall work for you.If it still doesn't work for we will have to extend the viewport so replace your viewport and add this:
这对你有用。如果它仍然不起作用,我们将不得不扩展视口,因此替换您的视口并添加以下内容:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, target-densitydpi=medium-dpi, user-scalable=0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, target-densitydpi=medium-dpi, user-scalable=0" />
For your javascript error have a look at this link:
对于您的 javascript 错误,请查看此链接:
回答by Dan1121
UPDATE:I think I misunderstood the question. Here's a sample code that adjusts the "zoom" depending on the device's viewport
更新:我想我误解了这个问题。这是根据设备的视口调整“缩放”的示例代码
http://jpanagsagan.com/viewport/index2.html
http://jpanagsagan.com/viewport/index2.html
Take note that I used jquery to append the meta tag as I am having issue using the vanilla append.
请注意,我使用 jquery 来附加元标记,因为我在使用 vanilla 附加时遇到了问题。
One thing I noticed is that if you hard-coded in the HTML and change it via JS, the document won't apply the correction (needs verification). I was able to change it via JS if there is no previous tag in the HTML, thus I used append.
我注意到的一件事是,如果您在 HTML 中进行硬编码并通过 JS 对其进行更改,则该文档将不会应用更正(需要验证)。如果 HTML 中没有以前的标签,我可以通过 JS 更改它,因此我使用了 append。
You may play around with the ratio, but in the example I used width of the viewport divided by width of the div.
您可以使用比例,但在示例中我使用视口的宽度除以 div 的宽度。
hope this helps.
希望这可以帮助。
回答by Scott Bartell
Set the height and width of the div's parent elements (html
and body
) to 100% and zero out the margin.
将 div 的父元素(html
和body
)的高度和宽度设置为 100% 并将边距归零。
html, body {
margin: 0;
height: 100%;
width: 100%;
}
Next, because you want a border on the div you need to make sure that the border width is included when you specify the width / height of the element, to do this use box-sizing: border-box
on the div.
接下来,因为你想要在 div 上有一个边框,所以你需要确保在指定元素的宽度/高度时包含边框宽度,以便box-sizing: border-box
在 div 上使用。
Because you want a 3px top margin on the div relative positioning of the div will result in a height that is 3px too tall. To fix this use absolute positioning on the div and set top, left, and bottom to 0.
因为你想要 div 上的 3px 上边距 div 的相对定位会导致 3px 的高度太高。要解决此问题,请在 div 上使用绝对定位并将顶部、左侧和底部设置为 0。
div {
position: absolute;
top: 3px;
left: 0;
bottom: 0;
box-sizing: border-box;
width: 100%;
border: 1px solid #f00;
background-color: #fdd;
}
Here's a working example.
这是一个工作示例。
UPDATE:I think I misunderstood the question. Here's a sample code that adjusts the "zoom" depending on the device's viewport
更新:我想我误解了这个问题。这是根据设备的视口调整“缩放”的示例代码
http://jpanagsagan.com/viewport/index2.html
http://jpanagsagan.com/viewport/index2.html
Take note that I used jquery to append the meta tag as I am having issue using the vanilla append.
请注意,我使用 jquery 来附加元标记,因为我在使用 vanilla 附加时遇到了问题。
One thing I noticed is that if you hard-coded in the HTML and change it via JS, the document won't apply the correction (needs verification). I was able to change it via JS if there is no previous tag in the HTML, thus I used append.
我注意到的一件事是,如果您在 HTML 中进行硬编码并通过 JS 对其进行更改,则该文档将不会应用更正(需要验证)。如果 HTML 中没有以前的标签,我可以通过 JS 更改它,因此我使用了 append。
You may play around with the ratio, but in the example I used width of the viewport divided by width of the div.
您可以使用比例,但在示例中我使用视口的宽度除以 div 的宽度。
hope this helps.
希望这可以帮助。
回答by Strongdog
I think Steven's should be the accepted answer.
我认为史蒂文应该是公认的答案。
In case it is helpful someone else, I would add the following 2 things to Steven's AutoViewport.js, in order to center the view within the viewport when the user is in landscape view:
如果对其他人有帮助,我会将以下两件事添加到 Steven 的 AutoViewport.js 中,以便在用户处于横向视图时将视图居中显示在视口中:
Add "var viewport_margin = 0;" as the first line of code (as it's own line before "function AutoViewport() {}".
添加“var viewport_margin = 0;” 作为第一行代码(因为它是“function AutoViewport() {}”之前的自己的一行。
Add "viewport_margin = Math.abs(actual_width-(ratio*requiredWidth))/(actual_width*2)*100;" after the line that reads "document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + ratio + ', maximum-scale=' + ratio + ', minimum-scale=' + ratio + ', user-scalable=yes, width=' + actual_width);"
添加“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 + ', minimum -scale=' + ratio + ', user-scalable=yes, width=' + actual_width);"
Thanks for all those who posted to bring this solution to light.
感谢所有发布此解决方案的人。
UPDATE: In Android, my additions only appear to work with API 24+. Not sure why they aren't working with APIs 19-23. Any ideas?
更新:在 Android 中,我的添加似乎只适用于 API 24+。不知道为什么他们不使用 API 19-23。有任何想法吗?