简体   繁体   English

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

[英]Setting the Viewport to Scale to Fit Both Width and Height

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). 我正在开发一个适合特定宽度和高度的网站(885x610 div,1px边框和3px上边距)。 I would like the user to never have to scroll or zoom in order to see the entire div; 我希望用户永远不必滚动或缩放以查看整个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. 由于设备具有多种分辨率和宽高比,因此我想到的想法是使用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. 这样,div将始终具有相同的尺寸,不同的设备必须以不同的方式进行缩放,以便在视口中适合整个div。 I tried out my idea and got some strange results. 我尝试了我的想法并得到了一些奇怪的结果。

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. 以下代码适用于第一页加载(在Android 4.4.0上的Chrome 32.0.1700.99中测试),但在刷新时,缩放级别会发生变化。 Also, if I comment out the alert , it doesn't work even on the first page load. 此外,如果我注释掉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? 我该怎么做才能让这个网站扩展到适合宽度和高度?

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 : 在您的上下文中,因为布局是固定的并且大于视口,所以对于DOMContentLoaded的更合理的替代方案,可能需要initial-scale='+scale

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. 这应该让视口在Webkit浏览器中重新调整,而不会出现缩放问题。 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中,因为遗憾的是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. IE有自己的方式来动态更改视口,实际上IE快照模式需要响应。 http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ 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),您需要在<head>动态插入内联<style>

<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. 不幸的是,Firefox既没有:视口是一次性设置的,如上面的兼容性表所示。 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. 目前,由于缺乏其他方法,使用CSS Transform(如@imcg所指出的)是在Android或Gecko OS上改变FireFox Mobile视口的唯一方法。 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) (在“响应式设计上下文”中,布局可以通过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的浏览器之间的整体不一致性(请注意, documentElement.clientWidth在内部宽度上获得准确的布局像素宽度更加可靠)您还必须处理quirksmode.org表中指示的devicePixelRatio差异。

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. 刚刚在Webkit和Firefox中成功测试了这一点,没有缩放问题。 I can't test on Window Phones, so I am not sure itf document.write works for IEMobile... 我不能在Window Phones上测试,所以我不确定itf document.write适用于IEMobile ......

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. 实际上,我将所需的宽度和高度填充了一小部分(大约15个像素),这样我的预期显示效果很好。 Also please note from the usage code that you do not have to specify a viewport tag in your HTML. 另请注意您在HTML中不必指定视口标记的使用代码。 My library will automatically create one for you if one does not already exist. 如果还没有,我的库会自动为您创建一个。 Here is AutoViewport.js: 这是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. 这很重要,因为当您刷新页面或旋转屏幕时,视口宽度和高度可能会发生变化,但screen.width和screen.height永远不会改变。 The next thing you will notice is that I don't do the check for (ratio<1). 接下来你要注意的是我不做检查(比率<1)。 When refreshing or rotating the screen, that check was causing inconsistency, so I removed it. 刷新或旋转屏幕时,该检查导致不一致,因此我将其删除。 Also, I included a handler for screen rotation. 此外,我还包括一个屏幕旋转处理程序。

Finally, I'd just like to say thank you to the person who created this question for laying the groundwork, which saved me time! 最后,我只想感谢创建这个问题的人为奠定基础,这节省了我的时间!

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居中,您可以使用translate tranforms:

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 your viewport with this : 用以下内容替换您的视口:

<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" />

For your javascript error have a look at this link: 对于您的javascript错误,请查看此链接:

scale fit mobile web content using viewport meta tag 使用视口元标记缩放适合的移动Web内容

Set the height and width of the div's parent elements ( html and body ) to 100% and zero out the margin. 将div的父元素( htmlbody )的高度和宽度设置为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上使用边框,所以在指定元素的宽度/高度时需要确保包含边框宽度,为此,请使用div上的box-sizing: border-box

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. 因为你想要div的3px上边距div的相对位置将导致高度为3px太高。 To fix this use absolute positioning on the div and set top, left, and bottom to 0. 要解决此问题,请使用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;
}

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 append时遇到问题。

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). 我注意到的一件事是,如果您在HTML中进行硬编码并通过JS进行更改,则文档将不会应用更正(需要验证)。 I was able to change it via JS if there is no previous tag in the HTML, thus I used append. 如果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. 希望这可以帮助。

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 append时遇到问题。

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). 我注意到的一件事是,如果您在HTML中进行硬编码并通过JS进行更改,则文档将不会应用更正(需要验证)。 I was able to change it via JS if there is no previous tag in the HTML, thus I used append. 如果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. 希望这可以帮助。

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;" 添加“var viewport_margin = 0;” as the first line of code (as it's own line before "function AutoViewport() {}". 作为第一行代码(因为它是“函数AutoViewport(){}之前的自己的行”。

Add "viewport_margin = Math.abs(actual_width-(ratio*requiredWidth))/(actual_width*2)*100;" 添加“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);" 在读取“document.querySelector('meta [name =”viewport“]')的行之后.setAttribute('content','initial-scale ='+ ratio +',maximum-scale ='+ ratio +',最小值-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+. 更新:在Android中,我的添加内容似乎只适用于API 24+。 Not sure why they aren't working with APIs 19-23. 不确定他们为什么不使用API​​ 19-23。 Any ideas? 有任何想法吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM