开发课程 | 支付宝小程序开发中CSS中的“单位”

开发课程 | 支付宝小程序开发中CSS中的“单位”

作者简介:雪庭,来自蚂蚁金服支付宝,曾就职于 MathWorks、网易,具有丰富的软件开发、前端开发经验;从零到一打造支付宝电子发票、信用卡还款等小程序,支撑千万级PV应用;蚂蚁金服技术大学金牌讲师,所授课程均获得近乎满分评价。现推出支付宝小程序开发系列课程,将前端基础、小程序开发等知识与业务中沉淀的最佳实践相结合,深入浅出带你快速了解必要的前端基础,并掌握如何开发支付宝小程序

CSS units

单位是 css 中最基本的内容,常用的有 px,em,rem,以及和 viewport 相关的vw,vh 等,本文将从概念着手,辅以实例说明,将各常用单位的含义、用法进行讲解。

让我们开始吧!

px

绝对长度单位,非常熟悉,概念不再赘述。优点在使用简单,但是设置的属性值不会根据屏幕的大小变化而变化,也不会根据用户设置的字体大小发生改变

常用在设置固定尺寸、位置上,例如:

border: 1px solid black;
border-radius: 3px;

em

1 em  等于当前元素父元素的 font-size,例如父元素的 font-size 为 20px,则 1rem = 20px 。如果父元素未显式设置 font-size,则向上继承,直至 body 元素

基础示例

举个例子

(codepen 链接:https://codepen.io/yu-xueting/pen/byQxYZ?editors=1100#0

<div class="parent">
  parent
  <div class="child">
    child1
    <div class="child">
      child2
    </div>
  </div>
</div>
.parent {
  font-size: 15px;
}

.child {
  font-size: 2em;
}

开发课程 | 支付宝小程序开发中CSS中的“单位”

原理分析:

  • parent 元素设置了 font-size,它的字体大小是 15px 
  • child1 元素 font-size 是 2em,即 2 倍于父元素(parent)的字体大小,为  15px * 2 = 30px 
  • child2 元素 font-size 是 2em,即 2 倍于父元素(child1)的字体大小,为 30px * 2 = 60px 

逐层递进

在 em 的定义中,有一个关键点:父元素,即 em 的值的大小与直系父元素的 font-size 息息相关,这可以很方便的做出逐层递进的效果,比如说,sidebar!

(codepen 链接:https://codepen.io/yu-xueting/pen/eaxvXN

<div class="sidebar">
  <div class="sidebar-item">foo</div>
  <div class="sidebar-item">bar
    <div class="sidebar-item">aa</div>
    <div class="sidebar-item">bb
      <div class="sidebar-item">x</div>
      <div class="sidebar-item">y</div>
    </div>
  </div>
  <div class="sidebar-item">baz</div>
</div>
.sidebar {
  font-size: 60px;
}

.sidebar-item {
  border-left: 1px solid blue;
  font-size: 0.6em;
  margin-left: 1em;
}

开发课程 | 支付宝小程序开发中CSS中的“单位”

原理分析:

  • 每层 sidebar-item 设置的 font-size 为 0.75em,即为父元素字体大小的 0.75 倍,那么随着层级的加深,每层 sidebar-item 的字体大小会越来越小
  • 每层 sidebar-item 设置的 margin-left 为 1em,即左边距设置为父元素字体大小,随着层级的加深,margin-left 也呈递减态势(例子中利用 border-left 来辅助展示 margin-left)

屏幕自适应

em 是相对长度单位,不同于 px , em 的值是动态的,是可变的,只要调整父元素的字体大小,就可以改变 em 的值,这为不同屏幕宽度自适应布局提供了解决方案:

如果我们想在更宽的屏幕上显示更大的字体、更大的图片,只需要根据屏幕宽度设置父元素的 font-size 即可

来看如下例子,例子中使用顶部滚动条来模拟不同宽度屏幕下动态设置父元素 font-size 的过程,例子中 title 的字体大小是固定的,可借此对比 px 和 em 的区别

(codepen链接:https://codepen.io/yu-xueting/pen/zQeEmE

<div>拖动滚动条,模拟屏幕宽度变化</div>
<input type="range" id="range" min="1" max="6" step="0.1" value="2" oninput="onChange()" />
<div class="container">
  <div class="title">TITLE</div>
  <div class="content">
    <div class="logo"></div>
    <div class="text">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc vitae convallis felis. Quisque egestas lorem ligula, vel facilisis velit maximus a. Pellentesque gravida vestibulum odio. Suspendisse sagittis viverra arcu vel convallis. Duis magna felis, blandit eu nisl sollicitudin, lobortis varius odio. Pellentesque rhoncus at ante et vestibulum. Pellentesque et elit eu sapien vehicula euismod id vitae velit. Donec eleifend ligula eu nulla vulputate, sed porttitor velit mattis. Fusce scelerisque volutpat cursus. Phasellus eleifend, quam nec mollis blandit, nisi felis hendrerit dui, vitae efficitur urna est vitae dui. Vivamus vel pretium tellus. Vestibulum lobortis quam ac quam lobortis, sit amet fermentum purus lobortis. Cras rutrum ultricies lacus ac faucibus.
    </div>
  </div>
</div>
.container {
  width: 600px;
  font-size: 30px;
  border: 1px solid #108EE9;
  padding: 20px;
}

.logo {
  width: 2em;
  height: 2em;
  background: #108EE9;
}

.title {
  text-align: center;
  font-size: 50px;
  color: #108EE9;
}

.text {
  font-size: 0.5em;
}
function onChange () {
  const range = document.getElementById("range").value;
  document.querySelector('.container').style.fontSize= 15 * range + 'px';
}

开发课程 | 支付宝小程序开发中CSS中的“单位”

原理分析:

  • logo(蓝色方块)和 text(段落文字)使用 em 为单位设置宽、高、字体大小
  • 拖动滚动条,动态设置 container 的字体大小
  • 根据 em 的定义,container 元素是 logo 和 text 的父元素,改变 container 的字体大小会改变 em 的值,进而改变 logo 的大小及段落文字的字体大小

rem

1rem 等于根元素(html 元素)的字体大小,例如 html 元素的 font-size 为 20px,则 1rem = 20px 

em vs rem

em 与 rem 很相似,区别在于 rem 的值是 html 元素字体大小决定的,对于页面中各个层级的元素均相同,修改 rem 会动态更改所有以 rem 为单位的元素对应的属性值,不需要考虑层级结构

没有好坏之别,各有适用的场景

rem 适用于整体的屏幕自适应布局,只需修改 html 元素 font-size,即可全局调整页面样式
em 如上文所述,适用于写出逐层递进的效果,和部分区域自适应布局

rem 版本的屏幕自适应例子与 em 非常相似,这里不再赘述,让我们看一个更复杂的例子

固定元素在背景图片上

在不同的屏幕尺寸下,我们希望页面展示的效果是相同的,对于背景图比较好处理,通常设置背景图宽度与屏幕宽度保持一致,并保持背景图比例不变。但是除背景图外,有些元素会设置动画效果(例如呼吸的气泡),有些元素会设置操作热区供用户操作(例如刮刮卡的刮开区)。这些元素需要固定在背景图片的某个位置上,不能因为屏幕尺寸改变而移动,要如何固定呢?

举个例子,我们用图中蓝色方块来代表需要固定的元素,如何让它始终固定在 KEEP 的左上角?

开发课程 | 支付宝小程序开发中CSS中的“单位”

其实这是一个数学问题,示意图如下(图中大矩形为背景图,小矩形为定位元素)

开发课程 | 支付宝小程序开发中CSS中的“单位”

图中 w 代表屏幕宽度(背景图宽度),h 代表背景图高度,x 代表定位的位置距离背景图左边缘占比背景图宽度的大小,例如 20%,30%,y 代表定位的位置距离背景图上边缘占比背景图高度的大小

定位元素的位置信息为:

left: x * w;
top: y * h;

背景图随屏幕宽度变化而变化时,w,h 都会变化,也就是按照现有公式,left 和 top 需要设置的值始终在变,有没有什么办法可以让我们设置 left 和 top 的值是固定的,但是让变化自动进行呢?

有的,这个中间桥梁就是 rem 

关键的步骤是, 将 html 的 font-size 与屏幕宽度进行关联,进而使得以 rem 为单位的 left 和 top 的值随屏幕宽度变化而变化,来看看具体细节:

  1. 设置 html 的 font-size 为屏幕宽度 w 乘以某个系数,假设为 a,即为 a*w 
  2. 根据定义可得, 1rem = a*w 
  3. w = 1rem / a  => w = 1/a rem 
  4. 由于背景图始终保持原比例,假设比例为 r,则 h = w*r  => h = (1/a rem)*r => h = r/a rem 

现在,定位元素的位置信息为:

left: x * w => x * (1/a rem) => x/a rem;
top: y * h => y * (r/a rem) => y*r/a rem;

注意,在上述的公式中,x, y, r, a 都是常数,所以 x/a 和y*r/a 都是常数,进而导出结论,元素定位信息中的 left 和 top 可以使用固定值来设置

以下是示例

(codepen 链接:https://codepen.io/yu-xueting/pen/wbQmLX

<div>蓝色块表示需要固定的元素,它一直固定在图片 keep 文字的左上角</div>
<input type="range" id="range" min="1" max="6" step="0.1" value="3" oninput="onChange()" />
<div class="container">
  <div class="content">
    <div class=icon></div>
  </div>
</div>
html {
  font-size: 60px;
}
body {
  font-size: 16px;
}

.container {
  position: relative;
  width: 300px;
  height: 600px;
  border: 1px solid black;
}
.content {
  position: relative;
  width: 100%;
  height: 100%;
  background-image: url('https://cdn.shopify.com/s/files/1/1272/9011/files/1_6614eeeb-262e-4d53-99e6-b8cf6cac767e_1024x1024.png?v=1520891132');
  background-repeat: no-repeat;
  background-size: 100% auto;
  background-position: top center;
  overflow: hidden;
}

.icon {
  width: 30px;
  height: 30px;
  background-color: #108EE9;
  position: absolute;
  top: 3.5rem;
  left: 1.16rem;
  text-align: center;
}
function onChange () {
  const range = document.getElementById("range").value;
  document.querySelector('.container').style.width = 100*range + 'px';
  document.querySelector('html').style.fontSize = 20*range + 'px';
  document.querySelector('.icon').style.width = 10*range + 'px';
  document.querySelector('.icon').style.height = 10*range + 'px';
}

开发课程 | 支付宝小程序开发中CSS中的“单位”

viewport 相关

viewport 即视窗,可以理解为无须滚动时可见的浏览器窗口范围

  • vw - 视窗宽度的 1%,如视窗宽度为 500px,则 1vw = 5px 
  • vh - 视窗高度的 1%,如视窗高度为 500px,则 1vh = 5px 
  • vmax - vw 和 vh 中较大的值
  • vmin - vw 和 vh 中较小的值

来个例子

(codepen 链接:https://codepen.io/yu-xueting/pen/byzOBL

<div class="tip">改变结果窗口的大小,观察各 viewport 相关值的变化:</div>
<div class="test-vw">test vw</div>
<div class="test-vh">test vh</div>
<div class="test-vmax">test vmax</div>
<div class="test-vmin">test vmin</div>
body {
  font-size: 30px;
}

.tip {
  font-size: 20px;
  margin-bottom: 20px;
}

.test-vw {
  width: 30vw;
  border: 1px solid #108EE9;
}

.test-vh {
  width: 30vh;
  border: 1px solid #108EE9;
}

.test-vmax {
  width: 30vmax;
  border: 1px solid #108EE9;
}

.test-vmin {
  width: 30vmin;
  border: 1px solid #108EE9;
}

开发课程 | 支付宝小程序开发中CSS中的“单位”

Have fun! :D

Ref

开发课程 | 支付宝小程序开发中CSS中的“单位”

上一篇:运营专家详解“花呗分期”


下一篇:新能力丨困扰商家已久的“分账问题”终于被解决了!