目录

四、移动端适配

1、less使用方法(拓展)

1.1、概念

  • 含义:Less是一个CSS预处理器,赋予CSS一定的逻辑性、计算能力,当保存less文件后,vscode将自动生成CSS文件,然后css文件就可以引入html网页使用。
    • Less文件后缀是.less
    • less文件内容依然是css语法内容,只是支持变量、运算、css嵌套写法,相当于是针对css的增强处理
    • 浏览器不识别Less代码,当保存less文件后,vscode会自动生成css代码,需要网页引入css代码才能使用
  • 下载插件:在VS Code中,必须需要下载Easy LESS插件,该插件可以在保存less文件后自动生成对应的css文件

1.2、注释

  • 用法:
    • 单行注释:
      • 语法:// 注释内容
      • VS Code快捷键:Ctrl + /
    • 块注释:
      • 语法:/* 注释内容 */
      • VS Code快捷键:Shift + Alt + A
  • 特殊说明:
    • .css文件中仅仅支持块注释,所以less文件中编写的单行注释无法在css文件中生成
    • 如果运算中出现多个单位,最终单位以第一个出现的为准,例如 width: 100px / 2rem,最终的单位是px

1.3、运算

1.3.1、概念
  • 加法、减法、乘法 直接书写计算表达式即可
  • 除法:
    • 可以使用/,但是需要使用小括号包裹,例如 :(15 / 3)
    • 也可以直接使用./,其中在VS Code中会出现语法错误,所以一般用上面的方式
1.3.2、代码示例
.box {
    // 加法
    width: 100 + 200px;
    // 减法
    width: 100 - 200px;
    // 乘法
    width: 100 * 2px;
    // 除法
    width: (100 / 2px);
    width: 100 ./ 2px; // 在VSCode中会报错,不建议使用
}

1.4、变量

1.4.1、概念
  • 作用:存储数据,方便使用和修改
  • 语法:
    • 定义变量:@变量名: 变量值;
    • 使用变量:@变量名
1.4.2、代码示例
@rootSize: 37.5rem;
@subjectColor: pink;

.box {
    width: (100 / @rootSize);
    background-color: @subjectColor;
}

1.5、嵌套

1.5.1、概念
  • 作用:快速生成后代选择器
  • 用法:父元素的css设置放在大括号中,那么子元素的css设置整体都放在父元素的大括号中,以此类推
1.5.2、代码示例

在这里插入图片描述

1.6、导入、导出

1.6.1、概念
  • 作用:
    • 导入:导入公共样式的less文件到其他less文件中
    • 导出:将保存less文件后生成的css文件以特定文件名称导出到特定目录中
  • 语法:
    • 导入:
      • @import './base.less';:将base.less文件的内容进行导入。其中base.less是基础css文件,需要将base.less导入到其他less文件中
      • @import './base';:将base.less文件的内容进行导入,其中.less后缀可以省略
    • 导出:
      • 写法:在less文件中第一行添加// out: 存储CSS文件的URL路径
      • // out: ./index.css:将当前less文件导出到同级目录下,名称改为index.css
      • // out: ./css/:将当前less文件导出到css目录下,css文件名称和less文件名称一致
      • // out: false:禁止将当前less文件导出成css文件,例如base.less是基础css文件,需要导入到其他less文件中,不需要导出css文件
1.6.2、代码示例

1、导入:将base.less导入到index.less中

在这里插入图片描述

2、导出:禁止导出成css文件

在这里插入图片描述

3、导出:将index.less导出到css目录下,并且css文件名称还是index

在这里插入图片描述

2、移动端适配-概念解读

  • 屏幕分辨率:水平和垂直方向的像素点数,单位是px

  • 物理分辨率、逻辑分辨率:分辨率可以分为物理分辨率和逻辑分辨率,比如我自己的分辨率是1920*1080的,这是物理分辨率。但是由于我的屏幕是14英寸,确实有点小了,所以我将屏幕显示内容进行了缩放150%,这是逻辑分辨率。所以对于我的电脑来说,这样设置之后,分辨率变小了。编写代码看的是逻辑分辨率

  • 常见手机型号—屏幕分辨率:
    在这里插入图片描述

  • 视口:显示HTML网页的尺寸,使用逻辑分辨率,其中代码<meta name="viewport" content="width=device-width, initial-scale=1.0">中,width=device-width表示视口宽度=设备逻辑分辨率宽度,initial-scale=1.0表示缩放1倍(不进行缩放),这行代码在VS Code中写代码时会自动生成,不需要改动
    在这里插入图片描述

  • 二倍图:手机尺寸大小不一,现阶段设计稿参考iPhone6/7/8尺寸,设备宽度375px产出设计稿,那么二倍图设计稿尺寸是750px。

    • 使用二倍图原因:保证图片在各种设备中都清晰展示。
    • 如果判断2倍图:如果设计图宽度是750px,那就是二倍图,这时候需要调整设计图像素设置,调成375px的图,这样我们才能编写代码使用
      在这里插入图片描述

3、移动端-适配方案

3.1、概念

  • 适配方案:
  • PC端:只支持宽度自适应,实现方式包括:百分比布局、Flex布局
  • 移动端:宽高等比例缩放,实现方式包括:rem、vm

3.2、适配方式—rem

3.2.1、概念
  • 原理:rem单位是相对单位,以HTML标签设置的字号大小(font-size属性值)为相对依据,然后计算得出结果,例如2rem等于2 * HTML标签的font-size属性值
3.2.2、如何设置HTML标签字号(font-size属性值)
3.2.2.1、概念
  • 原理:根据设备逻辑分辨率大小进行设置,在rem目前的布局方案中,一般将网页分成10等份,HTML标签字号是视口宽度的十分之一。下图是根据视口宽度设置html标签字号的代码,但是这些写法很难兼容所有设备,因此我们需要借助阿里手机淘宝团队给出的一个js文件来统一设置html标签字号

在这里插入图片描述

3.2.2.2、如何使用手机淘宝的js文件设置html标签行号

作用: flexible.js是手机淘宝团队开发出来的一个用来适配移动端的js库。该js文件会设置html根节点标签设置font-size为设备逻辑分辨率的1/10,这样所有的子元素都会继承这个font-size,进而让每一个设置rem的地方都可以根据设备逻辑分辨率进行等比例缩放

在这里插入图片描述

使用方法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>极速问诊页</title>
</head>
<body>
    ……
    <!-- 该js为阿里手淘出品,根据逻辑分辨率设置html标签的font-size属性值,为设置rem提供依据 -->
    <script src="./js/flexible.js"></script>
</body>
</html>

flexible.js:

(function flexible (window, document) {
  var docEl = document.documentElement
  var dpr = window.devicePixelRatio || 1

  // adjust body font size
  function setBodyFontSize () {
    if (document.body) {
      document.body.style.fontSize = (12 * dpr) + 'px'
    }
    else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize)
    }
  }
  setBodyFontSize();

  // set 1rem = viewWidth / 10
  function setRemUnit () {
    var rem = docEl.clientWidth / 10
    docEl.style.fontSize = rem + 'px'
  }

  setRemUnit()

  // reset rem unit on page resize
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function (e) {
    if (e.persisted) {
      setRemUnit()
    }
  })

  // detect 0.5px supports
  if (dpr >= 2) {
    var fakeBody = document.createElement('body')
    var testElement = document.createElement('div')
    testElement.style.border = '.5px solid transparent'
    fakeBody.appendChild(testElement)
    docEl.appendChild(fakeBody)
    if (testElement.offsetHeight === 1) {
      docEl.classList.add('hairlines')
    }
    docEl.removeChild(fakeBody)
  }
}(window, document))
3.2.3、代码示例(功能演示代码,无法真正运行)

index.less:

// out: ../css/

@import './base.less';

// 定义变量
@rootSize: 37.5rem;

// 头部区域
.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: (44 / @rootSize);
    padding: 0 (15 / @rootSize);
    .back {
        // 解决父级带来的幽灵字符问题
        font-size: 0;
        .icon-left {
            font-size: (22 / @rootSize);
            vertical-align: middle;
        }
    }
    h3 {
        font-size: (17 / @rootSize);
    }
    .record {
        font-size: (15/ @rootSize);
        color: #2CB5A5;
    }
}

// 中间信息
.banner {
    margin-top: (30.5 / @rootSize);
    margin-bottom: (33.5 / @rootSize);
    text-align: center;
    img {
        width: (240 / @rootSize);
        height: (206 / @rootSize);
        margin-bottom: (18 / @rootSize);
    }
    p {
        font-size: (16 / @rootSize);
        line-height: (25 / @rootSize);
        strong {
            color: #16C2A3;
        }
    }
}

// 底部信息
.bottom {
    padding: 0 (15 / @rootSize);
    li {
        height: (78 / @rootSize);
        margin-bottom: (15 / @rootSize);
        padding: 0 (15 / @rootSize);
        border: (0.5 /@rootSize) solid #EDEDED;
        border-radius: (4 / @rootSize);
        a {
            display: flex;
            align-items: center;
            height: 100%;
            .left img {
                width: (40 / @rootSize);
                height: (40 / @rootSize);
                margin-right: (14 / @rootSize);
            }
            .middle {
                flex: 1;
                h4 {
                    color: #3C3E42;
                    font-size: (16 / @rootSize);
                    line-height: (24 / @rootSize);
                    margin-bottom: (4 / @rootSize);
                }
                p {
                    color: #848484;
                    font-size: (13 / @rootSize);
                    line-height: (20 / @rootSize);
                }
            }
            .icon-right {
                font-size: (22 / @rootSize);
                color: #848484;
            }
        }
    }
}

代码截图:

在这里插入图片描述

3.2.4、页面效果

在这里插入图片描述

3.3、适配方式—vw

3.3.1、概念
  • vw也是一个相对单位,相对于视口的尺寸。支持使用vw(英文全称viewport width,其中1vw = 1 / 100视口宽度)或者vh(英文全称 viewport height,其中1vh = 1 / 100 视口高度)。
    • 使用vw:在绘制页面的时候,UI设计工程师提供的都是iPhone6/7/8的设计图,所以我们调整设计图宽度为375px(如果是二倍图(750px),那就需要设置像素),假设使用vw单位,那么16 / 3.75vw(iphone6/7/8的宽度是375px,那么1 / 100 是3.75)代表16px,然后这个宽度就可以根据页面进行等比例改变了
      • 拓展:在计算方面,可以这么理解,16 / 3.75vw相当于 ( 16 / 3.75 ) * ( 375px / 100 ),其中最终结果就是16px
    • 使用vh:在绘制页面的时候,UI设计工程师提供的都是iPhone6/7/8的设计图,所以我们调整设计图宽度为375px(如果是二倍图(750px),那就需要设置像素),假设使用vh单位,那么16 / 6.67vh(iphone6/7/8的高度是667px,那么1 / 100 是6.67)代表16px,然后这个宽度就可以根据页面进行等比例改变了
    • 综上所述:vw和vh不能一起使用,要么使用vw,要么使用vh,不然图片在等比例变化时会脱离原设计图的样子,一般使用vw就可以了
  • 对比rem实现方案:vw实现方案直接相对于视口宽度,更加简单好用。而rem方案相对于标签字号,并且需要使用手淘js文件计算html标签的根字号,感觉还是麻烦一点
3.3.2、代码示例(功能演示代码,无法真正运行)

index.less:

// out: ../css/

@import url(./base.less);

@vw: 3.75vw;

body {
    background-color: #f9fafb;
    font-size: (14 / @vw);
}

// 头部
.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: fixed;
    left: 0;
    top: 0;
    z-index: 9999;
    width: 100%;
    height: (50 / @vw);
    background-color: #fff;
    .logo {
        width: (235 / @vw);
        height: 100%;
        background: url(../assets/head.png) no-repeat left center / contain;
    }
    a {
        padding: 0 (15 / @vw);
        height: (30 / @vw);
        margin-right: (15 / @vw);
        text-align: center;
        line-height: (30 / @vw);
        font-size: (13 / @vw);
        border-radius: (15 / @vw);
        background-color: #ffe31b;
    }
}

// 搜索栏
.search {
    height: (32 / @vw);
    margin: (60 / @vw) (15 / @vw) (10 / @vw) (15 / @vw);
    border-radius: (16 / @vw);
    background-color: #f2f4f5;
    text-align: center;
    line-height: (32 / @vw);
    font-size: (14 / @vw);
    color: #a1a4b3;
    span {
        font-size: (16 / @vw);
    }
}

// 轮播图
.banner {
    width: (345 / @vw);
    height: (108 / @vw);
    margin: 0 auto;
    ul {
        display: flex;
        a {
            display: block;
            width: (345 / @vw);
            height: (108 / @vw);
            img {
                width: 100%;
                height: 100%;
                object-fit: cover;
                border-radius: (5 / @vw);
            }
        }
    }
}

// 排行榜
.list {
    margin: (20 / @vw) (15 / @vw) 0 (15 / @vw);
    .content {
        li {
            display: flex;
            align-items: top;
            height: (105 / @vw);
            margin-bottom: (16 / @vw);
            .pic {
                margin-right: (20 / @vw);
                img {
                    width: (105 / @vw);
                    height: (105 / @vw);
                    border-radius: (10 / @vw);
                    object-fit: cover;
                }
            }
            .txt {
                dt a {
                    color: #333;
                    line-height: 1.8;
                    span {
                        vertical-align: (-1.5 / @vw);
                    }
                }
                dd a {
                    font-size: (12 / @vw);
                    color: #a1a4b3;
                    line-height: 1.8;
                }
            }
        }
    }
}

.title {
    margin-bottom: (16 / @vw);
    height: (26 / @vw);
    display: flex;
    justify-content: space-between;
    align-items: center;
    h4 {
        font-size: (20 / @vw);
    }
    a {
        font-size: (12 / @vw);
        color: #a1a4b3;
        line-height: 1;
        span {
            font-size: (16 / @vw);
            vertical-align: (-1 / @vw);
        }
    }
}

// 推荐歌单
.recommand {
    margin: 0 (15 / @vw);
    .content ul {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-between;
        li {
            margin-bottom: (16 / @vw);
            a {
                display: block;
                width: (105 / @vw);
                height: (143 / @vw);
                .pic {
                    position: relative;
                    border-radius: (16 / @vw);
                    overflow: hidden;
                    img {
                        width: (105 / @vw);
                        height: (105 / @vw);
                        object-fit: cover;
                    }
                    .hotNum {
                        position: absolute;
                        left: 0;
                        bottom: 0;
                        width: (70 / @vw);
                        height: (28 / @vw);
                        background-color: rgba(0, 0, 0, 0.8);
                        color: #fff;
                        text-align: center;
                        line-height: (28 / @vw);
                        border-top-right-radius: (16 / @vw);
                    }
                }
            }
        }
    }
}

// 下载
.download {
    display: flex;
    align-items: center;
    position: fixed;
    bottom: (30 / @vw);
    z-index: 9999;
    width: (345 / @vw);
    height: (45 / @vw);
    margin: 0 (15 / @vw);
    background-color: #fff;
    border-radius: (22.5 / @vw);
    padding: 0 (10 / @vw);
    img {
        width: (36 / @vw);
        height: (36 / @vw);
        margin-right: (10 / @vw);
    }
    p {
        flex: 1;
    }
    span {
        width: (27 / @vw);
        height: (27 / @vw);
        background-color: #f2f3f5;
        border-radius: 50%;
        font-size: (16 / @vw);
        text-align: center;
        line-height: (27 / @vw);
        vertical-align: middle;
    }
}

// 版权
.copyright {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: (40 / @vw) 0 (108 / @vw);
    .name {
        display: flex;
        align-items: flex-end;
        margin-bottom: (10 / @vw);
        img {
            width: (22 / @vw);
            height: (22 / @vw);
            vertical-align: middle;
        }
    }
    .agreement, .version, .comInto {
        color: #a1a4b3;
        font-size: (11 / @vw);
        line-height: 1.8;
    }
    .comInto span {
        font-size: (11 / @vw);
        vertical-align: middle;
    }
}

代码截图:

在这里插入图片描述

3.3.3、页面效果

在这里插入图片描述

五、响应式网页

1、媒体查询

1.1、概念

  • 含义:根据视口宽高不同,让不同的CSS生效

  • 完整写法:

    1、语法
    @media 逻辑操作符 媒体类型 逻辑操作符 (媒体特性) {
    	css语法
    }
    
    2、逻辑操作符(非必填)
    and、only、not
    
    3、媒体类型(默认值:all,非必填)
    作用:用了区分设备类型,如屏幕设备(screen)、打印设备(print)、阅读器(speech)、不区分类型(all,包括以上三种,这也是默认值)
    
    3、媒体特性
    作用:描述媒体类型的具体特征,例如当前屏幕的宽高、分辨率、横屏或者竖屏等,详细属性值请看下图
    

    在这里插入图片描述

  • 常用写法:

    @media (媒体特性) {
    	css语法
    }
    
    // 例如:当屏幕尺寸小于等于1200px的情况才生效
    @media (max-width: 1200px) {
    	css语法
    }
    
  • 优先级说明:如果多个媒体查询@media都符合要求,当出现css语法重复的时候,以最后一个媒体查询中配置的css效果为准

  • 使用注意事项:按照上面的优先级说明可知,当需要设置多个媒体查询效果时,,如果从小尺寸往大尺寸写css效果,那就使用min-width。反之使用max-width,示例如下:
    在这里插入图片描述

  • 编写css代码支持情况:不仅支持直接使用@media,然后在里面直接编写css代码。也支持引入css文件的方式,如下图所示
    在这里插入图片描述

1.2、@media属性直接设置css—效果示例

1.2.1、代码示例
  <!DOCTYPE html>
  <html lang="en">

  <head>
    <!-- 表头 -->
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>你好,春天~</title>
    <style>
      /* 当不符合媒体查询情况时生效  */
      body {
        background-color: gray;
      }
      /* 当屏幕宽度 >= 768px时生效 */
      @media (min-width: 768px) {
        body {
          background-color: goldenrod;
        }
      }
      /* 当屏幕宽度 >= 992px时生效 */
      @media (min-width: 992px) {
        body {
          background-color: orangered;
        }
      }
      /* 当屏幕宽度 >= 1200px时生效 */
      @media (min-width: 1200px) {
        body {
          background-color: pink;
        }
      }
    </style>
  </head>

  <body>
  </body>
  </html>
1.2.2、页面效果

在这里插入图片描述

1.3、link标签引入css文件支持媒体查询——效果示例

1.3.1、代码示例

index.html:

  <!DOCTYPE html>
  <html lang="en">

  <head>
    <!-- 表头 -->
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>你好,春天~</title>
    <link rel="stylesheet" href="./css/default.css">
    <link rel="stylesheet" media="(min-width: 768px)" href="./css/768-valid.css">
    <link rel="stylesheet" media="(min-width: 992px)" href="./css/992-valid.css">
    <link rel="stylesheet" media="(min-width: 1200px)" href="./css/1200-valid.css">
  </head>

  <body>
  </body>
  </html>

default.css:

body {
    background-color: gray;
}

768-valid.css:

body {
    background-color: goldenrod;
}

992-valid.css:

body {
    background-color: orangered;
}

1200-valid.css:

body {
    background-color: pink;
}
1.3.2、页面效果

在这里插入图片描述

六、教程案例

1、淘宝轮播图-html+css版本

1.1、代码示例

  <!DOCTYPE html>
  <html lang="en">

  <head>
    <!-- 表头 -->
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>你好,春天~</title>
    <link rel="stylesheet" href="./iconfont/iconfont.css">
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      /* 清除 a 标签所有默认样式 */
      a {
        /* 1. 去除下划线(核心) */
        text-decoration: none;
        /* 2. 清除默认颜色(继承父元素颜色) */
        color: inherit;
        /* 3. 清除点击后的高亮边框(部分浏览器) */
        outline: none;
        /* 4. 清除点击时的背景高亮(移动端) */
        -webkit-tap-highlight-color: transparent;
      }

      /* 可选:清除 visited(访问后)状态的默认紫色 */
      a:visited {
        color: inherit;
      }

      li {
        list-style: none;
      }

      .banner {
        position: relative;
        width: 556px;
        height: 276px;
        margin: 50px auto;
        overflow: hidden;
      }

      .banner ul {
        display: flex;
      }

      .banner ul li img {
        width: 556px;
        border-radius: 10px;
        vertical-align: middle;
      }

      .banner .prev,
      .banner .next {
        display: none;
        position: absolute;
        width: 20px;
        height: 30px;
        top: 50%;
        transform: translateY(-50%);
        background-color: rgba(0, 0, 0, 0.3);
        border-radius: 0 15px 15px 0;
        color: #fff;
        font-size: 10px;
        line-height: 30px;
        text-align: center;
        vertical-align: middle;
      }
      .banner .next {
        right: 0;
        border-radius: 15px 0 0 15px;
      }
      .banner:hover .prev,
      .banner:hover .next {
        display: block;
      }
      .banner ol {
        position: absolute;
        left: 50%;
        bottom: 20px;
        transform: translateX(-50%);
        display: flex;
        height: 13px;
        align-items: center;
        border-radius: 10px;
        background-color: rgba(0, 0, 0, 0.1);
      }
      .banner ol li {
        width: 8px;
        height: 8px;
        background-color: #fff;
        border-radius: 4px;
        margin: 3px;
        cursor: pointer;
      }
      .banner ol li.active {
        background-color: #ff5000;
      }
    </style>
  </head>

  <body>
    <div class="banner">
      <!-- 轮播图 -->
      <ul>
        <li>
          <a href="#">
            <img src="https://cdn.pinduoduo.com/upload/official_website/c90fe04a-27dd-4e66-9a41-4de27e5c19f6.png" alt="">
          </a>
        </li>
        <li>
          <a href="#">
            <img src="https://cdn.pinduoduo.com/upload/official_website/c90fe04a-27dd-4e66-9a41-4de27e5c19f6.png" alt="">
          </a>
        </li>
        <li>
          <a href="#">
            <img src="https://cdn.pinduoduo.com/upload/official_website/c90fe04a-27dd-4e66-9a41-4de27e5c19f6.png" alt="">
          </a>
        </li>
      </ul>
      <!-- 箭头 -->
      <a href="#" class="prev">
        <span>&lt;</span>
      </a>
      <a href="#" class="next">
        <span>&gt;</span>
      </a>
      <!-- 圆点 -->
      <ol>
        <li></li>
        <li class="active"></li>
        <li></li>
      </ol>
    </div>
  </body>

  </html>

1.2、页面效果

在这里插入图片描述

七、VS Code用法

1、Emmet缩写

说明: 当缩写编写完毕后,点击回车按键,即可生成完整结构

1.1、HTML

  • 类选择器
    • 写法:标签名.类名
    • 示例:div.box
    • 效果:<div class="box"></div>
  • id选择器
    • 写法:标签名#id名
    • 示例:div#box
    • 效果:<div id="box"></div>
  • 同级标签:
    • 写法:标签名1+标签名2
    • 示例:div+p
    • 效果:<div></div><p></p>
  • 父子级标签:
    • 写法:标签名>子标签名
    • 示例:div>p
    • 效果:<div><p></p></div>
  • 多个相同标签:
    • 写法:标签名*数量
    • 示例:div*2
    • 效果:<div></div><div></div>
  • 携带内容的标签:
    • 写法:标签名{标签内容}
    • 示例:div{你好}
    • 效果:<div>你好</div>

1.2、CSS

  • 说明:大多数简写方式为属性单词的首字母
  • 示例1:w100,代表:width: 100px;
  • 示例2:bgc,代表:background-color: #ffff;
  • 示例3:w100+h100,代表:width:100px; height:100px;

2、快捷键

  • Ctrl + Enter:在光标下方插入新行,无论光标是否在行尾
  • 复制行:按下 Shift + Alt + 下箭头(向下复制)或 Shift + Alt + 上箭头(向上复制)
  • 删除当前行:Ctrl+Shift+K
  • 代码格式化:Shift+Alt+F

八、CSS单位使用介绍

1、绝对单位(Absolute Units)

绝对单位是固定的物理长度,不会因设备、字体、视口大小而改变。但在屏幕上,由于设备像素密度不同,实际显示的物理尺寸可能略有差异。

单位名称定义换算关系使用场景注意事项
px像素设备屏幕上的一个点。CSS 规范规定 1px 为 1/96 英寸。1in = 96px屏幕显示首选:边框、阴影、小图标、定位偏移在高分辨率屏幕(Retina)上,1px 可能对应多个物理像素,但布局逻辑不变
pt传统印刷单位,1pt = 1/72 英寸1pt = 1/72in ≈ 0.3528mm打印样式:字体大小、上下边距屏幕显示时,不同系统映射不同,通常 1pt = 1.333px(基于 96dpi)
pc派卡印刷单位1pc = 12pt = 1/6in极少使用,传统排版可简单视为 12pt
cm厘米物理厘米1cm = 1/2.54in打印样式,控制实际尺寸屏幕显示误差大,不推荐
mm毫米物理毫米1mm = 0.1cm打印样式cm
in英寸物理英寸1in = 2.54cm = 96px(参考值)打印样式cm

📌 总结:屏幕用 px,打印用 ptcm。绝对单位在响应式设计中基本不用于布局。


2、相对单位(Relative Units)

相对单位依赖于另一个基准值,分为三类:字体相对单位(相对于字号的)、视口相对单位(相对于视口尺寸的)、百分比(相对于父元素或自身属性的)。

2.1、字体相对单位(Font-relative Units)

它们基于 字体度量基准字号 计算。

单位定义参照基准计算示例特点 / 注意事项典型应用
em相对单位,相对于当前元素的 font-size当前元素的 font-size父元素 font-size: 20px,子元素 font-size: 0.8em → 子元素字号 16px;
子元素 width: 10em → 宽度 160px
嵌套传递:子元素再用 em 会继续相对于新字号;
属性如 margin 使用 em 时,参照的是当前元素的字号
组件内与字号相关的间距(按钮内边距、图标尺寸)
remroot em,相对于根元素的 font-size根元素 <html>font-size根字号 16px,1.5rem = 24px;
根字号改为 20px → 自动变为 30px
不受父元素影响,适合全局控制;
媒体查询时只需修改根字号即可实现整体缩放
大多数元素的尺寸、间距(最推荐用于响应式)
ex相对于当前字体的 x-height(小写字母 x 的高度)当前字体的 x-height一般约等于 0.5em,但依赖于字体不同字体差异大,难以预判;很少单独使用某些垂直对齐微调,例如让图标与文字基线对齐
ch相对于当前字体中 数字 0 的宽度(即字符“0”的 advance measure)当前字体的“0”宽度等宽字体中:20ch 大约是 20 个英文字符的宽度;
非等宽字体,每个字符宽度不同
对于等宽字体(如 monospace)预测性好;对于比例字体,实际宽度仍可变控制文本列宽(如密码框、代码块),使其大约容纳一定数量的字符
ic相对单位(较新)。相对于当前字体中 表意文字(CJK 水字) 的 advance measure中日韩字体中“水”字(或全角字符)的宽度一个全角字符宽度 = 1ic;常用于亚洲文字排版ch 类似,但针对全角字符;兼容性较低中文、日文、韩文内容容器宽度

🔍 emrem 深度对比

  • em 嵌套陷阱:

    /* 根字号 16px */
    .parent { font-size: 1.5em; } /* =24px */
    .child { font-size: 1.5em; }  /* =36px(相对于父节点 24px) */
    

    解决方案:全局用 rem,仅在需要相对自身字号时用 em(如按钮 padding)。

  • font-size 中使用 em/rem 的区别

    html { font-size: 16px; }
    .box { font-size: 1.25rem; } /* 20px,固定 */
    .box span { font-size: 0.8em; } /* 16px,相对于 .box 的 20px */
    

2.2、视口相对单位(Viewport-relative Units)

视口指浏览器可视区域(不包括工具栏、滚动条)。这些单位适合全屏布局、响应式字体。

单位定义基准计算方式场景注意事项
vw视口宽度的 1%浏览器视口宽度若视口宽 1000px,1vw = 10px全宽容器 (width: 100vw);响应式标题 (font-size: 5vw)包含滚动条宽度,可能导致水平溢出(100vw 比父容器宽)
vh视口高度的 1%浏览器视口高度视口高 800px,1vh = 8px全高容器 (height: 100vh);弹窗移动端地址栏出现/消失时会改变 100vh 的实际高度
vminvwvh 中较小的那个min(vw, vh)竖屏手机:100vmin = 100vw(因为宽较小)保持元素在横竖屏下比例一致(例如始终正方形)需要稳定的比例时好用
vmaxvwvh 中较大的那个max(vw, vh)横屏时 100vmax = 100vh装饰元素,全屏背景使用较少

⚠️ 移动端 vh 问题及新解决方案:

  • 传统 100vh 在移动浏览器中,地址栏和工具栏会导致可视区域高度变化,100vh 始终等于最大可能高度(地址栏收起时),而非当前可见高度。这会导致元素被遮挡或底部留白。
  • 新视口单位(兼容性逐渐提升):
    • svh(Small Viewport Height):小视口高度——地址栏展开时的最小可视高度。
    • lvh(Large Viewport Height):大视口高度——地址栏收起时的最大可视高度。
    • dvh(Dynamic Viewport Height):动态视口高度——随地址栏变化而变化(适合弹窗)。
    • 同样有 svw / lvw / dvw 对应宽度。
  • 建议:对关键全屏元素,可以使用 min-height: 100svhheight: 100dvh

💡 视口单位与 rem 结合:响应式字体

html {
  font-size: clamp(16px, 4vw, 24px);
}

可实现字号在 16px~24px 之间随视口宽度变化。

2.3、百分比单位(%)

百分比的行为高度依赖于具体 CSS 属性,必须分情况记忆。

2.3.1、盒模型尺寸属性
属性参照基准详细说明示例
widthmax-widthmin-width父元素的 内容宽度(content width)百分比相对于父元素的 width(不包括 padding/border)父元素 width: 800px; padding: 20px;子元素 width: 50% → 400px
heightmax-heightmin-height父元素的 内容高度父元素必须有明确的高度,否则百分比无效
height: auto 时,子元素 height: 50% 不生效)
解决方法:父元素 height: 100vh 或固定像素
padding(上下左右)父元素的宽度即使 padding-top 也参照父元素宽度,不是高度常用于实现固定宽高比(如正方形:padding: 50% 0
margin(上下左右)父元素的宽度同样是参照父元素宽度(经典设计)有时会让垂直边距意外很大

🔍 宽度百分比的特殊场景

  • 如果父元素是 inline-block 或浮动,它的宽度可能由内容撑开,此时子元素百分比参照父元素的实际宽度
  • 绝对定位元素:width: 50% 参照最近的非 static 定位祖先的内容宽度。如果祖先没有设置宽度,则参照祖先的父级直到视口。

🔍 高度百分比的生效条件

  • 父元素有固定的 height(如 100vh500px10em
  • 父元素使用绝对定位且高度由其他方式确定
  • 父元素的 height 为百分比,那么它的父级也必须有一个明确的链直到视口(例如 html, body { height: 100%; }
2.3.2、字体与行高
属性参照基准计算方式注意
font-size: 150%父元素的 font-size父字号 20px → 子字号 30px效果等同于 1.5em
line-height: 150%当前元素的 font-size当前字号 20px → 行高 30px继承时:子元素继承计算后的值(如 30px),如果子元素字号不同,行高可能偏离预期。最好用无单位的 line-height: 1.5(相对于当前字号)
2.3.3、定位偏移(toprightbottomleft
定位模式参照基准示例
position: relative元素自身的尺寸(高度/宽度)自身高度 100px,top: 20% → 向下移动 20px
position: absolute最近的非 static 定位祖先(relative/absolute/fixed/sticky)的对应尺寸祖先高度 500px,top: 10% → 50px
position: fixed视口的尺寸left: 10% → 视口宽度的 10%
position: stickyrelative(在偏移前)很少用百分比
2.3.4、背景位置 background-position
  • 参照:背景图片区域大小减去背景图片自身大小的差值。
  • 例如:background-position: 25% 50% 表示图片左边距与区域左边距的差值为区域宽度减去图片宽度的 25%。
  • 通常理解:0% 0% 左上角,100% 100% 右下角。
2.3.5、变换 transform: translate() / scale() / transform-origin
  • translateX(50%):相对于元素自身的宽度(包括 border-box?实际是边界盒尺寸)。
  • translateY(50%):相对于自身高度
  • transform-origin: 50% 50%:相对于元素自身的宽/高(默认是中心)。

经典居中技巧

.center {
  position: absolute;
  top: 50%;        /* 父容器高度的 50% */
  left: 50%;       /* 父容器宽度的 50% */
  transform: translate(-50%, -50%); /* 自身宽高的一半 */
}
2.3.6、边框圆角 border-radius
  • 水平方向百分比:参照元素盒子的宽度
  • 垂直方向百分比:参照元素盒子的高度
  • 例如 border-radius: 50% → 椭圆,若盒子是正方形就是圆。
2.3.7、Flex 和 Grid 布局

Flex 容器中:

  • flex-basis: 50%:参照父容器主轴方向的尺寸(若主轴为 row,则参照父容器宽度)。
  • 子项的 width/height 百分比仍然参照父容器内容区。

Grid 布局中:

  • grid-template-columns: 50% 25% 25%:参照网格容器的内容宽度。
  • 网格项内部的百分比参照该网格项自己的所在网格区域尺寸(由网格轨道决定)。
2.3.8、其他属性的百分比
属性参照基准说明
text-indent父元素的宽度首行缩进相比父元素宽度
vertical-align当前元素的 line-height例如 vertical-align: 20% 按行高比例上移/下移
object-position类似 background-position用于 <video><img> 的裁切定位
clip-path 中的百分比参考元素自身的边框盒例如 clip-path: polygon(50% 0, ...)

2.4、其他较新的相对单位

  • cqwcqhcqicqbcqmincqmax:容器查询单位(Container Query Units)。参照查询容器的尺寸(而非视口或父元素)。
    • cqi:内联轴尺寸的 1%(通常是宽度)
    • cqb:块轴尺寸的 1%(通常是高度)
    • 适用于组件层级响应式(容器尺寸改变时自动适配)。
  • lvh / dvh:已在前文视口单位说明。

3、单位选择完整指南

3.1、框架性原则

  1. 字体大小:根字号固定用 px%(兼容性),然后用 rem 定义各级标题和正文。移动端可用 clamp() 配合 vw 实现 fluid typography。
  2. 盒模型尺寸:宽高用 rem(固定比例)或 %(父容器占比);边距(margin/padding):
    • 与字体相关的内边距用 em(如按钮)。
    • 与父容器比例相关的用 %
    • 全局间距用 rem
  3. 全屏/视口填充100vh / 100dvh / 100svh 根据兼容性选择。100vw 注意滚动条。
  4. 边框与细节px 保持不变。
  5. 居中与变形:定位偏移用 % 配合 transform: translate(%)

3.2、典型场景示例

/* 基础设置 */
html {
  font-size: 16px;
  /* 配合视口缩放(可选) */
  /* font-size: clamp(14px, 2vw, 24px); */
}

/* 组件:卡片 */
.card {
  width: 80%;               /* 相对于父容器宽度 */
  max-width: 1200px;
  margin: 2rem auto;        /* 全局间距,不影响内部字号 */
  padding: 1.5rem;          /* rem 保持统一 */
  border: 1px solid #ccc;
}

.card h2 {
  font-size: 1.5rem;        /* 相对根字号 */
  margin-bottom: 0.5em;     /* 相对当前字号 */
}

/* 全屏弹窗 */
.modal {
  position: fixed;
  top: 10vh;                /* 相对视口高度 */
  left: 5vw;
  width: 90vw;              /* 相对视口宽度 */
  max-height: 80dvh;        /* 动态视口高度(现代浏览器) */
  overflow-y: auto;
}

/* 正方形占位(图片占位) */
.square {
  width: 100%;
  padding-top: 100%;        /* 参照父宽度,形成正方形 */
  background: #eee;
}

4、重点知识对比表

对比项目emrem% (宽度)vwvh
参照对象当前元素字号根元素字号父元素宽度视口宽度视口高度
级联影响会(嵌套放大)不会会(继承父的宽%可能失效)不会不会
典型用途组件内间距全局布局响应式比例全宽/流体字全高容器
移动端问题滚动条溢出地址栏改变
对比项目line-height: 150%line-height: 1.5
计算时机计算当前元素的具体像素值并继承继承时重新计算(基于继承元素的字体)
子元素影响子元素继承父级计算后的固定值子元素继承 1.5,并基于自己的字号重新计算
推荐性不推荐✅ 推荐

5、常见问题与排查

5.1、height: 100% 不生效怎么办?

  1. 确认父元素高度是否设置(pxvh% 但需要一直追溯到 html)。
  2. html, body { height: 100%; }
  3. 使用 min-height: 100vhmin-height: 100dvh
  4. 使用绝对定位,然后 top: 0; bottom: 0;(不依赖百分比高度)。

5.2、width: 100%width: 100vw 有何区别?

  • 100% 相对于父元素内容宽度,受父元素 paddingborder 影响(如果父元素是 box-sizing: content-box)。
  • 100vw 相对于视口宽度,通常包含滚动条宽度(如果有纵向滚动条)因而可能超出 html 宽度,导致水平溢出。

5.3、transform: translate(-50%, -50%) 为什么能居中?

因为 top: 50% 参照父元素高度,把元素左上角放在父中心;translate(-50%, -50%) 将元素向左向上移动自身宽高的一半,使得中心点对齐。

5.4、marginpadding 的百分比为什么参照宽度而不是高度?

CSS 规范设计如此,主要保证水平和垂直方向的相对一致性,以及实现固定宽高比的技巧。

更多推荐