您的位置:永利集团登录网址 > web资讯 > 移动端页面适配永利游戏平台———多方案解析

移动端页面适配永利游戏平台———多方案解析

2019-10-04 15:21

移动端自适应方案

2015/09/14 · JavaScript, 基础技术 · 移动端, 自适应

原文出处: 大搜车前端团队博客   

前方依旧高能 ^_^ , 本文主要解决以下问题:

  • 真的需要动态生成viewport吗?
  • 如何自适应?

然后给出主观的最佳实践。

  • 最帅的flex

赶时间戳这里传送门

比较无聊干燥的文章,看前请喝水。

研究样本

  1. 手淘 ml.js
  2. 天猫首页
  3. 手机携程

一个月前去了css开发者大会,听到了手淘的自适应方案,想起之前一直就想了解ml.js到底干了什么事。回来仔细研究了一下,抱着好奇心一并看了同样类型的网站的方案,深入学习一下。

研究结论

  1. 手淘

    • 获取手机dpr(window.devicePixelRatio),动态生成viewport。
    • 换取手机宽度,分成10份,每一份的宽度即是rem的尺寸。
    • 根据设计稿尺寸(px)通过计算,转换成rem去布局。

    ps:海外淘宝并没有这样做,而是scale1.0并且图片大概都是2倍图。

  2. 天猫

    • 采用scale=1.0 写死viewport。
    • flex布局,笃定认为布局尺寸是375 (iPhone6)
    • rem 确定非flex的元素
  3. 手机携程
    • 采用scale=1.0 写死viewport
    • px + 百分比布局

实现之前

提及实现之前,先简单过一些概念。

完美视口

完美视口的概念已经街知巷闻了,如果不知道可以先戳这里。

在这几篇文章里,还会学会设备像素,css像素等概念,大神讲的很透彻,这里就不献丑了。

ppk 谈 viewport其1 ppk 谈 viewport其2 ppk 谈 viewport其3

这里给出完美视口

XHTML

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

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

在移动端,低端无定制的需求,都可以用这个完美视口完成。然而看到这篇文章的你,显然完美视口还不能满足。

dpr

dpr是devicePixelRatio的简写,也就是屏幕分辩比

历史原因,由于苹果retina的产生,使得清晰度提升,主要是因为`设备像素`提升了一倍,因此可以用更多像素去绘画更清晰的图像。#我乱说的#

1
历史原因,由于苹果retina的产生,使得清晰度提升,主要是因为`设备像素`提升了一倍,因此可以用更多像素去绘画更清晰的图像。#我乱说的#

坊间对于dpr更通俗的说法叫

  • 一倍屏
  • 两倍屏
  • 三倍屏

scale

scale是屏幕拉伸比。也就是视口上的initial-scale , maximum-sacle 等属性。

scale 和 dpr的关系是倒数。

1
scale 和 dpr的关系是倒数。

直观感受

这是我对dpr的直观感受永利游戏平台 1

同样去展示 1 x 1 像素的点,虽然在屏幕上看到的大小是一样,但背后表现它的像素数量是不同。

这也意味着,在一样大小的面积内,更多物理像素的屏幕上展现色彩的能力越强。

但这不是我要关注的点,我们关注的是。

1. 是否需要根据倍屏去切换scale达到伸缩的目的

2. 切换scale的成本和回报

下面根据几个实验来回答这两个问题。

自适应问题

实验1 - 传说中的1px

大多数给出要动态切换scale的理由有以下两个。

  1. 1px并不是 [ 真实的1px ] , 2. 为了充分利用屏幕的分辨率,使用符合屏幕的图片。
1
2
3
1. 1px并不是 [ 真实的1px ] ,
 
2. 为了充分利用屏幕的分辨率,使用符合屏幕的图片。

在移动互联网快速发展的今天,手机的种类和尺寸越来越多,作为前端的小伙伴们可能会越来越头疼,但又不得不去适配一款又一款的新机型。对于移动端适配,不同的公司、不同的团队有不同的解决方案。我在项目中也用了一部分解决方案,也看到了一些解决方案,对比下,总结一些自己的理解,希望对各位有帮助,找到最适合你们项目的适配方案。

此篇总结是在学习了 viewport 基础知识,再参考了淘宝的 lib.flexible 可伸缩布局这个库,自己推演了 lib.flexible 是怎么作出这个解决方案的。

真实的1px

这一条和设计稿密切想关,要讨论它不能抛开设计稿不谈。

这里先补一下切图课,如果自己要做1x , 2x, 3x 的设计稿。如何去实现?

尺寸!!!

大多数情况下,设计师产出各种尺寸的稿子(事实上一般只是2倍稿子),都是先画出大尺寸的稿子,再去缩小尺寸,最后导出。 这样会带来问题:

如果设计师在2倍稿子里画了一条1px的线,这时候假如我们要在scale=1.0里呈现的话,就会变成0.5px,如下图。

永利游戏平台 2

而很大一部分手机是无法画出0.5px的,因此这里一般有一个hack

CSS

transform:scaleX(0.5)或transform:scaleY(0.5)

1
transform:scaleX(0.5)或transform:scaleY(0.5)

但是有人提出了, 既然可以改变viewport的scale达到合理利用不同倍屏的优势,为什么不这么写呢。

XHTML

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

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

等等,为了设计稿的尺寸我们如此大费周章?

事实上,即使2x设计稿避免了1px。3x设计稿也可能出现2px。

而且这里如果写死scale可能造成部分地方和稿子出入较大,无法还原设计稿,界面的显示会打折扣。

解决这个问题的关键在于:交流

  • 如果你的设计师是个要求严格,而且产品界面把控非常严格的话,应该动态去实现viewport或使用scale的hack去改变。
  • 如果部分区域实在没有必要[ 过度优化 ], scale=1.0 实在是非常低成本还原的方案,未尝不可。
下面是一些基础概念的讲解,帮助理解各种适配方案实现。

上一篇我们说过,对于 device-width 相同但是分辨率不同的手机我们可以通过设置 meta viewport 把移动页面的宽度归一到一个统一的宽度(这样一套布局就可以适用不同分辨率的手机)。但是现在不但 iphone 阵营自己出了好几个 device-width (320px,375px,414px),android 阵营更时百花齐放。那么我们对于不同的宽度的页面我们希望如果能用一套 css 搞定。最容易想到的就是使用百分比来设置尺寸。但是 css 百分比是根据父元素的尺寸来计算,而不是根元素譬如 viewport,这样对嵌套过深的元素计算尺寸非常不友好。同理使用 em 单位也会产生同样的问题。幸好 css3 出了一个新单位 rem,我简单的介绍下 rem 的规则:根据根元素(html)的字体大小来计算当前尺寸。譬如说 html 这个元素的 font-size 设了 10px,那么当前页面 1rem 就是 10px,2rem 就是 20px,如果 html 元素的 font-size 设置了 75px,则当前页面 1rem = 75px,2rem = 150px。

对应倍图

对于这一点,争议较多,因为如果要做到对应倍图的话,意味着图片都需要做三份。成本太高了。

这里通常有两种做法

  1. 图片服务

    例如在100×100的图片容器中。

1倍图 http:// img.xxx.com/abc.jpg_100x100 2倍图 http://
img.xxx.com/abc.jpg_200x200 3倍图 http://
img.xxx.com/abc.jpg_300x300

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f19d520d5d723297616-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d5d723297616-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d5d723297616-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d5d723297616-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d5d723297616-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d5d723297616-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d5d723297616-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d5d723297616-8">
8
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f19d520d5d723297616-1" class="crayon-line">
1倍图
</div>
<div id="crayon-5b8f19d520d5d723297616-2" class="crayon-line crayon-striped-line">
 http:// img.xxx.com/abc.jpg_100x100
</div>
<div id="crayon-5b8f19d520d5d723297616-3" class="crayon-line">
 
</div>
<div id="crayon-5b8f19d520d5d723297616-4" class="crayon-line crayon-striped-line">
 2倍图
</div>
<div id="crayon-5b8f19d520d5d723297616-5" class="crayon-line">
 http:// img.xxx.com/abc.jpg_200x200
</div>
<div id="crayon-5b8f19d520d5d723297616-6" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d5d723297616-7" class="crayon-line">
 3倍图
</div>
<div id="crayon-5b8f19d520d5d723297616-8" class="crayon-line crayon-striped-line">
 http:// img.xxx.com/abc.jpg_300x300
</div>
</div></td>
</tr>
</tbody>
</table>
  1. 定死尺寸

    放弃1屏手机,全部启用2倍图,由于流量会消耗比较大(低端机),因此滚动加载等优化手段就会显得比较重要了。

实验1 – scale对倍图重要吗

这里看一下不同scale下图片的差异。

  • 测试样本:160×160凯尔特人队标logo(一不小心暴露了绿色的血液)
  • 测试容器:160×160 img标签
  • 测试环境: intial-scale分别为1.0 / 0.5 / 0.3333
  • 图片尺寸: 1x(160×160) 2x(320×320) 3x(480×480)

永利游戏平台 3

测试结论:不同scale下使用不同图片差异非常大。

但是这里需要验证,是否不同scale同一图片差异起到绝对作用。

永利游戏平台 4

  • 肉眼观看基本无区别,除了用取色器去获取,会发现有色差和部分像素被分割(下面会说到),之外,用不同scale显示同一图片基本没有什么区别。

实验2 – DownSampling

由于上一个实验最后的图片,使用同一scale下,不同倍数的图片,存在色差,这里验证一下。

  • 测试方案

    测试图片:

 永利游戏平台 5

图片尺寸: 400×300 , 300×225 , 200×150 , 100×75

测试环境: scale = 1.0

测试容器: 100×75的 img元素

由于之前知道了DownSampling概念的存在,这里只是好奇心驱动试验一下。(对自适应其实没有卵用)

DownSampling是说大图放入比图片尺寸小的容器中的时候,出现像素分割成就近色的情况。

测试结果:

永利游戏平台 6

注:6plus貌似和其他机型不同。

触发情况: 不同颜色像素接触的地方,会出现DownSampling。

永利游戏平台 7

rem

对于rem要说的不多,看这张图。对于用到px的元素,使用rem统一去管理是很灵活的!

永利游戏平台 8

字体

无论是采用动态生成viewport或者写死scale,字体都需要适配大屏。之前提出的rem方案被证实在不同手机上显示不一致,这里还是回归成了px。

px最好用双数

两种方案(这里不考虑媒体查询,因为Android碎..,嗯,不说了…)

  1. JS动态计算(常见做法)
根据不同屏幕宽度计算不同字号大小。 1.
定基准值,设计稿是750宽度(2倍屏),字体的大小是24px. 2.
计算指定宽度的字体大小。 var fontSize = width / 750 * 24 ;

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f19d520d62124238623-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d62124238623-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d62124238623-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d62124238623-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d62124238623-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d62124238623-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d62124238623-7">
7
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f19d520d62124238623-1" class="crayon-line">
根据不同屏幕宽度计算不同字号大小。
</div>
<div id="crayon-5b8f19d520d62124238623-2" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d62124238623-3" class="crayon-line">
1. 定基准值,设计稿是750宽度(2倍屏),字体的大小是24px.
</div>
<div id="crayon-5b8f19d520d62124238623-4" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d62124238623-5" class="crayon-line">
2. 计算指定宽度的字体大小。
</div>
<div id="crayon-5b8f19d520d62124238623-6" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d62124238623-7" class="crayon-line">
var fontSize = width / 750 * 24 ;
</div>
</div></td>
</tr>
</tbody>
</table>
  1. 根据dpr设定 (比较好的做法)

    ps : 一般时初始化时设置为根元素html的attribute,

JavaScript

window.document.documentElement.setAttribute('dpr',window.devicePixelRatio)

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f19d520d65248160001-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d65248160001-2">
2
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f19d520d65248160001-1" class="crayon-line">
   window.document.documentElement.setAttribute('dpr',window.devicePixelRatio)
</div>
<div id="crayon-5b8f19d520d65248160001-2" class="crayon-line crayon-striped-line">
 
</div>
</div></td>
</tr>
</tbody>
</table>

然后css这样写



CSS

[dpr=1] { font-size=16px; } [dpr=2] { font-size=32px; }

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f19d520d69092077898-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d69092077898-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d69092077898-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d69092077898-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d69092077898-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f19d520d69092077898-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f19d520d69092077898-7">
7
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f19d520d69092077898-1" class="crayon-line">
[dpr=1] {
</div>
<div id="crayon-5b8f19d520d69092077898-2" class="crayon-line crayon-striped-line">
       font-size=16px; 
</div>
<div id="crayon-5b8f19d520d69092077898-3" class="crayon-line">
}
</div>
<div id="crayon-5b8f19d520d69092077898-4" class="crayon-line crayon-striped-line">
 
</div>
<div id="crayon-5b8f19d520d69092077898-5" class="crayon-line">
[dpr=2] {
</div>
<div id="crayon-5b8f19d520d69092077898-6" class="crayon-line crayon-striped-line">
       font-size=32px; 
</div>
<div id="crayon-5b8f19d520d69092077898-7" class="crayon-line">
}
</div>
</div></td>
</tr>
</tbody>
</table>

布局

权衡之下,我觉得flex真的灵活方便太多,因此这里给出一个布局demo。大致如下图。(画的比较粗糙..)

(上稿下还原)

永利游戏平台 9永利游戏平台 10

基本涵盖:

  • 固定头部
  • 固定底部
  • 多列自适应
  • 高度自定义
  • 内容滚动

为什么flex能够做到百分比做不到的自适应。

比如我们也去学天猫,笃定认为宽度就是375(iPhone6尺寸),那么两个元素flex分别为200和175。

无需计算百分比,在不同的界面上就会自动计算,而且以该浏览器可以识别的最小单位实现,比自己计算的百分比要精准。

永利游戏平台 11

demo传送门

结论

  1. 写死initial-scale=1.0 对于实现1px问题, 问题比较大。与设计师沟通协商才是最好的解决问题的方法。
  2. 写死initial-scale=1.0 对于不同图片的显示, 采用不同倍图的话,会有一定压缩,但在可接受范围内。(当然,动态生成scale能够完美呈现…)
  3. 布局

    如果采用动态生成viewport方案,就用到rem来还原设计稿(还有rem-px的计算)。成本在效率上。

    如果采用写死initial-scale=1.0方案,就用flex布局,主要成本在flex兼容性上,但是实现非常灵活简单。

后记

viewport的scale的重要性远比我想象的要低很多,我原本以为这就是自适应。

但是后来发现,其实自适应还是回到了远古时代的百分比%,只是现在有更聪明更灵活的方式flex,未来应该有两个方向去自适应。

  • 一个是拥抱vw,vh。(手淘的ml.js十等分宽度,1rem=10vw
  • 一个是更好的使用flex

现在使用后者已经有很多的库可以解决兼容性了,如参考资源最后的一个flex库。

调研的网站并不多,但是百分比仍然是很多人的首选。

参考资源

手淘ml库

手机淘宝

天猫首页

移动端高清、多平适配方案

rem对webapp带来的影响

flex方案 适配到IE10+

 

 

2 赞 10 收藏 评论

永利游戏平台 12

像素:

如果我们把页面宽度分成 100 份,把 html 的 font-size 设置成 viewport-width/100(px),则当前页面 1rem 就等于 1% 页面宽度,这样使用 rem 作为单位开发就相当于用百分比单位来设置尺寸了。如果愿意可以把所有的尺寸都转成百分比布局,那么所有不同宽度的页面都可以用一套 css 搞定。这个方案是可以实现的,只要把 html 的 font-size 设置成 document.documentElement.clientWidth/100(px)。

1、物理像素(设备像素)

屏幕的物理像素,又被称为设备像素,他是显示设备中一个最微小的物理部件。任何设备屏幕的物理像素出厂时就确定了,且固定不变的。

那是不是简单的写下如下的代码就搞定了多屏适配呢?

2、设备独立像素

设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素。

<html style="font-size:(document.documentElement.clientWidth/100)px">
  <head>
    <meta name="viewport" content="width=device-width">
    <style>
      div {
        width: 50rem;
      }
    </style>
  <head>
</html>
3、设备像素比

设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系

设备像素比 = 物理像素 / 设备独立像素
以iphone6为例:
iphone6的设备宽和高为375pt * 667pt,可以理解为设备的独立像素,而其设备像素比为2.固有设备像素为750pt * 1334pt

通过:window.devicePixelRatio获得。

设备像素比是区别是否是高清屏的标准,dpr大于1时就为高清屏,一般情况下dpr为整数,但是android有些奇葩机型不为整数。

这样的方案在高分辨率的手机上会有一系列的问题:

4、css像素

在CSS、JS中使用的一个长度单位。单位px

注:在pc端1物理像素等于1px,但是移动端1物理像素不一定等于1px,1物理像素与px的关系与以下因素有关。(有些视口概念,可以把下面视口看完了再来看)

1、屏幕布局视口大小(下面会讲到)
2、屏幕的分辨率(物理像素)

对于一块屏幕,其物理像素是确定的。视觉视口尺寸是继承的布局视口的,而视觉视口里宽度即是css的px数。故在一块屏上物理像素与px的关系就是物理像素与布局视口的px数的关系。

比如iphone6,期物理像素为750,如果没有设置布局视口时,viewport为980px
此时:1物理像素长度等于980/750px = 1.3067px的长度
由于像素都是点阵的,故1物理像素相当于1.3067px * 1.3067px方格。
当在meta中设置了如下配置时
<meta name="viewport" content="width=device-width">
相当于把布局视口设置为设备的宽度(即上面讲到的设备独立像素), 对于iphone6就是375px。
此时1物理像素长度等于375/750px = 0.5px的长度,故1物理像素相当于0.5px * 0.5px的方格。
  1. 在大屏幕高分辨率的手机上,以 320px 为页面宽度布局,元素过大,而且对于设计不友好(空间太小)。
  2. 会出现 1px 占2个或者多个物理像素的情况,无法做到对设计稿高度还原。
  3. 譬如在 640x960 分辨率的手机上如果用 320px 布局,页面上有《img src="xxx.png" style="width:25px;height:25px"/》,如果切图使用 25x25 的图片,会产生模糊的情况,因为其实高分辨率手机是把图片放大了,原因大家网上搜下,这不是这篇主要总结的问题。

视口:

针对第一个问题高分辨率手机我们可以设置 initial-scale 来把初始化页面缩小。打个比方 640x960 分辨率的手机设置 <meta name="viewport" content="width=device-width, initial-scale=0.5">,那么页面初始化宽度就是 750px。

1、布局视口:

在html中一般在meta中的name为viewport字段就是控制的布局视口。布局视口一般都是浏览器厂商给的一个值。在手机互联网没有普及前,网络上绝大部分页面都是为电脑端浏览而做的,根本没有做移动端的适配。随着移动端的发展,在手机上看电脑端的页面已成为非常普及现象。而电脑端页面宽度较大,移动端宽度有限,要想看到整个网页,会有很长的滚动条,看起来非常麻烦。于是浏览器厂商为了让用户在小屏幕下网页也能够显示地很好,所以把布局视口设置的很大,一般在768px ~ 1024px 之间,最常用的宽度就是 980。这样用户就能看到绝大部分内容,并根据具体内容选择缩放。

故布局视口是看不见的,浏览器厂商设置的一个固定值,如980px,并将980px的内容缩放到手机屏内。

布局视口可以通过:

document.documentElement.clientWidth(clientHeight) // 布局视口的尺寸。

针对第二个问题也可以通过把页面宽度设置成同手机分辨率宽度一样,来做到 css 像素和物理像素 1:1 来真实还原设计稿。问题是我怎么知道手机分辨率是多少,且来看 window.devicePixelRatio: 他是密度无关像素(dip)和物理像素比(我们俗称的 dpr)。举个例子如果 device-width 是 320,window.devicePixelRatio = 2 说明手机分辨率是 640xYYY,window.devicePixelRatio = 3 说明手机分辨率是 960xYYY。那么我们根据自己的项目需求针对不同的分辨率的手机对上面的方案可以做一个改进,这次我们要动态生成 meta(下面是伪代码,只是为了说明):

2、视觉视口:

浏览器可视区域的大小,即用户看到的网页的区域。(其宽度继承的布局视口宽度)

window.innerWidth(innerHeight)  // 视觉视口尺寸
<html style="font-size:(document.documentElement.clientWidth/100)px">
  <head>
    <script>
      var deviceWidth = document.documentElement.clientWidth;
      var dpr = window.devicePixelRatio;
      var scale = 1 / dpr; // 如果我们做到 dip 和物理像素 1:1 
      var metaEl = document.createElement('meta');
      metaEl.setAttribute('name', 'viewport');
      metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale );
      document.firstElementChild.appendChild(metaEl);
    </script>
    <style>
      div {
        width: 50rem;
      }
    </style>
  <head>
</html>
3、理想视口:

布局视口虽然解决了移动端查看pc端网页的问题,但是完全忽略了手机本身的尺寸。所以苹果引入了理想视口,它对设备来说是最理想的布局视口,用户不需要对页面进行缩放就能完美的显示整个页面。最简单的做法就是使布局视口宽度改成屏幕的宽度。

可以通过window.screen.width获取。

<meta name="viewport" content="width=device-width">

移动端到底怎么适配不同的屏幕呢?最简单的方法是设置如下视口:

<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

当使用以上方案定义布局视口时,即布局视口等于理想视口(屏幕宽度),屏幕没有滚动条,不存在高清屏下,字体较小的问题。但是在不同屏幕上,其视觉宽度是不同的,不能简单的将所有的尺寸都设置为px,可能会出现滚动条。小尺寸的可以用px,大尺寸的只能用百分比和弹性布局。

当然这个 scale 怎么设置可以根据项目具体调整。

viewport缩放

对于上面的设置,再不同的屏幕上,css像素对应的物理像素具数是不一致的。

在普通屏幕下,dpr=1时,

1个css像素长度对应1个物理像素长度,1个css像素对应1个物理像素。

而在Retina屏幕下,如果dpr=2,

1个css像素长度对应2个物理像素长度,1css像素对应4个物理像素。

此时如果css中写

border: 1px solid red; // 此时1px 对应的宽度是2物理像素的宽度。

而一般现在移动端设计稿都是基于iphone设计的,稿子一般为750px或640px,这正好是iphone6和iphone5的物理像素。在设计稿中,一般有些边框效果,这时边框的线宽为1px,对应的就是1物理像素。而对于iphone5和iphone6,当width=device-width时,css的1px显示出来的是2个物理像素,所以看起来线就比较粗。怎么解决呢?1px边框效果其实有很多hack方法,其中一种就是通过缩放viewport。

initial-scale是将布局视口进行缩放,initial-scale是相对于理想视口的,即initial-scale=1与width=device-width是一样的效果。initial-scale=0.5等效于width= 2倍的device-width,所以设置initial-scale和width都可以改变布局视口的大小。

<meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">  

对于iphone6当添加如上设置后,initial-scale=0.5,即将页面缩小2倍后等于屏幕宽度。

布局视口width:
width / 2 = 375px; width = 750px;

所以此时布局视口为750px,此时1px等于1物理像素。

针对第三个问题我们的解决方案是不同分辨率,加载不同的图片。还是那上面第三点问题中的例子,如果 dpr = 2,那么我就提供一个 50x50 的图片放在你 25x25 的 img 元素里面,这样就能解决图片模糊的问题。但是如果每个图片都需要判断 dpr 动态设置 img 的 src,那么写起来是很麻烦,是否能有方案统一处理?有!把 img 全部转换成 background-image 然后用 css 来统一处理,看下代码:

适配方案:

上面讲了一些基础概念,下面讲具体适配。

对于ui设计师给的一张设计稿,怎么将其还原到页面上?对于不同手机屏幕,其dpr不同,屏幕尺寸也不同,考虑到各种情况,有很多适配方案,所以不同的适配方案,实现方法不同,处理复杂度也不同,还原程度也不同。

方案一

固定高度,宽度自适应。

这种方案是目前使用较多的方案,也是相对较简单的实现方案:

该方法使用了理想视口:

<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

垂直方向使用固定的值,水平方向使用弹性布局,元素采用定值、百分比、flex布局等。这种方案相对简单,还原度也非常低。

方案二:

固定布局视口宽度,使用viewport进行缩放

如:荔枝FM、网易应用

荔枝的代码:

if(/Android (d+.d+)/.test(navigator.userAgent)){
  var version = parseFloat(RegExp.$1);
  if(version>2.3){
    var phoneScale = parseInt(window.screen.width)/640;
    if(/MZ-M571C/.test(navigator.userAgent)){
      document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">');
    }else if(/M571C/.test(navigator.userAgent)&&/LizhiFM/.test(navigator.userAgent)){
      document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">');
    }else{
      document.write('<meta name="viewport" content="width=640, minimum-scale = '+ phoneScale +', maximum-scale = '+ phoneScale +', target-densitydpi=device-dpi">');
    }
  }else{
    document.write('<meta name="viewport" content="width=640, target-densitydpi=device-dpi">');
  }
}else{
  document.write('<meta name="viewport" content="width=640, user-scalable=no, target-densitydpi=device-dpi">');
}

网易应用:

var win = window,
        width = 640,
        iw = win.innerWidth || width,
        ow = win.outerHeight || iw,
        sw = win.screen.width || iw,
        saw = win.screen.availWidth || iw,
        ih = win.innerHeight || width,
        oh = win.outerHeight || ih,
        ish = win.screen.height || ih,
        sah = win.screen.availHeight || ih,
        w = Math.min(iw, ow, sw, saw, ih, oh, ish, sah),
        ratio = w / width,
        dpr = win.devicePixelRatio;
        if (ratio = Math.min(ratio, dpr), 1 > ratio) {
            var ctt = ",initial-scale=" + ratio + ",maximum-scale=" + ratio,
                metas = document.getElementsByTagName("meta");ctt += "";
            for (var i = 0, meta; i < metas.length; i++) meta = metas[i], "viewport" == meta.name && (meta.content += ctt)
        }

固定布局视口,宽度设置固定的值,总宽度为640px,根据屏幕宽度动态生成viewport。(设计稿应该是640px的)

<meta name="viewport" content="width=640, minimum-scale = 0.5625, maximum-scale = 0.5625, target-densitydpi=device-dpi">

这种方式布局如荔枝FM的网页宽度始终为640px。缩放比例scale为:

var scale = window.screen.width / 640

设计稿为640px时,正好可以1:1以px来写样式。但是1px所对应的物理像素就不一定是1了。

(window.screen.width * dpr) / 640   // 1px对应的物理像素

永利游戏平台 13

iphone5.png

永利游戏平台 14

iphone6.png

方案三:

根据不同屏幕动态写入font-size,以rem作为宽度单位,固定布局视口。

如网易新闻:

<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

以640px设计稿和750px的视觉稿,网易这样处理的:

var width = document.documentElement.clientWidth;   // 屏幕的布局视口宽度
var rem = width / 7.5;                              // 750px设计稿将布局视口分为7.5份
var rem = width / 6.4;                              // 640px设计稿将布局视口分为6.4份

这样不管是750px设计稿还是640px设计稿,1rem 等于设计稿上的100px。故px转换rem时:

rem = px * 0.01;

在750px设计稿上:

75px 对应 0.75rem, 距离占设计稿的10%;

在ipone6上:
width = document.documentElement.clientWidth = 375px;
rem = 375px / 7.5 = 50px;
0.75rem = 37.5px;   (37.5/375=10%;占屏幕10%)

在ipone5上:
width = document.documentElement.clientWidth = 320px;
rem = 320px / 7.5 = 42.667px;
0.75rem = 32px; (32/320=10%;占屏幕10%)

故对于设计稿上任何一个尺寸换成rem后,在任何屏下对应的尺寸占屏幕宽度的百分比相同。故这种布局可以百分比还原设计图。

永利游戏平台 15

iphone5-2.png

永利游戏平台 16

iphone6-2.png

方案四:

以rem作为宽度单位,动态写入viewport和font-size进行缩放。

根据设置的dpr设置font-size。如:

document.documentElement.style.fontSize = 50 * dpr;
// dpr 为设置的设备像素比。(注意不是设备自身的设备像素比,而是认为设置的dpr)

这种情况下,dpr = 1时,1rem = 50px;

dpr = 2时, 1rem = 100px;

当设计以iphone6为标准,出750px的设计稿时,此时dpr=2,故1rem 等于100px,将图上的尺寸转换为rem非常方便,除以100就行。代码如下:

var scale             = 1.0;
var dpr             = 1;
var isAndroid         = window.navigator.appVersion.match(/android/gi);
var isIPhone          = window.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio  = window.devicePixelRatio;
// 此处只简单对ios做了伸缩处理,安卓没有做伸缩处理,统一dpr = 1
if ( isIPhone ) {
  scale /= devicePixelRatio;
  dpr *= devicePixelRatio;
}

var viewport        = document.getElementById('viewport');
var content         = 'initial-scale=' + scale + ', maximum-scale=' + scale + ',minimum-scale=' + scale + ', width=device-width, user-scalable=no';

viewport.setAttribute( 'content', content );
document.documentElement.style.fontSize = 50 * dpr + 'px';
document.documentElement.setAttribute('data-dpr', dpr);

对于该方案,

假设肉眼看到的宽度(视觉宽度):visualWidth,令dpr=1时,其1rem对应的宽度为50.

dpr = 1 时, 1rem = 50px, initial-scale=1, 缩放为1。
visualWidth = 50 * 1 = 50;

dpr = 2 时, 1rem = 100px, initial-scale=0.5, 缩放为0.5。
visualWidth = 100 * 0.5 = 50;

dpr = 3 时, 1rem = 150px, initial-scale=0.3333, 缩放为0.3333。
visualWidth = 150 * 0.3333 = 50;

所以该方案,1rem在所有屏幕上对应的肉眼距离相同,故不同屏幕下,总的rem数不同,大屏下总的rem数大于小屏下,如iphone6下,总宽度为7.5rem,iphone5下,总宽度为6.4rem。故此方案不能百分比还原设计稿,故写样式时,对于大块元素应该用百分比,flex等布局,不能直接用rem。

关于这个方案的详细教程请参考这篇文章传送门

永利游戏平台 17

iphone5-3.png

永利游戏平台 18

iphone6-3.png

方案五:

根据不同屏幕动态写入font-size和viewport,以rem作为宽度单位

将屏幕分为固定的块数10:

var width = document.documentElement.clientWidth;   // 屏幕的布局视口宽度
var rem = width / 10;                               // 将布局视口分为10份

这样在任何屏幕下,总长度都为10rem。1rem对应的值也不固定,与屏幕的布局视口宽度有关。

对于动态生成viewport,他们原理差不多,根据dpr来设置缩放。看看淘宝的:

var devicePixelRatio = window.devicePixelRatio;
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var dpr,scale;
if (isIPhone) {
  if (devicePixelRatio >=3) {
    dpr = 3;
  } else if (devicePixelRatio >=2) {
    dpr = 2;
  } else {
    dpr = 1;
  }
} else {
  dpr = 1;
}
scale = 1 / dpr;

淘宝只对iphone做了缩放处理,对于android所有dpr=1,scale=1即没有缩放处理。

此方案与方案三相似,只是做了viewport缩放,能百分比还原设计稿。

永利游戏平台 19

iphone5-4.png

永利游戏平台 20

iphone6-4.png

适配中要解决的问题 :

移动端适配最主要的是使在不同屏幕下不用缩放页面就能正常显示整个页面。以上方案都完成了这一需求。其次有几个需求:

1、解决高清屏下1px的问题,其实有很多hack方法,这里只讲了缩放视口。先将布局视口设置为高清屏的物理像素。这样css中1px就是1个物理像素,这样看到的线条才是真正的1px。但是此时视口宽度大于设备的宽度,就会出现滚动条。故对视口进行缩放,使视口宽度缩放到设备宽度。

淘宝团队在处理安卓端的缩放存在很多问题,所以dpr都做1处理,所以安卓端就没有解决1px的问题。

2、在大屏手机中一行看到的段落文字应该比小屏手机的多。

由于淘宝和网易新闻rem都是百分比,故如果用rem一行显示的文字个数应该是相同的。故对于段落文本不能用rem作为单位,应该用px处理,对于不同的dpr下设置不同的字体。

.selector { 
  color: red; 
  font-size: 14px; 
}

[data-dpr="2"] .selector { 
  font-size: 28px; // 14 * 2
} 

[data-dpr="3"] .selector { 
  font-size: 42px; // 14 * 3
}

对于方案四,不管什么情况下,1rem对应的视觉上的宽度都是一样的,而对应的大屏、小屏手机其视觉宽度当然不同,故字体设置为rem单位时,也能满足大屏手机一行显示的字体较多这个需求。

五种方案对比:

上面四种方案对设计稿还原程度是有差别的。

除了方案一和方案四以外,其他方案都是百分比还原设计稿,大屏下元素的尺寸就大。

方案一还原设计稿程度较低,这里不做说明。

方案二做了百分比适配,部分1px适配,没有字体适配。

方案三做了百分比适配,没有1px适配,有字体大小适配。

方案四没有做百分百适配,布局要用百分百和flex布局,做了1px的适配,并且对于段落文字直接可以用rem做单位,不需要做适配。

方案五做了百分比适配,有1px适配,有字体大小适配。

项目中遇到的问题:

在我们项目中方案四和方案五都用过。

方案五在使用中没有遇到什么问题,就是刚开始没有做字体适配都是用的rem,后面加入了字体适配,这种方案设计师相对轻松些,不用考虑在大小屏幕下的布局效果。

方案四时没有跟ui设计师沟通清楚,导致设计师在设计图上一行排了很多交互元素,在小屏下放不下去,又不能简单放百分比(元素里的文字放不下)。所以还是要做动态判断大小屏,做出相应适配。这个方案可能设计师需要考虑的多些,尽量减少一行内的交互元素,当一行交互元素多时要考虑小屏手机怎么适配。

其实对于1px的适配在苹果端很好,在android端各个厂商手机差别太大,适配有很多问题。这是为什么绝大多数方案里都放弃了android端1px适配。不过最近看到很多网站都用了densitydpi=device-dpi这个安卓的私有属性来兼容部分安卓机型,这个属性在新的webkit已经被移除了,使用它主要为了兼容低版本的android系统。

这里大漠老师针对flexible方案进行了改版,兼容了更多的android机型的1px效果。文章传送门

他给了个压缩版的方案,我看了下源码,把它写了一遍,不知道有没有问题,效果是一样的。

var dpr, scale, timer, rem;
var style = document.createElement('style');

dpr = window.devicePixelRatio || 1;
scale = 1 / dpr;

document.documentElement.setAttribute('data-dpr', dpr);
var metaEl = document.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'target-densitydpi=device-dpi, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
document.documentElement.firstElementChild.appendChild(metaEl);
document.documentElement.firstElementChild.appendChild(style);
if (980 === document.documentElement.clientWidth) {
  metaEl.setAttribute('content', 'target-densitydpi=device-dpi,width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1');
}

function refreshRem () {
  var c = '}';
  var width = document.documentElement.clientWidth;
  var isPhone = window.navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i);
  if (!isPhone && width > 1024) {
    width = 640;
    c = 'max-width:' + width + 'px;margin-right:auto!important;margin-left:auto!important;}';
  }
  window.rem = rem = width / 16;
  style.innerHTML = 'html{font-size:' + rem + 'px!important;}body{font-size:' + parseInt(12 * (width / 320)) + 'px;' + c;;
}

refreshRem();

window.addEventListener('resize', function () {
  clearTimeout(timer);
  timer = setTimeout(refreshRem, 300);
}, false);

window.addEventListener('pageshow', function (e) {
  if (e.persisted) {
    clearTimeout(timer);
    timer = setTimeout(refreshRem, 300);
  }
}, false);

这些方案只是针对绝大部分机型,项目中可能有些特殊机型有特殊问题,需要特殊对待。比如在这篇文章中作者使用flexible在小米max和荣耀8中有问题,需要特殊hack。传送门,我没有这种手机,也没有对此做验证。

对于上面的五种方案,方案五看似是适配最好的,但是当项目中引入第三方插件时可能要一一适配,比如:引入一个富文本,里面设置字体大小的一般都是px,你需要将其一一转换成rem。而对于方案二,可以直接用px做单位来百分百还原设计稿,引入的插件时也不用适配。所以说,具体项目中用哪个方案,其实不光是前端的选择,还要跟设计师讨论下,满足设计需求,选择最适合项目的方案。

以上是个人对于移动端适配的一些理解,如有不对欢迎指正。

参考文章:

移动前端开发之viewport的深入理解

再谈Retina下1px的解决方案

使用Flexible实现手淘H5页面的终端适配

永利游戏平台,移动端适配方案(上)

viewports剖析

<html>
  <head>
    <script>
      var docEl = document.documentElement
      var deviceWidth = docEl.clientWidth;
      var dpr = window.devicePixelRatio;
      var scale = 1 / dpr; // 如果我们做到 dip 和物理像素 1:1 
      var metaEl = document.createElement('meta');
      metaEl.setAttribute('name', 'viewport');
      metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale );
      docEl.firstElementChild.appendChild(metaEl);
      // 给 html 添加 font-size 和 data-dpr
      docEl.style.fontSize = document.documentElement.clientWidth/100 + 'px';
      docEl.setAttribute('data-dpr', dpr);
    </script>
    <style>
      div {
        width: 50rem;
      }
      .page {
        width: 90rem;
        height: 100rem;
        background-image: url(bg.png) /* 25x25 图片 */
      }
      [data-dpr="2"] .page {
        background-image: url(bg@2x.png) /* 50x50 图片 */
      }

      [data-dpr="3"] .page {
        background-image: url(bg@3x.png) /* 75x75 图片 */
      }
    </style>
  <head>
</html>

这样不同分辨率的页面自动加载不同的图片,解决了第三个图片模糊的问题。多屏适配的方案大致的内容就都在这里了,但是我们参看 lib.flexible 库,它对字体推荐是使用 px 而非 rem,那么针对字体 css 同样需要:

div {
    width: 1rem; 
    height: 0.4rem;
    font-size: 12px; /* 默认写上dpr为1的fontSize */
}

[data-dpr="2"] div {
    font-size: 24px;
}

[data-dpr="3"] div {
    font-size: 36px;
}

网上搜了下,看到给出的理由是 :

设计师原本的要求是这样的:任何手机屏幕上字体大小都要统一 (注意,字体不可以用rem,误差太大了,且不能满足任何屏幕下字体大小相同)

我觉得这个说法也是合理的,所以最终多屏适配的方案的细节还是需要大家根据自己的项目进行微调。

移动H5页面开发多屏适配的方案内容总结到这里,我了解了大致的原理就可以放心使用 lib.flexible 了。

本文由永利集团登录网址发布于web资讯,转载请注明出处:移动端页面适配永利游戏平台———多方案解析

关键词: