Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Flexible 原理浅析

一、为解决缩放问题,固定屏幕宽度(移动端为什么有 1px 问题)

缩放

默认情况下,html 文件在移动端上是可以缩放的。

有两个 pixel 单位:

  • device pixel,如screen.width/screen.heightevent.screenX/event.screenY
  • CSS pixel,其余大多数值,如document.documentElement.clientWidth

在屏幕上 device pixel 的长度是不变的,可以认为是设备属性。CSS pixel 的长度可以通过缩放改变。

缩放的本质就是 CSS pixel 和 device pixel 的比例改变。

CSS pixel 和 device pixel 的比例是 100%:

放大:

缩小:

因此,默认情况下屏幕最小单位的 1px hairline,页面缩小到一定程度是会出现的。见 demo: 1-zoom

屏幕宽度

由于存在两种 pixel 单位,屏幕宽度是多少呢?

  • screen.width,用 device pixel 作为单位
  • document.documentElement.clientWidth(或window.innerWidth),用 CSS pixel 作为单位

iPhone 6 中:

screen.width // 375
document.documentElement.clientWidth // 980

禁止缩放

缩放让页面失控,为了提升用户体验,移动端通常会在 html 中引入这段代码:

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

这段代码中,

user-scalable=no表示禁止缩放。

width=device-width表示页面宽度设置为设备宽度,也就是把可变的 CSS pixel 设置为与 device pixel 相等,当然也就是把document.documentElement.clientWidth设置为与screen.width相等。见 demo: 2-device-width

initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0表示每个 CSS pixel 放大倍数,如设置为 1,则document.documentElement.clientWidth等于screen.width,如设置为 0.5,则document.documentElement.clientWidth / screen.width === 2

scale 的优先级高于 width=device-width

demo: 3-scale

在一段时间内,这种方法非常有效地提升了移动端的用户体验。但 retina 屏幕出现后,又出问题了。

retina 设备上引入这段代码后,1 个 device pixel 不是屏幕最小长度单位,而是{{devicePixelRadio}}个。若把可变的 CSS pixel 设置为与 device pixel 相等,1px 其实是 devicePixelRadio 倍的屏幕最小单位。

二、为解决 1px 问题,动态改变 scale

既然能拿到 devicePixelRadio,可以动态地设置 scale:

var dpr = window.devicePixelRatio || 1
var scale = 1 / dpr
var content = 'width=device-width, initial-scale=' + scale + ', minimum-scale=' + scale + ', maximum-scale=' + scale + ', user-scalable=no'
var viewportEl = document.querySelector(flexible)

if (viewportEl) {
  viewportEl.setAttribute('content', content)
} else {
  viewportEl = document.createElement(flexible)
  viewportEl.setAttribute('name', 'viewport')
  viewportEl.setAttribute('content', content)
  document.head.appendChild(viewportEl)
}

demo: 4-dynamic-scale

1px 的问题完美解决,然而又引入了新问题,px 单位长度在不同 dpr 的设备上非常的不同,因此用什么单位又成了问题。

三、为解决 px 单位问题,引入 rem 布局

引入动态 scale 后,在 iPhone 6 上,屏幕宽度 document.documentElement.clientWidth 是 750,在 iPhone 6 plus 上,屏幕宽度 document.documentElement.clientWidth 是 1242,同样 100px 的小方块,在两个屏幕上的占比非常不一样。因此,px 不再适用。

为了适配不同屏幕,可以引入 rem 布局。rem 在 html 中的 fontSize 值怎么确定呢?

为了方便计算,我们希望1rem = 1px。设计师的基准屏幕宽度是 750px。那么有以下公式:

屏幕宽度 / 750 = fontSize / 1,即 fontSize = 屏幕宽度 / 750。

问题又出现了,fontSize 不能小于 12px,否则直接设置为 12px,因此现在1rem === 12px

demo: 5-font-size

四、为解决 fontSize 过小问题,引入倍数

屏幕宽度 / 750 = fontSize / 1,这个公式建立在我们希望1rem = 1px

如果1rem = 12px,问题得到解决。但是每次测量都需要把 px 值除以 12 才能得到 rem 值。

直接1rem = 100px,可以让换算过程更加简单。

到此为止,问题都得到很好的解决。见 demo: 6-100

其他解决方案

  • gradient,原理是在background-image中写一个线性渐变linear-gradient,可以得到 0.5 倍的线,但在多倍屏上有局限性)
  • postcss-write-svg

参考资料

About

移动端 1px 解决方案

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages