前端CSS经典面试题总结

前端CSS经典面试题总结

2.1 介绍一 下 CSS 的盒子模型?

有两种, IE 盒子模型、W3C 盒子模型;
盒模型: 内容(content)、填充(padding)、边界(margin)、 边框(border);
区 别: IE 的 content 部分把 border 和 padding 计算了进去;

2.2 css 选择器优先级?

!important > 行内样式(比重1000)> ID 选择器(比重100) > 类选择器(比重10) > 标签(比重1) > 通配符 > 继承 > 浏览器默认属性

2.3 垂直居中几种方式?

单行文本: line-height = height
图片: vertical-align: middle;
absolute 定位: top: 50%;left: 50%;transform: translate(-50%, -50%);
flex: display:flex;margin:auto

2.4 简明说一下 CSS link 与 @import 的区别和用法?

link 是 XHTML 标签,除了加载CSS外,还可以定义 RSS 等其他事务;@import 属于 CSS 范畴,只能加载 CSS。
link 引用 CSS 时,在页面载入时同时加载;@import 需要页面网页完全载入以后加载。
link 是 XHTML 标签,无兼容问题;@import 是在 CSS2.1 提出的,低版本的浏览器不支持。
link 支持使用 Javascript 控制 DOM 去改变样式;而@import不支持。

2.5 rgba和opacity的透明效果有什么不同?

opacity 会继承父元素的 opacity 属性,而 RGBA 设置的元素的后代元素不会继承不透明属性。

2.6 display:none和visibility:hidden的区别?

display:none 隐藏对应的元素,在文档布局中不再给它分配空间,它各边的元素会合拢,就当他从来不存在。
visibility:hidden 隐藏对应的元素,但是在文档布局中仍保留原来的空间。

2.7 position的值, relative和absolute分别是相对于谁进行定位的?

relative:相对定位,相对于自己本身在正常文档流中的位置进行定位。
absolute:生成绝对定位,相对于最近一级定位不为static的父元素进行定位。
fixed: (老版本IE不支持)生成绝对定位,相对于浏览器窗口或者frame进行定位。
static:默认值,没有定位,元素出现在正常的文档流中。
sticky:生成粘性定位的元素,容器的位置根据正常文档流计算得出。

2.8 画一条0.5px的直线?

考查的是css3的transform

height: 1px;
transform: scale(0.5);

2.9 calc, support, media各自的含义及用法?

  • @support 主要是用于检测浏览器是否支持CSS的某个属性,其实就是条件判断,如果支持某个属性,你可以写一套样式,如果不支持某个属性,你也可以提供另外一套样式作为替补。
  • calc() 函数用于动态计算长度值。 calc()函数支持 “+”, “-”, “*”, “/” 运算;
  • @media 查询,你可以针对不同的媒体类型定义不同的样式。

2.10 1rem、1em、1vh、1px各自代表的含义?

  • rem
    rem是全部的长度都相对于根元素元素。通常做法是给html元素设置一个字体大小,然后其他元素的长度单位就为rem。
  • em
    子元素字体大小的em是相对于父元素字体大小
    元素的width/height/padding/margin用em的话是相对于该元素的font-size
  • vw/vh
    全称是 Viewport Width 和 Viewport Height,视窗的宽度和高度,相当于 屏幕宽度和高度的 1%,不过,处理宽度的时候%单位更合适,处理高度的 话 vh 单位更好。
  • px
    px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。
    一般电脑的分辨率有{19201024}等不同的分辨率
    1920
    1024 前者是屏幕宽度总共有1920个像素,后者则是高度为1024个像素

2.11 画一个三角形?

这属于简单的css考查,平时在用组件库的同时,也别忘了原生的css

.a {
    width: 0;
    height: 0;
    border-width: 100px;
    border-style: solid;
    border-color: transparent #0099CC transparent transparent;
    transform: rotate(90deg); /*顺时针旋转90°*/
}
<div class="a"></div>

2.12 说说你对 CSS 模块化的理解

CSS 发展

我们在书写 css 的时候其实经历了以下几个阶段:

  • 手写源生 CSS
  • 使用预处理器 Sass/Less
  • 使用后处理器 PostCSS
  • 使用 css modules
  • 使用 css in js

手写源生 CSS

在我们最初学习写页面的时候,大家都学过怎么去写 css,也就以下几种情况:

  • 行内样式,即直接在 html 中的 style 属性里编写 css 代码。
  • 内嵌样式,即在 html h 中的 style 标签内编写 class,提供给当前页面使用。
  • 导入样式,即在内联样式中 通过 @import 方法,导入其他样式,提供给当前页面使用。
  • 外部样式,即使用 html 中的 link 标签,加载样式,提供给当前页面使用。

我们在不断摸索中,逐渐形成了以编写内嵌样式外部样式为主要的编写习惯。

读到这里大家肯定有所疑问,为什么不建议使用行内样式?

使用行内样式的缺点

  • 样式不能复用。
  • 样式权重太高,样式不好覆盖。
  • 表现层与结构层没有分离。
  • 不能进行缓存,影响加载效率。

然后我们继续剖析一下,为什么不建议使用导入样式?

经测试,在 css 中使用 @import 会有以下两种情况:

1、在 IE6-8 下,@import 声明指向的样式表并不会与页面其他资源并发加载,而是等页面所有资源加载完成后才开始下载。

2、如果在 link 标签中去 @import 其他 css,页面会等到所有资源加载完成后,才开始解析 link 标签中 @import 的 css。

使用导入样式的缺点

  • 导入样式,只能放在 style 标签的第一行,放其他行则会无效。
  • @import 声明的样式表不能充分利用浏览器并发请求资源的行为,其加载行为往往会延后触发或被其他资源加载挂起。
  • 由于 @import 样式表的延后加载,可能会导致页面样式闪烁。

使用预处理器 Sass/Less

随着时间的不断发展,我们逐渐发现,编写源生的 css 其实并不友好,例如:源生的 css 不支持变量,不支持嵌套,不支持父选择器等等,这些种种问题,催生出了像 sass/less 这样的预处理器。

预处理器主要是强化了 css 的语法,弥补了上文说了这些问题,但本质上,打包出来的结果和源生的 css 都是一样的,只是对开发者友好,写起来更顺滑。

后处理器 PostCSS

随着前端工程化的不断发展,越来越多的工具被前端大佬们开发出来,愿景是把所有的重复性的工作都交给机器去做,在 css 领域就产生了 postcss。

postcss 可以称作为 css 界的 babel,它的实现原理是通过 ast 去分析我们的 css 代码,然后将分析的结果进行处理,从而衍生出了许多种处理 css 的使用场景。

常用的 postcss 使用场景有:

  • 配合 stylelint 校验 css 语法
  • 自动增加浏览器前缀 autoprefixer
  • 编译 css next 的语法

CSS 模块化迅速发展

随着 react、vue 等基于模块化的框架的普及使用,我们编写源生 css 的机会也越来越少。我们常常将页面拆分成许多个小组件,然后像搭积木一样将多个小组件组成最终呈现的页面。

但是我们知道,css 是根据类名去匹配元素的,如果有两个组件使用了一个相同的类名,后者就会把前者的样式给覆盖掉,看来解决样式命名的冲突是个大问题。

为了解决这个问题,产生出了 CSS 模块化的概念。

CSS 模块化定义

  • 你是否为 class 命名而感到苦恼?
  • 你是否有怕跟别人使用同样 class 名而感到担忧?
  • 你是否因层级结构不清晰而感到烦躁?
  • 你是否因代码难以复用而感到不爽?
  • 你是否因为 common.css 的庞大而感到恐惧?

你如果遇到如上问题,那么就很有必要使用 css 模块化。

CSS 模块化的实现方式

BEM 命名规范

BEM 的意思就是块(block)、元素(element)、修饰符(modifier)。是由 Yandex 团队提出的一种前端命名方法论。这种巧妙的命名方法让你的 css 类对其他开发者来说更加透明而且更有意义。

BEM 的命名规范如下:

/* 块即是通常所说的 Web 应用开发中的组件或模块。每个块在逻辑上和功能上都是相互独立的。 */
.block {
}

/* 元素是块中的组成部分。元素不能离开块来使用。BEM 不推荐在元素中嵌套其他元素。 */
.block__element {
}

/* 修饰符用来定义块或元素的外观和行为。同样的块在应用不同的修饰符之后,会有不同的外观 */
.block--modifier {
}

通过 bem 的命名方式,可以让我们的 css 代码层次结构清晰,通过严格的命名也可以解决命名冲突的问题,但也不能完全避免,毕竟只是一个命名约束,不按规范写照样能运行。

CSS Modules

CSS Modules 指的是我们像 import js 一样去引入我们的 css 代码,代码中的每一个类名都是引入对象的一个属性,通过这种方式,即可在使用时明确指定所引用的 css 样式。

并且 CSS Modules 在打包的时候会自动将类名转换成 hash 值,完全杜绝 css 类名冲突的问题。

使用方式如下:

1、定义 css 文件。

.className {
  color: green;
}
/* 编写全局样式 */
:global(.className) {
  color: red;
}

/* 样式复用 */
.otherClassName {
  composes: className;
  color: yellow;
}

.otherClassName {
  composes: className from "./style.css";
}

2、在 js 模块中导入 css 文件。

import styles from "./style.css";

element.innerHTML = '<div class="' + styles.className + '">';

3、配置 css-loader 打包。

CSS Modules 不能直接使用,而是需要进行打包,一般通过配置 css-loader 中的 modules 属性即可完成 css modules 的配置。

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use:{
          loader: 'css-loader',
          options: {
            modules: {
              // 自定义 hash 名称
              localIdentName: '[path][name]__[local]--[hash:base64:5]',
            }
          }
       }
    ]
  }
};

4、最终打包出来的 css 类名就是由一长串 hash 值生成。

._2DHwuiHWMnKTOYG45T0x34 {
  color: red;
}

._10B-buq6_BEOTOl9urIjf8 {
  background-color: blue;
}

CSS In JS

CSS in JS,意思就是使用 js 语言写 css,完全不需要些单独的 css 文件,所有的 css 代码全部放在组件内部,以实现 css 的模块化。

CSS in JS 其实是一种编写思想,目前已经有超过 40 多种方案的实现,最出名的是 styled-components。

使用方式如下:

import React from "react";
import styled from "styled-components";

// 创建一个带样式的 h1 标签
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

// 创建一个带样式的 section 标签
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

// 通过属性动态定义样式
const Button = styled.button`
  background: ${props => (props.primary ? "palevioletred" : "white")};
  color: ${props => (props.primary ? "white" : "palevioletred")};

  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

// 样式复用
const TomatoButton = styled(Button)`
  color: tomato;
  border-color: tomato;
`;

<Wrapper>
  <Title>Hello World, this is my first styled component!</Title>
  <Button primary>Primary</Button>
</Wrapper>;

可以看到,我们直接在 js 中编写 css,案例中在定义源生 html 时就创建好了样式,在使用的时候就可以渲染出带样式的组件了。

除此之外,还有其他比较出名的库:

  • emotion
  • radium
  • glamorous

总结

最后放一张总结好的图。

img

2.13 说说对 CSS 预编语言的理解,以及它们之间的区别

一、是什么

Css 作为一门标记性语言,语法相对简单,对使用者的要求较低,但同时也带来一些问题

需要书写大量看似没有逻辑的代码,不方便维护及扩展,不利于复用,尤其对于非前端开发工程师来讲,往往会因为缺少 Css 编写经验而很难写出组织良好且易于维护的 Css 代码

Css预处理器便是针对上述问题的解决方案

预处理语言

扩充了 Css 语言,增加了诸如变量、混合(mixin)、函数等功能,让 Css 更易维护、方便

本质上,预处理是Css的超集

包含一套自定义的语法及一个解析器,根据这些语法定义自己的样式规则,这些规则最终会通过解析器,编译生成对应的 Css 文件

二、有哪些

Css预编译语言在前端里面有三大优秀的预编处理器,分别是:

  • sass
  • less
  • stylus

sass

2007 年诞生,最早也是最成熟的 Css 预处理器,拥有 Ruby 社区的支持和 Compass 这一最强大的 Css 框架,目前受 LESS 影响,已经进化到了全面兼容 CssScss

文件后缀名为.sassscss,可以严格按照 sass 的缩进方式省去大括号和分号

less

2009年出现,受SASS的影响较大,但又使用 Css 的语法,让大部分开发者和设计师更容易上手,在 Ruby 社区之外支持者远超过 SASS

其缺点是比起 SASS 来,可编程功能不够,不过优点是简单和兼容 Css,反过来也影响了 SASS 演变到了 Scss 的时代

stylus

Stylus 是一个Css的预处理框架,2010 年产生,来自 Node.js 社区,主要用来给 Node 项目进行 Css 预处理支持

所以 Stylus 是一种新型语言,可以创建健壮的、动态的、富有表现力的 Css。比较年轻,其本质上做的事情与SASS/LESS等类似

三、区别

虽然各种预处理器功能强大,但使用最多的,还是以下特性:

  • 变量(variables)
  • 作用域(scope)
  • 代码混合( mixins)
  • 嵌套(nested rules)
  • 代码模块化(Modules)

因此,下面就展开这些方面的区别

基本使用

less和scss

.box {
  display: block;
}

sass

.box
  display: block

stylus

.box
  display: block
嵌套

三者的嵌套语法都是一致的,甚至连引用父级选择器的标记 & 也相同

区别只是 Sass 和 Stylus 可以用没有大括号的方式书写

less

.a {
  &.b {
    color: red;
  }
}
变量

变量无疑为 Css 增加了一种有效的复用方式,减少了原来在 Css 中无法避免的重复「硬编码」

less声明的变量必须以@开头,后面紧跟变量名和变量值,而且变量名和变量值需要使用冒号:分隔开

@red: #c00;

strong {
  color: @red;
}

sass声明的变量跟less十分的相似,只是变量名前面使用$开头

$red: #c00;

strong {
  color: $red;
}
stylus`声明的变量没有任何的限定,可以使用`$`开头,结尾的分号`;`可有可无,但变量与变量值之间需要使用`=

stylus中我们不建议使用@符号开头声明变量

red = #c00

strong
  color: red
作用域

Css 预编译器把变量赋予作用域,也就是存在生命周期。就像 js 一样,它会先从局部作用域查找变量,依次向上级作用域查找

sass中不存在全局变量

$color: black;
.scoped {
  $bg: blue;
  $color: white;
  color: $color;
  background-color:$bg;
}
.unscoped {
  color:$color;
} 

编译后

.scoped {
  color:white;/*是白色*/
  background-color:blue;
}
.unscoped {
  color:white;/*白色(无全局变量概念)*/
} 

所以,在sass中最好不要定义相同的变量名

lessstylus的作用域跟javascript十分的相似,首先会查找局部定义的变量,如果没有找到,会像冒泡一样,一级一级往下查找,直到根为止

@color: black;
.scoped {
  @bg: blue;
  @color: white;
  color: @color;
  background-color:@bg;
}
.unscoped {
  color:@color;
} 

编译后:

.scoped {
  color:white;/*白色(调用了局部变量)*/
  background-color:blue;
}
.unscoped {
  color:black;/*黑色(调用了全局变量)*/
} 
混入

混入(mixin)应该说是预处理器最精髓的功能之一了,简单点来说,Mixins可以将一部分样式抽出,作为单独定义的模块,被很多选择器重复使用

可以在Mixins中定义变量或者默认参数

less中,混合的用法是指将定义好的ClassA中引入另一个已经定义的Class,也能使用够传递参数,参数变量为@声明

.alert {
  font-weight: 700;
}

.highlight(@color: red) {
  font-size: 1.2em;
  color: @color;
}

.heads-up {
  .alert;
  .highlight(red);
}

编译后

.alert {
  font-weight: 700;
}
.heads-up {
  font-weight: 700;
  font-size: 1.2em;
  color: red;
}

Sass声明mixins时需要使用@mixinn,后面紧跟mixin的名,也可以设置参数,参数名为变量$声明的形式

@mixin large-text {
  font: {
    family: Arial;
    size: 20px;
    weight: bold;
  }
  color: #ff0000;
}

.page-title {
  @include large-text;
  padding: 4px;
  margin-top: 10px;
}

stylus中的混合和前两款Css预处理器语言的混合略有不同,他可以不使用任何符号,就是直接声明Mixins名,然后在定义参数和默认值之间用等号(=)来连接

error(borderWidth= 2px) {
  border: borderWidth solid #F00;
  color: #F00;
}
.generic-error {
  padding: 20px;
  margin: 4px;
  error(); /* 调用error mixins */
}
.login-error {
  left: 12px;
  position: absolute;
  top: 20px;
  error(5px); /* 调用error mixins,并将参数$borderWidth的值指定为5px */
} 
代码模块化

模块化就是将Css代码分成一个个模块

scsslessstylus三者的使用方法都如下所示

@import './common';
@import './github-markdown';
@import './mixin';
@import './variables';

2.14 仅使用 CSS 怎么实现宽高自适应的正方形?

在电商、个人博客等网站我们或多或少都可以看到css正方形的应用场景,自适应正方形布局是必须要掌握的。

正方形

这很简单,设置盒子宽高都为200px就可以了

<div class="box">小明写的</div>

.box {
    width: 200px;
    height: 200px;
    background: pink;
}

自适应正方形

青铜

提起自适应,想必都会想到remvw等相对单位,我们分别用这些单位来实现一下。

rem
<div class="rem">rem</div>

body {
    font-size: 16px;
}

.rem {
    width: 10rem;
    height: 10rem;
    background: pink;
}

@media only screen and (max-width: 1200px) {
    body {
        font-size: 12px;
    }
}

img

屏幕宽度大于1200px时,字体为16px,rem的盒子的大小为 160px * 160px;

屏幕宽度小于1200px时,字体为12px,rem的盒子的大小为 120px * 120px;

实际应用场景屏幕宽度变化会动态计算rem的值,这里只用于案例理解。

优点

在适配了rem的项目中,可以直接使用rem设置盒子宽高,即自适应正方形

缺点

在未设置rem的项目中,单独为了实现自适应正方形有点大材小用

vw

<div class="vw">vw</div>

.vw {
    width: 10vw;
    height: 10vw;
    background: yellow;
}

img

可以看到正方形跟随着屏幕宽度的变化自适应,这是我们想要的效果

优点

vw为内置视口单位,可直接使用。

缺点

在实际的业务场景中,将设计稿的尺寸转化为vw相对麻烦

黄金

使用 百分比+padding,这里有一个很细的知识点;当padding、margin取值为百分比时,百分比的值是以父元素的width为参考

<div class="padding"></div>

.padding {
    width: 20%;
    padding-top: 20%;
    /* padding-top或padding-bottom都可以 */
    background: #696969;
}

我们给盒子的宽度设置20%,使用的padding的纵轴将盒子高度撑开,这样便得到了一个自适应正方形。

img

细心的小伙伴会发现,现在是个自适应正方形没错了,但是高度被占满了,内容没地方放了。 是的,当前只是一个正方形;在实际业务场景中,内部不是图片就是其他内容,不止需要一个正方形的。

解决办法:再嵌套一层内容盒子,外层方形盒为相对定位,内层内容盒为绝对定位;内层盒宽高基于外层方形盒,代码如下

<div class="box-wrap">
    <div class="box-content">我是内容区域</div>
</div>

.box-wrap {
    position: relative;
    width: 20%;
    padding-top: 20%;
}

.box-content {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: 1;
    background: burlywood;
}

.box-content类为内容盒,我们可以在内容盒里为所欲为还原设计稿了。

若方形区域只需要展示图片,同理如下

<div class="box-wrap">
    <img src="./img/A.webp" alt="">
</div>

.box-wrap {
    position: relative;
    width: 20%;
    padding-top: 20%;
}

.box-wrap > img {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: 1;
    object-fit: cover;
}

img

这里我们就实现了一个自适应方形图片了。

优点

无需其他配置,设置灵活,扩展性强。

缺点

需要额外嵌套一层内容盒(个人拙见)

钻石

顾名思义,它很腻害;他就是css属性aspect-ratio

aspect-ratio MDN文档

aspect-ratio,简言之就是宽高比。多说无益,上代码

<div class="box-square">我是内容</div>

.box-square {
    aspect-ratio: 1 / 1;
    /* aspect-ratio: 1; 可简写 */
    width: 20%;
    background: chocolate;
}

img

预览

一个属性,自适应正方形就OK啦;属性再好,也得看兼容性和浏览器支持。

img

优点

属性通俗易懂,无需其他配置,无需嵌套内容盒。

缺点

唯一缺点就是兼容性了,2023年了该支持的基本都支持上了,此事古难全!

活学活用

至此,知识点应该都掌握了吧;让我们来实现一个真实的业务需求,如图

img

实现这个布局,假设总宽1000px,商品卡片左右间距12px,布局要求自适应+响应式。

百分比+padding实现

<main>
    <div class="shop">
        <div class="shop-img"><img src="./img/A.webp" alt=""></div>
        <div class="shop-title">这是小黎测试的文字区域,实际运用一下百分比+padding实现自适应方形布局。</div>
    </div>
    <div class="shop">
        <div class="shop-img"><img src="./img/A.webp" alt=""></div>
        <div class="shop-title">这是小黎测试的文字区域,实际运用一下百分比+padding实现自适应方形布局。</div>
    </div>
   <div class="shop">
        <div class="shop-img"><img src="./img/A.webp" alt=""></div>
        <div class="shop-title">这是小黎测试的文字区域,实际运用一下百分比+padding实现自适应方形布局。</div>
    </div>
    <div class="shop">
        <div class="shop-img"><img src="./img/A.webp" alt=""></div>
        <div class="shop-title">这是小黎测试的文字区域,实际运用一下百分比+padding实现自适应方形布局。</div>
    </div>
</main>
main {
    max-width: 1000px;
    margin: 0 auto;
    padding: 0 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;

    .shop {
        width: calc(25% - 9px);
        box-shadow: 0 0 8px #0002;
        border-radius: 4px;
       overflow: hidden;

        .shop-img {
            position: relative;
            width: 100%;
           padding-top: 100%;

            > img {
                position: absolute;
                width: 100%;
                height: 100%;
                top: 0;
                left: 0;
                z-index: 1;
                object-fit: cover;
            }
        }

        .shop-title {
            padding: 8px;
            font-size: 14px;
            color: #232323;
        }
    }
}

@media only screen and (max-width: 768px) {
    main {

        .shop {
            width: calc(50% - 6px);
            margin-bottom: 12px;
        }
    }
}

来看看效果

img

aspect-ratio实现

使用aspect-ratio了属性,我们可以少嵌套一个外层方形盒

<main>
    <div class="shop">
        <img src="./img/A.webp" alt="">
        <div class="shop-title">这是小黎测试的文字区域,实际运用aspect-ratio实现自适应方形布局。</div>
    </div>
    <div class="shop">
        <img src="./img/A.webp" alt="">
        <div class="shop-title">这是小黎测试的文字区域,实际运用aspect-ratio实现自适应方形布局。</div>
    </div>
    <div class="shop">
        <img src="./img/A.webp" alt="">
        <div class="shop-title">这是小黎测试的文字区域,实际运用aspect-ratio实现自适应方形布局。</div>
    </div>
    <div class="shop">
        <img src="./img/A.webp" alt="">
        <div class="shop-title">这是小黎测试的文字区域,实际运用aspect-ratio实现自适应方形布局。</div>
    </div>
</main>

样式基本不变,改一下图片相关的部分。

main {
    max-width: 1000px;
    margin: 0 auto;
    padding: 0 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;

   .shop {
        width: calc(25% - 9px);
        box-shadow: 0 0 8px #0002;
        border-radius: 4px;
        overflow: hidden;

        > img {
            width: 100%;
            aspect-ratio: 1;
            object-fit: cover;
        }

        .shop-title {
            padding: 8px;
            font-size: 14px;
            color: #232323;
        }
    }
}

@media only screen and (max-width: 768px) {
    main {
        .shop {
            width: calc(50% - 6px);
            margin-bottom: 12px;
        }
    }
}

来看看效果

img

扩展

在实际业务中,应用到的布局不止正方形,也有长方形、圆形等。

我们再来看个需求——UI规定图片区域宽高比为4:3,给的图片也是4:3。

一般情况我们的方案是:设置好图片宽度,高度自适应就OK了。这样做相对简便,但也会存在一些问题;比如图片宽高比并不是4:3,或多或少,又或者传的图不规整;这样会导致我们的页面布局不可控,所谓失之毫厘谬以千里。

这时候就牵扯到了代码健壮性和可扩展性,作为前端开发个人觉得页面应该有良好的用户体验,而不是调教用户。接下来我分别用百分比+padding、aspect-ratio等方案来实现该业务场景。

借用方形示例,这里我就只贴关键代码

百分比+padding实现

main {
    .shop {
        .shop-img {
            width: 100%;
            padding-top: 75%;
        }
    }
}

aspect-ratio实现

main {
    .shop {
        > img {
            width: 100%;
            aspect-ratio: 4 / 3;
            object-fit: cover;
        }
    }
}

看下效果

img

2.15 怎么理解回流跟重绘?什么场景下会触发?

一、是什么

HTML中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及到回流与重绘:

  • 回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置
  • 重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制

具体的浏览器解析渲染机制如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dfKKrtqV-1683893248605)(null)]

  • 解析HTML,生成DOM树,解析CSS,生成CSSOM树
  • 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
  • Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
  • Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  • Display:将像素发送给GPU,展示在页面上

在页面初始渲染阶段,回流不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局发生改变

当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来

当我们对 DOM 的修改导致了样式的变化(colorbackground-color),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了重绘

二、如何触发

要想减少回流和重绘的次数,首先要了解回流和重绘是如何触发的

回流触发时机

回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流,如下面情况:

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
  • 页面一开始渲染的时候(这避免不了)
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

还有一些容易被忽略的操作:获取一些特定属性的值

offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight

这些属性有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流

除此还包括getComputedStyle 方法,原理是一样的

重绘触发时机

触发回流一定会触发重绘

可以把页面理解为一个黑板,黑板上有一朵画好的小花。现在我们要把这朵从左边移到了右边,那我们要先确定好右边的具体位置,画好形状(回流),再画上它原有的颜色(重绘)

除此之外还有一些其他引起重绘行为:

  • 颜色的修改
  • 文本方向的修改
  • 阴影的修改

浏览器优化机制

由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列

当你获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的offsetTop等方法都会返回最新的数据

因此浏览器不得不清空队列,触发回流重绘来返回正确的值

三、如何减少

我们了解了如何触发回流和重绘的场景,下面给出避免回流的经验:

  • 如果想设定元素的样式,通过改变元素的 class 类名 (尽可能在 DOM 树的最里层)
  • 避免设置多项内联样式
  • 应用元素的动画,使用 position 属性的 fixed 值或 absolute 值(如前文示例所提)
  • 避免使用 table 布局,table 中每个元素的大小以及内容的改动,都会导致整个 table 的重新计算
  • 对于那些复杂的动画,对其设置 position: fixed/absolute,尽可能地使元素脱离文档流,从而减少对其他元素的影响
  • 使用css3硬件加速,可以让transformopacityfilters这些动画不会引起回流重绘
  • 避免使用 CSS 的 JavaScript 表达式

在使用 JavaScript 动态插入多个节点时, 可以使用DocumentFragment. 创建后一次插入. 就能避免多次的渲染性能

但有时候,我们会无可避免地进行回流或者重绘,我们可以更好使用它们

例如,多次修改一个把元素布局的时候,我们很可能会如下操作

const el = document.getElementById('el')
for(let i=0;i<10;i++) {
    el.style.top  = el.offsetTop  + 10 + "px";
    el.style.left = el.offsetLeft + 10 + "px";
}

每次循环都需要获取多次offset属性,比较糟糕,可以使用变量的形式缓存起来,待计算完毕再提交给浏览器发出重计算请求

// 缓存offsetLeft与offsetTop的值
const el = document.getElementById('el') 
let offLeft = el.offsetLeft, offTop = el.offsetTop

// 在JS层面进行计算
for(let i=0;i<10;i++) {
  offLeft += 10
  offTop  += 10
}

// 一次性将计算结果应用到DOM上
el.style.left = offLeft + "px"
el.style.top = offTop  + "px"

我们还可避免改变样式,使用类名去合并样式

const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'

使用类名去合并样式

<style>
    .basic_style {
        width: 100px;
        height: 200px;
        border: 10px solid red;
        color: red;
    }
</style>
<script>
    const container = document.getElementById('container')
    container.classList.add('basic_style')
</script>

前者每次单独操作,都去触发一次渲染树更改(新浏览器不会),

都去触发一次渲染树更改,从而导致相应的回流与重绘过程

合并之后,等于我们将所有的更改一次性发出

我们还可以通过通过设置元素属性display: none,将其从页面上去掉,然后再进行后续操作,这些后续操作也不会触发回流与重绘,这个过程称为离线操作

const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'

离线操作后

let container = document.getElementById('container')
container.style.display = 'none'
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
...(省略了许多类似的后续操作)
container.style.display = 'block'

2.16 z-index属性在什么情况下会失效?

通常 z-index 的使用是在有两个重叠的标签,在一定的情况下控制其中一个在另一个的上方或者下方出现。z-index值越大就越是在上层。z-index元素的position属性需要是relative,absolute或是fixed。

z-index属性在下列情况下会失效:

  • 父元素position为relative时,子元素的z-index失效。解决:父元素position改为absolute或static;
  • 元素没有设置position属性为非static属性。解决:设置该元素的position属性为relative,absolute或是fixed中的一种;
  • 元素在设置z-index的同时还设置了float浮动。解决:float去除,改为display:inline-block

20230116,有小伙伴补充:

  • 在手机端 iOS 13 系统中,-webkit-overflow-scrolling:touch 也会使 z-index 失效,将 touch 换成 unset

具体原因可参考这篇文章: 为什么我的 z-index 又不生效了?

2.17 使用原生js实现以下效果:点击容器内的图标,图标边框变成border:1px solid red,点击空白处重置


const box = document.getElementById('box');

function isIcon(target) {
 return target.className.includes('icon');
}

box.onclick = function(e) {
 e.stopPropagation();
 const target = e.target;
 if (isIcon(target)) {
   target.style.border = '1px solid red';
 }
}

const doc = document;

doc.onclick = function(e) {
 const children = box.children;
 for(let i = 0; i < children.length; i++) {
   if (isIcon(children[i])) {
     children[i].style.border = 'none';
   }
 }
}

2.18 position: fixed 一定是相对于浏览器窗口进行定位吗?

不一定。

position:fixed;的元素会被移出正常文档流,并不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置,元素的位置在屏幕滚动时不会改变。fixed 属性会创建新的层叠上下文。

当元素祖先的 transform, perspectivefilter 属性非 none 时,容器由视口改为该祖先。

2.19 css选择器有哪些?优先级分别是什么?哪些属性可以继承?

一、选择器

CSS选择器是CSS规则的第一部分

它是元素和其他部分组合起来告诉浏览器哪个HTML元素应当是被选为应用规则中的CSS属性值的方式

选择器所选择的元素,叫做“选择器的对象”

我们从一个Html结构开始

<div id="box">
	<div class="one">
	    <p class="one_1"></p>
	    <p class="one_1"></p>
	</div>
	<div class="two"></div>
	<div class="two"></div>
	<div class="two"></div>
</div>

关于css属性选择器常用的有:

1- id选择器(#box),选择id为box的元素
2- 类选择器(.one),选择类名为one的所有元素
3- 标签选择器(div),选择标签为div的所有元素
4- 后代选择器(#box div),选择id为box元素内部所有的div元素
5- 子选择器(.one>one_1),选择父元素为.one的所有.one_1的元素
6- 相邻同胞选择器(.one+.two),选择紧接在.one之后的所有.two元素
7- 群组选择器(div,p),选择div、p的所有元素

还有一些使用频率相对没那么多的选择器:

  • 伪类选择器
1:link :选择未被访问的链接
2:visited:选取已被访问的链接
3:active:选择活动链接
4:hover :鼠标指针浮动在上面的元素
5:focus :选择具有焦点的
6:first-child:父元素的首个子元素
  • 伪元素选择器
1:first-letter :用于选取指定选择器的首字母
2:first-line :选取指定选择器的首行
3:before : 选择器在被选元素的内容前面插入内容
4:after : 选择器在被选元素的内容后面插入内容
  • 属性选择器
1[attribute] 选择带有attribute属性的元素
2[attribute=value] 选择所有使用attribute=value的元素
3[attribute~=value] 选择attribute属性包含value的元素
4[attribute|=value]:选择attribute属性以value开头的元素

CSS3中新增的选择器有如下:

  • 层次选择器(p~ul),选择前面有p元素的每个ul元素
  • 伪类选择器
1:first-of-type 父元素的首个元素
2:last-of-type 父元素的最后一个元素
3:only-of-type 父元素的特定类型的唯一子元素
4:only-child 父元素中唯一子元素
5:nth-child(n) 选择父元素中第N个子元素
6:nth-last-of-type(n) 选择父元素中第N个子元素,从后往前
7:last-child 父元素的最后一个元素
8:root 设置HTML文档
9:empty 指定空的元素
10:enabled 选择被禁用元素
11:disabled 选择被禁用元素
12:checked 选择选中的元素
13:not(selector) 选择非 <selector> 元素的所有元素
  • 属性选择器
[attribute*=value]:选择attribute属性值包含value的所有元素
[attribute^=value]:选择attribute属性开头为value的所有元素
[attribute$=value]:选择attribute属性结尾为value的所有元素

二、优先级

相信大家对CSS选择器的优先级都不陌生:

内联 > ID选择器 > 类选择器 > 标签选择器

到具体的计算层⾯,优先级是由 A 、B、C、D 的值来决定的,其中它们的值计算规则如下:

  • 如果存在内联样式,那么 A = 1, 否则 A = 0
  • B的值等于 ID选择器出现的次数
  • C的值等于 类选择器 和 属性选择器 和 伪类 出现的总次数
  • D 的值等于 标签选择器 和 伪元素 出现的总次数

这里举个例子:

1#nav-global > ul > li > a.nav-link

套用上面的算法,依次求出 A B C D 的值:

  • 因为没有内联样式 ,所以 A = 0
  • ID选择器总共出现了1次, B = 1
  • 类选择器出现了1次, 属性选择器出现了0次,伪类选择器出现0次,所以 C = (1 + 0 + 0) = 1
  • 标签选择器出现了3次, 伪元素出现了0次,所以 D = (3 + 0) = 3

上面算出的ABCD 可以简记作:(0, 1, 1, 3)

知道了优先级是如何计算之后,就来看看比较规则:

  • 从左往右依次进行比较 ,较大者优先级更高
  • 如果相等,则继续往右移动一位进行比较
  • 如果4位全部相等,则后面的会覆盖前面的

经过上面的优先级计算规则,我们知道内联样式的优先级最高,如果外部样式需要覆盖内联样式,就需要使用!important

三、继承属性

css中,继承是指的是给父元素设置一些属性,后代元素会自动拥有这些属性 关于继承属性,可以分成:

  • 字体系列属性
1font:组合字体
2font-family:规定元素的字体系列
3font-weight:设置字体的粗细
4font-size:设置字体的尺寸
5font-style:定义字体的风格
6font-variant:偏大或偏小的字体
  • 文本系列属性
1text-indent:文本缩进
2text-align:文本水平对齐
3line-height:行高
4word-spacing:增加或减少单词间的空白
5letter-spacing:增加或减少字符间的空白
6text-transform:控制文本大小写
7direction:规定文本的书写方向
8color:文本颜色
  • 元素可见性
1visibility
  • 表格布局属性
1caption-side:定位表格标题位置
2border-collapse:合并表格边框
3border-spacing:设置相邻单元格的边框间的距离
4empty-cells:单元格的边框的出现与消失
5table-layout:表格的宽度由什么决定
  • 列表属性
1list-style-type:文字前面的小点点样式
2list-style-position:小点点位置
3list-style:以上的属性可通过这属性集合
  • 引用
1quotes:设置嵌套引用的引号类型
  • 光标属性
1cursor:箭头可以变成需要的形状

继承中比较特殊的几点:

  • a 标签的字体颜色不能被继承
  • h1-h6标签字体的大下也是不能被继承的

无继承的属性

  • display
  • 文本属性:vertical-align、text-decoration
  • 盒子模型的属性:宽度、高度、内外边距、边框等
  • 背景属性:背景图片、颜色、位置等
  • 定位属性:浮动、清除浮动、定位position等
  • 生成内容属性:content、counter-reset、counter-increment
  • 轮廓样式属性:outline-style、outline-width、outline-color、outline
  • 页面样式属性:size、page-break-before、page-break-after

2.20 怎么触发BFC,BFC有什么应用场景?

文档流

在介绍BFC之前,需要先给大家介绍一下文档流。

我们常说的文档流其实分为定位流浮动流普通流三种。

绝对定位(Absolute positioning)

如果元素的属性 positionabsolutefixed,它就是一个绝对定位元素。

在绝对定位布局中,元素会整体脱离普通流,因此绝对定位元素不会对其兄弟元素造成影响,而元素具体的位置由绝对定位的坐标决定。

它的定位相对于它的包含块,相关CSS属性:topbottomleftright

对于 position: absolute,元素定位将相对于上级元素中最近的一个relative、fixed、absolute,如果没有则相对于body;

对于 position:fixed,正常来说是相对于浏览器窗口定位的,但是当元素祖先的 transform 属性非 none 时,会相对于该祖先进行定位

浮动 (float)

在浮动布局中,元素首先按照普通流的位置出现,然后根据浮动的方向尽可能的向左边或右边偏移,其效果与印刷排版中的文本环绕相似。

普通流 (normal flow)

普通流其实就是指BFC中的FC。FC(Formatting Context),直译过来是格式化上下文,它是页面中的一块渲染区域,有一套渲染规则,决定了其子元素如何布局,以及和其他元素之间的关系和作用。

在普通流中,元素按照其在 HTML 中的先后位置至上而下布局,在这个过程中,行内元素水平排列,直到当行被占满然后换行。块级元素则会被渲染为完整的一个新行。

除非另外指定,否则所有元素默认都是普通流定位,也可以说,普通流中元素的位置由该元素在 HTML 文档中的位置决定。

BFC 概念

先看下MDN上关于BFC的定义:

块格式化上下文(Block Formatting ContextBFC) 是Web页面的可视CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。

具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。

通俗一点来讲,可以把 BFC 理解为一个封闭的大箱子,箱子内部的元素无论如何翻江倒海,都不会影响到外部。

除了 BFC,还有:

  • IFC(行级格式化上下文)- inline 内联
  • GFC(网格布局格式化上下文)- display: grid
  • FFC(自适应格式化上下文)- display: flexdisplay: inline-flex

注意:同一个元素不能同时存在于两个 BFC 中。

BFC的触发方式

MDN上对于BFC的触发条件写的很多,总结一下常见的触发方式有(只需要满足一个条件即可触发 BFC 的特性):

  • 根元素,即 <html>
  • 浮动元素:float 值为 leftright
  • overflow 值不为 visible,即为 autoscrollhidden
  • display 值为 inline-blocktable-celltable-captiontableinline-tableflexinline-flexgridinline-grid
  • 绝对定位元素:position 值为 absolutefixed

BFC的特性

  • BFC 是页面上的一个独立容器,容器里面的子元素不会影响外面的元素。
  • BFC 内部的块级盒会在垂直方向上一个接一个排列
  • 同一 BFC 下的相邻块级元素可能发生外边距折叠,创建新的 BFC 可以避免外边距折叠
  • 每个元素的外边距盒(margin box)的左边与包含块边框盒(border box)的左边相接触(从右向左的格式的话,则相反),即使存在浮动
  • 浮动盒的区域不会和 BFC 重叠
  • 计算 BFC 的高度时,浮动元素也会参与计算

应用

BFC是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面元素,反之亦然。我们可以利用BFC的这个特性来做很多事。

自适应两列布局

左列浮动(定宽或不定宽都可以),给右列开启 BFC。

<div>
    <div class="left">浮动元素,无固定宽度</div>
    <div class="right">自适应</div>
</div>
* {
    margin: 0;
    padding: 0;
}
.left {
    float: left;
    height: 200px;
    margin-right: 10px;
    background-color: red;
}
.right {
    overflow: hidden;
    height: 200px;
    background-color: yellow;
}

效果:

img

  • 将左列设为左浮动,将自身高度塌陷,使得其它块级元素可以和它占据同一行的位置。
  • 右列为 div 块级元素,利用其自身的流特性占满整行。
  • 右列设置overflow: hidden,触发 BFC 特性,使其自身与左列的浮动元素隔离开,不占满整行。

这即是上面说的 BFC 的特性之一:浮动盒的区域不会和 BFC 重叠

防止外边距(margin)重叠

兄弟元素之间的外边距重叠

<div>
    <div class="child1"></div>
    <div class="child2"></div>
</div>
* {
    margin: 0;
    padding: 0;
}
.child1 {
    width: 100px;
    height: 100px;
    margin-bottom: 10px;
    background-color: red;
}
.child2 {
    width: 100px;
    height: 100px;
    margin-top: 20px;
    background-color: green;
}

效果:

img

两个块级元素,红色 div 距离底部 10px,绿色 div 距离顶部 20px,按道理应该两个块级元素相距 30px 才对,但实际却是取距离较大的一个,即 20px。

块级元素的上外边距和下外边距有时会合并(或折叠)为一个外边距,其大小取其中的较大者,这种行为称为外边距折叠(重叠),注意这个是发生在属于同一 BFC 下的块级元素之间

根据 BFC 特性,创建一个新的 BFC 就不会发生 margin 折叠了。比如我们在他们两个 div 外层再包裹一层容器,加属性 overflow: hidden,触发 BFC,那么两个 div 就不属于同个 BFC 了。

<div>
    <div class="parent">
        <div class="child1"></div>
    </div>
    <div class="parent">
        <div class="child2"></div>
    </div>
</div>
.parent {
    overflow: hidden;
}

/* ... */

img

这个关于兄弟元素外边距叠加的问题,除了触发 BFC 也有其他方案,比如你统一只用上边距或下边距,就不会有上面的问题。

父子元素的外边距重叠

这种情况存在父元素与其第一个或最后一个子元素之间(嵌套元素)。

如果在父元素与其第一个/最后一个子元素之间不存在边框、内边距、行内内容,也没有创建块格式化上下文、或者清除浮动将两者的外边距 分开,此时子元素的外边距会“溢出”到父元素的外面。

<div id="parent">
  <div id="child"></div>
</div>
* {
    margin: 0;
    padding: 0;
}
#parent {
    width: 200px;
    height: 200px;
    background-color: green;
    margin-top: 20px;
}
#child {
    width: 100px;
    height: 100px;
    background-color: red;
    margin-top: 30px;
}

img

如上图,红色的 div 在绿色的 div 内部,且设置了 margin-top 为 30px,但我们发现红色 div 的顶部与绿色 div 顶部重合,并没有距离顶部 30px,而是溢出到父元素的外面计算。即本来父元素距离顶部只有 20px,被子元素溢出影响,外边距重叠,取较大的值,则距离顶部 30px。

解决办法:

  • 给父元素触发 BFC(如添加overflow: hidden)
  • 给父元素添加 border
  • 给父元素添加 padding

这样就能实现我们期望的效果了:

img

清除浮动解决令父元素高度坍塌的问题

当容器内子元素设置浮动时,脱离了文档流,容器中总父元素高度只有边框部分高度。

<div class="parent">
  <div class="child"></div>
</div>
* {
    margin: 0;
    padding: 0;
}
.parent {
    border: 4px solid red;
}
.child {
    float: left;
    width: 200px;
    height: 200px;
    background-color: blue;
}

img

解决办法:给父元素触发 BFC,使其有 BFC 特性:计算 BFC 的高度时,浮动元素也会参与计算

.parent {
    overflow: hidden;
    border: 4px solid red;
}

img

上面我们都是用的 overflow: hidden 触发 BFC,因为确实常用,但是触发 BFC 也不止是只有这一种方法。

如上面写的所示,可以设置float: left;float: right;display: inline-block;overflow: auto;display: flex;display: table;positionabsolutefixed 等等,这些都可以触发,不过父元素宽度表现不一定相同,但父元素高度都被撑出来了。

当然实际运用可不是随便挑一个走,还是根据场景选择。

2.21 说说对 CSS 工程化的理解

CSS 工程化是为了解决以下问题:

  1. 宏观设计:CSS 代码如何组织、如何拆分、模块结构怎样设计?
  2. 编码优化:怎样写出更好的 CSS?
  3. 构建:如何处理我的 CSS,才能让它的打包结果最优?
  4. 可维护性:代码写完了,如何最小化它后续的变更成本?如何确保任何一个同事都能轻松接手?

以下三个方向都是时下比较流行的、普适性非常好的 CSS 工程化实践:

  • 预处理器:Less、 Sass 等;
  • 重要的工程化插件: PostCss;
  • Webpack loader 等 。

基于这三个方向,可以衍生出一些具有典型意义的子问题,这里我们逐个来看:

(1)预处理器:为什么要用预处理器?它的出现是为了解决什么问题?

预处理器,其实就是 CSS 世界的“轮子”。预处理器支持我们写一种类似 CSS、但实际并不是 CSS 的语言,然后把它编译成 CSS 代码:

img

那为什么写 CSS 代码写得好好的,偏偏要转去写“类 CSS”呢?这就和本来用 JS 也可以实现所有功能,但最后却写 React 的 jsx 或者 Vue 的模板语法一样。

随着前端业务复杂度的提高,前端工程中对 CSS 提出了以下的诉求:

  1. 宏观设计上:我们希望能优化 CSS 文件的目录结构,对现有的 CSS 文件实现复用;
  2. 编码优化上:我们希望能写出结构清晰、简明易懂的 CSS,需要它具有一目了然的嵌套层级关系,而不是无差别的一铺到底写法;我们希望它具有变量特征、计算能力、循环能力等等更强的可编程性,这样我们可以少写一些无用的代码;
  3. 可维护性上:更强的可编程性意味着更优质的代码结构,实现复用意味着更简单的目录结构和更强的拓展能力,这两点如果能做到,自然会带来更强的可维护性。

这三点是传统 CSS 所做不到的,也正是预处理器所解决掉的问题。预处理器普遍会具备这样的特性:

  • 嵌套代码的能力,通过嵌套来反映不同 css 属性之间的层级关系 ;
  • 支持定义 css 变量;
  • 提供计算函数;
  • 允许对代码片段进行 extend 和 mixin;
  • 支持循环语句的使用;
  • 支持将 CSS 文件模块化,实现复用。

(2)PostCss:PostCss 是如何工作的?我们在什么场景下会使用 PostCss?

img

它和预处理器的不同就在于,预处理器处理的是 类CSS,而 PostCss 处理的就是 CSS 本身。Babel 可以将高版本的 JS 代码转换为低版本的 JS 代码。PostCss 做的是类似的事情:它可以编译尚未被浏览器广泛支持的先进的 CSS 语法,还可以自动为一些需要额外兼容的语法增加前缀。更强的是,由于 PostCss 有着强大的插件机制,支持各种各样的扩展,极大地强化了 CSS 的能力。

PostCss 在业务中的使用场景非常多:

  • 提高 CSS 代码的可读性:PostCss 其实可以做类似预处理器能做的工作;
  • 当我们的 CSS 代码需要适配低版本浏览器时,PostCss 的 Autoprefixer 插件可以帮助我们自动增加浏览器前缀;
  • 允许我们编写面向未来的 CSS:PostCss 能够帮助我们编译 CSS next 代码;

(3)Webpack 能处理 CSS 吗?如何实现?

  • Webpack 在裸奔的状态下,是不能处理 CSS 的,Webpack 本身是一个面向 JavaScript 且只能处理 JavaScript 代码的模块化打包工具;
  • Webpack 在 loader 的辅助下,是可以处理 CSS 的。

如何用 Webpack 实现对 CSS 的处理:

  • Webpack 中操作 CSS 需要使用的两个关键的 loader:css-loader 和 style-loader
  • 注意,答出“用什么”有时候可能还不够,面试官会怀疑你是不是在背答案,所以你还需要了解每个 loader 都做了什么事情:
    • css-loader:导入 CSS 模块,对 CSS 代码进行编译处理;
    • style-loader:创建style标签,把 CSS 内容写入标签。

在实际使用中,css-loader 的执行顺序一定要安排在 style-loader 的前面。因为只有完成了编译过程,才可以对 css 代码进行插入;若提前插入了未编译的代码,那么 webpack 是无法理解这坨东西的,它会无情报错。

2.22 为什么有时候⽤translate来改变位置⽽不是使用position进行定位?

translate 是 transform 属性的⼀个值。

改变transform或opacity不会触发浏览器重新布局(reflow)或重绘(repaint),只会触发复合(compositions)。

⽽改变绝对定位会触发重新布局,进⽽触发重绘和复合。

transform使浏览器为元素创建⼀个 GPU 图层,但改变绝对定位会使⽤到 CPU。

因此translate()更⾼效,可以缩短平滑动画的绘制时间。

⽽translate改变位置时,元素依然会占据其原始空间,绝对定位就不会发⽣这种情况。

具体的原理可查看 【前端基础系列】CSS篇-带你搞懂“硬件加速”

2.23 硬件加速的原理是什么?

面试中可能会经常会碰到怎么解决动画卡顿的问题,然后会引导到硬件加速。那么究竟什么是硬件加速,为什么它可以提高咱们的动画效率?我们今天就来一探究竟。

首先,我们先从 CPU 和 GPU 开始了解。

CPU 和 GPU 的区别

CPU 即中央处理器,GPU 即图形处理器。

CPU是计算机的大脑,它提供了一套指令集,我们写的程序最终会通过 CPU 指令来控制的计算机的运行。它会对指令进行译码,然后通过逻辑电路执行该指令。整个执行的流程分为了多个阶段,叫做流水线。指令流水线包括取指令、译码、执行、取数、写回五步,这是一个指令周期。CPU会不断的执行指令周期来完成各种任务。

GPU,是Graphics ProcessingUnit的简写,是现代显卡中非常重要的一个部分,其地位与CPU在主板上的地位一致,主要负责的任务是加速图形处理速度。GPU是显卡的“大脑”,它决定了该显卡的档次和大部分性能,同时也是2D显示卡和3D显示卡的区别依据。2D显示芯片在处理3D图像和特效时主要依赖CPU的处理能力,称为“软加速”。3D显示芯片是将三维图像和特效处理功能集中在显示芯片内,也即所谓的“硬件加速”功能。

要解释两者的区别,要先明白两者的相同之处:两者都有总线和外界联系,有自己的缓存体系,以及数字和逻辑运算单元。

一句话,两者都为了完成计算任务而设计。

两者的区别在于存在于片内的缓存体系和数字逻辑运算单元的结构差异:

  • CPU虽然有多核,但总数没有超过两位数,每个核都有足够大的缓存和足够多的数字和逻辑运算单元,并辅助有很多加速分支判断甚至更复杂的逻辑判断的硬件;
  • GPU 的核数远超CPU,被称为众核(NVIDIA Fermi有512个核)。每个核拥有的缓存大小相对小,数字逻辑运算单元也少而简单(GPU初始时在浮点计算上一直弱于CPU)。

从结果上导致CPU擅长处理具有复杂计算步骤和复杂数据依赖的计算任务,如分布式计算,数据压缩,人工智能,物理模拟,以及其他很多很多计算任务等。

GPU由于历史原因,是为了视频游戏而产生的(至今其主要驱动力还是不断增长的视频游戏市场),在三维游戏中常常出现的一类操作是对海量数据进行相同的操作,如:对每一个顶点进行同样的坐标变换,对每一个顶点按照同样的光照模型计算颜色值。

GPU的众核架构非常适合把同样的指令流并行发送到众核上,采用不同的输入数据执行。在通用计算领域有广泛应用,包括:数值分析,海量数据处理(排序,Map-Reduce等),金融分析等等。

简而言之,当程序员为CPU编写程序时,他们倾向于利用复杂的逻辑结构优化算法从而减少计算任务的运行时间,即 Latency。当程序员为GPU编写程序时,则利用其处理海量数据的优势,通过提高总的数据吞吐量(Throughput)来掩盖 Lantency

目前,CPUGPU 的区别正在逐渐缩小,因为GPU也在处理不规则任务和线程间通信方面有了长足的进步。

每一帧的执行步骤

一般浏览器的刷新率为60HZ,即1秒钟刷新60次。

1000ms / 60hz = 16.6 ,也就是大概每过 16.6ms 浏览器就会渲染一帧画面。

浏览器对每一帧画面的渲染工作都要在 16ms 内完成,超出这个时间,页面的渲染就会出现卡顿现象,影响用户体验。

简单概括下,浏览器在每一帧里会依次执行以下这些动作:

  • JavaScript:JavaScript 实现动画效果,DOM 元素操作等。
  • Style(计算样式):确定每个 DOM 元素应该应用什么 CSS 规则。
  • Layout(布局):计算每个 DOM 元素在最终屏幕上显示的大小和位置。由于 web 页面的元素布局是相对的,所以其中任意一个元素的位置发生变化,都会联动的引起其他元素发生变化,这个过程叫 reflow。
  • Paint(绘制):在多个层上绘制 DOM 元素的的文字、颜色、图像、边框和阴影等。
  • Composite(渲染层合并):按照合理的顺序合并图层然后显示到屏幕上。

减少或者避免 layoutpaint 可以让页面减少卡顿,动画效果更加流畅。

完整的渲染流程

更具体一些,一个完整的渲染步骤大致可总结为如下:

  • 渲染进程将HTML内容转换为能够读懂的DOM树结构。
  • 渲染引擎将CSS样式表转化为浏览器可以理解的 styleSheets ,计算出DOM节点的样式。
  • 创建布局树,并计算元素的布局信息。
  • 对布局树进行分层,并生成分层树。
  • 为每个图层生成绘制列表,并将其提交到合成线程。
  • 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  • 合成线程发送绘制图块命令DrawQuad给浏览器进程。
  • 浏览器进程根据DrawQuad消息生成页面,并显示到显示器上

普通图层和复合图层

上面的介绍中,提到了 composite 概念。

可以简单的这样理解,浏览器渲染的图层一般包含两大类:渲染图层(普通图层)以及复合图层

  • 渲染图层:又称默认复合层,是页面普通的文档流。我们虽然可以通过绝对定位,相对定位,浮动定位脱离文档流,但它仍然属于默认复合层,共用同一个绘图上下文对象(GraphicsContext)。
  • 复合图层,它会单独分配资源(当然也会脱离普通文档流,这样一来,不管这个复合图层中怎么变化,也不会影响默认复合层里的回流重绘)

某些特殊的渲染层会被提升为复合成层(Compositing Layers),复合图层拥有单独的 GraphicsLayer,而其他不是复合图层的渲染层,则和其第一个拥有 GraphicsLayer 父层共用一个。

每个 GraphicsLayer 都有一个 GraphicsContextGraphicsContext 会负责输出该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后 draw 到屏幕上,此时,我们的页面也就展现到了屏幕上。

可以 Chrome源码调试 -> More Tools -> Rendering -> Layer borders中看到,黄色的就是复合图层信息。

硬件加速

硬件加速,直观上说就是依赖 GPU 实现图形绘制加速,软硬件加速的区别主要是图形的绘制究竟是 GPU 来处理还是 CPU,如果是 GPU,就认为是硬件加速绘制,反之,则为软件绘制。

一般一个元素开启硬件加速后会变成复合图层,可以独立于普通文档流中,改动后可以避免整个页面重绘,提升性能。

常用的硬件加速方法有:

  • 最常用的方式:translate3dtranslateZ
  • opacity 属性/过渡动画(需要动画执行的过程中才会创建合成层,动画没有开始或结束后元素还会回到之前的状态)
  • will-change属性(这个知识点比较冷僻),一般配合 opacitytranslate 使用(而且经测试,除了上述可以引发硬件加速的属性外,其它属性并不会变成复合层),作用是提前告诉浏览器要变化,这样浏览器会开始做一些优化工作(这个最好用完后就释放)
  • <video><iframe><canvas><webgl>等元素
  • 其它,譬如以前的 flash 插件

当然,有的时候我们想强制触发硬件渲染,就可以通过上面的属性,比如

1will-change: transform; 

或者

1transform:translate3d(0, 0, 0);

使用硬件加速的注意事项

使用硬件加速并不是十全十美的事情,比如:

  • 内存。如果GPU加载了大量的纹理,那么很容易就会发生内容问题,这一点在移动端浏览器上尤为明显,所以,一定要牢记不要让页面的每个元素都使用硬件加速。
  • 使用GPU渲染会影响字体的抗锯齿效果。这是因为GPU和CPU具有不同的渲染机制。即使最终硬件加速停止了,文本还是会在动画期间显示得很模糊。

所以不要大量使用复合图层,否则由于资源消耗过度,页面可能会变的更加卡顿。

同时,在使用硬件加速时,尽可能的使用z-index,防止浏览器默认给后续的元素创建复合层渲染。

具体的原理是这样的:

webkit CSS3中,如果一个元素添加了硬件加速,并且z-index层级比较低,那么在这个元素的后面其它元素(层级比这个元素高的,或者相同的,并且releativeabsolute属性相同的),会默认变为复合层渲染,如果处理不当会极大的影响性能。

简单点理解,其实可以认为是一个隐式合成的概念:如果a是一个复合图层,而且b在a上面,那么b也会被隐式转为一个复合图层,这点需要特别注意。

2.24 CSS动画和JS实现的动画分别有哪些优缺点?

CSS动画

优点

  • 浏览器可以对动画进行优化
  • 代码相对简单,性能调优方向固定
  • 对于帧速表现不好的低版本浏览器,CSS3可以做到自然降级,而JS则需要撰写额外代码

缺点

  • 运行过程控制较弱,无法附加事件绑定回调函数
  • 代码冗长,想用CSS实现稍微复杂一点动画,最后CSS代码都会变得非常笨重

JS动画

优点

  • 控制能力很强, 可以在动画播放过程中对动画进行控制:开始、暂停、回放、终止、取消都是可以做到的。
  • 动画效果比css3动画丰富,有些动画效果,比如曲线运动,冲击闪烁,视差滚动效果,只有js动画才能完成
  • CSS3有兼容性问题,而JS大多时候没有兼容性问题

缺点

  • 代码的复杂度高于CSS动画
  • JavaScript在浏览器的主线程中运行,而主线程中还有其它需要运行的JavaScript脚本、样式计算、布局、绘制任务等,对其干扰导致线程可能出现阻塞,从而造成丢帧的情况

2.25 前端实现动画有哪些方式?

前端常用的动画实现方式有以下种:

  1. css3的transition 属性
  2. css3的animation 属性
  3. 原生JS动画
  4. 使用canvas绘制动画
  5. SVG动画
  6. Jquery的animate函数
  7. 用gif图片

1. css3的transition

transition属性:

用来设置样式的属性值是如何从一种状态平滑过渡到另外一种状态

语法:

1transition: property duration timing-function delay;

transition是一种简写属性,它可以拆分为四个过渡属性。你可以 transition: 值1,值2,值3,值4 这样写,也可以:transition-property: 值1;transition-duration:值2;transition-timing-function:值2;transition-delay:值4;这样写。

描述
transition-property 规定设置过渡效果的 CSS 属性的名称。
transition-duration 规定完成过渡效果需要多少秒或毫秒。
transition-timing-function 规定速度效果的速度曲线。
transition-delay 定义过渡效果何时开始。

演示代码:

<div></div>

div{
  width:50px;
  height: 50px;
  background-color: pink;
}

div:hover{
  width:200px;
}

效果图:

img

由上图可看出:鼠标移入移出时,width状态的变化是瞬间完成的。

添加transition: 1s;

div{
  width:50px;
  height: 50px;
  background-color: pink;
  transition: 1s;
}
div:hover{
  width:200px;
}

效果图:

img

transition: 1s; 设置了width属性状态变化的过渡时间为1秒。

transition`属性默认为:`transition: all 0 ease 0;
transition:1s;` 等价于 `transition: all 1s ease 0;

2. css3的animation

animation属性:比较类似于 flash 中的逐帧动画。学习过 flash的同学知道,这种逐帧动画是由关键帧组成,很多个关键帧连续的播放就组成了动画在 CSS3 中是由属性keyframes来完成逐帧动画的。

animation属性与transition属性的区别:

  • transition只需指定动画的开始和结束状态,整个动画的过程是由特定的函数控制,你不用管它。
  • animation可以对动画过程中的各个关键帧进行设置

演示代码:

<div></div>

1div{
	width:50px;
	height:50px;
	background-color: pink;
}
div:hover{
	animation: change1 5s;
}
@keyframes change1{
	25%  {width:130px;background-color: red;}
	50%  {width:170px;background-color: blue;}
	75%  {width:210px;background-color: green;}
	100% {width:250px;background-color: yellow;}
}

效果图:

img

3. 原生JS动画

其主要思想是通过setInterval或setTimeout方法的回调函数来持续调用改变某个元素的CSS样式以达到元素样式变化的效果。

javascript 实现动画通常会导致页面频繁性重排重绘,消耗性能,一般应该在桌面端浏览器。在移动端上使用会有明显的卡顿。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style type="text/css">
        #rect {
            width: 200px;
            height: 200px;
            background: #ccc;
        }
    </style>
</head>
<body>
    <div id="rect"></div>
    <script>
        let elem = document.getElementById('rect');
        let left = 0;
        let timer = setInterval(function(){
            if(left<window.innerWidth-200){
                elem.style.marginLeft = left+'px';
                left ++;
            }else {
                clearInterval(timer);
            }
        },16);
    </script>
</body>
</html>

上面的例子中,我们设置的setInterval时间间隔是16ms。一般认为人眼能辨识的流畅动画为每秒60帧,这里16ms比(1000ms/60)帧略小一些,但是一般可仍为该动画是流畅的。

在很多移动端动画性能优化时,一般使用16ms来进行节流处理连续触发的浏览器事件。例如对touchmove、scroll事件进行节流等。通过这种方式减少持续事件的触发频率,可以大大提升动画的流畅性。

4. 使用canvas绘制动画

canvas作为H5新增元素,是借助Web API来实现动画的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
    *{
        margin:0;
        padding:0;
    }
    </style>
</head>
<body>
    <canvas id="canvas" width="700" height="550"></canvas>
    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let ctx = canvas.getContext("2d");
        let left = 0;
        let timer = setInterval(function(){
            ctx.clearRect(0,0,700,550);
            ctx.beginPath();
            ctx.fillStyle = "#ccc";
            ctx.fillRect(left,0,100,100);
            ctx.stroke();
            if(left>700){
                clearInterval(timer);
            }
            left += 1;
        },16);
    </script>
</body>
</html>

注释:通过getContext()获取元素的绘制对象,通过clearRect不断清空画布并在新的位置上使用fillStyle绘制新矩形内容实现页面动画效果。

Canvas主要优势是可以应对页面中多个动画元素渲染较慢的情况,完全通过javascript来渲染控制动画的执行。可用于实现较复杂动画。

5. SVG 动画

SVG是一种基于XML的图像格式,非常类似于HTML的工作方式。它为许多熟悉的几何形状定义了不同的元素,这些元素可以在标记中组合以产生二维图形。

同样高清的质地,矢量图不畏惧放大,体积小。

这里要说明一点就是,因为 SVG 中保存的是点、线、面的信息,与分辨率和图形大小无关,只是跟图像的复杂程度有关,所以图像文件所占的存储空间通常会比 png 小。

SVG动画的优势:

  • 优化 SEO 和无障碍的利器,因为 SVG 图像是使用XML(可扩展标记语言【英语:Extensible Markup Language,简称:XML】标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等)来标记构建的,浏览器通过绘制每个点和线来打印它们,而不是用预定义的像素填充某些空间。这确保 SVG 图像可以适应不同的屏幕大小和分辨率。
  • 由于是在 XML 中定义的,SVG 图像比 JPG 或 PNG 图像更灵活,而且我们可以使用 CSS 和 JavaScript 与它们进行交互。SVG 图像设置可以包含 CSS 和 JavaScript。在 react、vue 这种数据驱动视图的框架下,对于 SVG 操作就更加如鱼得水了。(下文会跟大家分享一些小的 SVG 动画在我们项目中的实践)
  • 在运用层面上,SVG 提供了一些图像编辑效果,比如屏蔽和剪裁、应用过滤器等等。并且 SVG 只是文本,因此可以使用 GZip 对其进行有效压缩。

6. Jquery的animate()方法

  • animate() 方法执行 CSS 属性集的自定义动画。
  • 该方法通过 CSS 样式将元素从一个状态改变为另一个状态。
  • CSS属性值是逐渐改变的,这样就可以创建动画效果。
  • 只有数字值可创建动画(比如 “margin:30px”)。字符串值无法创建动画(比如 “background-color:red”)。

代码演示:

<button id="btn1">使用动画放大高度</button>
<button id="btn2">重置高度</button>
<div id="box" style="background:#98bf21;height:100px;width:100px;margin:6px;">
</div>

$(document).ready(function(){
    $("#btn1").click(function(){
        $("#box").animate({height:"300px"});
    });
    $("#btn2").click(function(){
        $("#box").animate({height:"100px"});
    });
});

效果图:

img

7. 使用gif图片

gif图想必大家都接触过,前端使用也非常简单。

总结:

  • 代码复杂度方面简单动画:css代码实现会简单一些,js复杂一些。 复杂动画的话:css代码就会变得冗长,js实现起来更优。
  • 动画运行时,对动画的控制程度上 js 比较灵活,能控制动画暂停,取消,终止等css动画不能添加事件,只能设置固定节点进行什么样的过渡动画。
  • 兼容方面 css 有浏览器兼容问题js大多情况下是没有的。
  • 性能方面 css动画相对于优一些,css 动画通过GUI解析js动画需要经过js引擎代码解析,然后再进行 GUI 解析渲染。

2.26 为何CSS不支持父选择器?

这个问题的答案和“为何CSS相邻兄弟选择器只支持后面的元素,而不支持前面的兄弟元素?”是一样的。

浏览器解析HTML文档,是从前往后,由外及里的。所以,我们时常会看到页面先出现头部然后主体内容再出现的加载情况。

但是,如果CSS支持了父选择器,那就必须要页面所有子元素加载完毕才能渲染HTML文档,因为所谓“父选择器”,就是后代元素影响祖先元素,如果后代元素还没加载处理,如何影响祖先元素的样式?于是,网页渲染呈现速度就会大大减慢,浏览器会出现长时间的白板。加载多少HTML就可以渲染多少HTML,在网速不是很快的时候,就显得尤为的必要。比方说你现在看的这篇文章,只要文章内容加载出来就可以了,就算后面的广告脚本阻塞了后续HTML文档的加载,我们也是可以阅读和体验。但是,如果支持父选择器,则整个文档不能有阻塞,页面的可访问性则要大大降低。

有人可能会说,要不采取加载到哪里就渲染到哪里的策略?这样子问题更大,因为会出现加载到子元素的时候,父元素本来渲染的样式突然变成了另外一个样式的情况,体验非常不好。

“相邻选择器只能选择后面的元素”也是一样的道理,不可能说后面的HTML加载好了,还会影响前面HTML的样式。

所以,从这一点来讲,CSS支持“父选择器”或者“前兄弟选择器”的可能性要比其他炫酷的CSS特性要低,倒不是技术层面,而是CSS和HTML本身的渲染机制决定的。当然,以后的事情谁都说不准,说不定以后网速都是每秒几个G的,网页加载速度完全就忽略不计,说不定就会支持了。

2.27 脱离文档流有哪些方法?

一、什么是文档流?

将窗体自上而下分成一行一行,并在每行中按从左至右依次排放元素,称为文档流,也称为普通流。

这个应该不难理解,HTML中全部元素都是盒模型,盒模型占用一定的空间,依次排放在HTML中,形成了文档流。

二、什么是脱离文档流?

元素脱离文档流之后,将不再在文档流中占据空间,而是处于浮动状态(可以理解为漂浮在文档流的上方)。脱离文档流的元素的定位基于正常的文档流,当一个元素脱离文档流后,依然在文档流中的其他元素将忽略该元素并填补其原先的空间。

三、怎么脱离文档流?

float

使用float可以脱离文档流。

注意!!!:使用float脱离文档流时,其他盒子会无视这个元素,但其他盒子内的文本依然会为这个元素让出位置,环绕在该元素的周围。

absolute

absolute称为绝对定位,其实博主觉得应该称为相对定位,因为使用absolute脱离文档流后的元素,是相对于该元素的父类(及以上,如果直系父类元素不满足条件则继续向上查询)元素进行定位的,并且这个父类元素的position必须是非static定位的(static是默认定位方式)。

fixed

完全脱离文档流,相对于浏览器窗口进行定位。(相对于浏览器窗口就是相对于html)。

2.28 css sprites是什么,怎么使用?

是什么

CSS Sprites是一种网页图片应用处理方式,就是把网页中一些背景图片整合到一张图片文件中,再利用CSS的“background-image”,“background- repeat”,“background-position”的组合进行背景定位。

优点

其优点在于:

  • 减少网页的http请求,提高性能,这也是CSS Sprites最大的优点,也是其被广泛传播和应用的主要原因;
  • 减少图片的字节:多张图片合并成1张图片的字节小于多张图片的字节总和;
  • 减少了命名困扰:只需对一张集合的图片命名,不需要对每一个小元素进行命名提高制作效率;
  • 更换风格方便:只需要在一张或少张图片上修改图片的颜色或样式,整个网页的风格就可以改变,维护起来更加方便。

缺点

诚然CSS Sprites是如此的强大,但是也存在一些不可忽视的缺点:

  • 图片合成比较麻烦;
  • 背景设置时,需要得到每一个背景单元的精确位置;
  • 维护合成图片时,最好只是往下加图片,而不要更改已有图片。

2.29 html和css中的图片加载与渲染规则是什么样的?

Web浏览器先会把获取到的HTML代码解析成一个DOM树,HTML中的每个标签都是DOM树中的一个节点,包括display: none隐藏的标签,还有JavaScript动态添加的元素等。浏览器会获取到所有样式,并会把所有样式解析成样式规则,在解析的过程中会去掉浏览器不能识别的样式。

浏览器将会把DOM树和样式规则组合在一起(DOM元素和样式规则匹配)后将会合建一个渲染树(Render Tree),渲染树类似于DOM树,但两者别还是很大的:渲染树能识别样式,渲染树中每个节点(NODE)都有自己的样式,而且渲染树不包含隐藏的节点(比如display:none的节点,还有内的一些节点),因为这些节点不会用于渲染,也不会影响节点的渲染,因此不会包含到渲染树中。一旦渲染树构建完毕后,浏览器就可以根据渲染树来绘制页面了。

简单的归纳就是浏览器渲染Web页面大约会经过六个过程:

  • 解析HTML,构成DOM树
  • 解析加载的样式,构建样式规则树
  • 加载JavaScript,执行JavaScript代码
  • DOM树和样式规则树进行匹配,构成渲染树
  • 计算元素位置进行页面布局
  • 绘制页面,最终在浏览器中呈现

是不是会感觉这个和我们图像加载渲染没啥关系一样,事实并非如此,因为img、picture或者background-image都是DOM树或样式规则中的一部分,那么咱们套用进来,图片加载和渲染的时机有可能是下面这样:

  • 解析HTML时,如果遇到img或picture标签,将会加载图片
  • 解析加载的样式,遇到background-image时,并不会加载图片,而会构建样式规则树
  • 加载JavaScript,执行JavaScript代码,如果代码中有创建img元素之类,会添加到DOM树中;如查有添加background-image规则,将会添加到样式规则树中
  • DOM树和样式规则匹配时构建渲染树,如果DOM树节点匹配到样式规则中的backgorund-image,则会加载背景图片
  • 计算元素(图片)位置进行布局
  • 开始渲染图片,浏览器将呈现渲染出来的图片

上面套用浏览器渲染页面的机制,但图片加载与渲染还是有一定的规则。因为,页面中不是所有的(或picture)元素引入的图片和background-image引入的背景图片都会加载的。那么就引发出新问题了,什么时候会真正的加载,加载规则又是什么?

先概括一点:

Web页面中不是所有的图片都会加载和渲染!

根据前面介绍的浏览器加载和渲染机制,我们可以归纳为:

  • 、和设置background-image的元素遇到display:none时,图片会加载但不会渲染
  • 、和设置background-image的元素祖先元素设置display:none时,background-image不会渲染也不会加载,而img和picture引入的图片不会渲染但会加载
  • 、和background-image引入相同路径相同图片文件名时,图片只会加载一次
  • 样式文件中background-image引入的图片,如果匹配不到DOM元素,图片不会加载
  • 伪类引入的background-image,比如:hover,只有当伪类被触发时,图片才会加载

2.30 什么是BFC?

BFC:block formatting context,块级格式化上下文。

BFC是Web页面的可视CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。

定位方案:

  • 内部的Box会在垂直方向上一个接一个放置。
  • Box垂直方向的距离由margin决定,属于同一个BFC的两个相邻Box的margin会发生重叠。
  • 每个元素的margin box 的左边,与包含块border box的左边相接触。
  • BFC的区域不会与float box重叠。
  • BFC是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。
  • 计算BFC的高度时,浮动元素也会参与计算。

满足下列条件之一就可触发BFC:

  • 根元素,即html
  • float的值不为none(默认)
  • overflow的值不为visible(默认)
  • display的值为table-cell, table-caption, inline-block, flex, 或者 inline-flex 中的其中一个
  • position的值为absolute或fixed

2.31 分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景

结构

display:none: 会让元素完全从渲染树中消失,渲染的时候不占据任何空间, 不能点击

visibility: hidden:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,不能点击

opacity: 0: 不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击

继承

display: none和opacity: 0:是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示。

visibility: hidden:是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式。

性能

displaynone : 修改元素会造成文档回流,读屏器不会读取display: none元素内容,性能消耗较大

visibility:hidden: 修改元素只会造成本元素的重绘,性能消耗较少读屏器读取visibility: hidden元素内容

opacity: 0 :修改元素会造成重绘,性能消耗较少

2.32 Atom CSS 是什么?

Atom CSS:原子CSS,意思是一个类只干一件事。

不同于大家常用的BEM这类规则,原子css就是拆分,所有 CSS 类都有一个唯一的 CSS 规则。例如如下

.w-full{
  width:100%;
}
.h-full{
  height:100%;
}

而像这种就不是

.w&h-full{
  width:100%;
  height:100%;
}

当我们使用的时候,直接写class名就可以

<html>
	<body>
    	<div id="app" class="w-full h-full">
        </div>
	</body>
</html>

原子CSS的优缺点

  • 优点
    • 减少了css体积,提高了css复用
    • 减少起名的复杂度
  • 缺点
    • 增加了记忆成本。将css拆分为原子之后,你势必要记住一些class才能书写,哪怕tailwindcss提供了完善的工具链,你写background,也要记住开头是bg。
    • 增加了html结构的复杂性。当整个dom都是这样class名,势必会带来调试的麻烦,有的时候很难定位具体css问题
    • 你仍需要起class名。对于大部分属性而言,你可以只用到center,auto,100%,这些值,但是有时候你仍需要设定不一样的参数值,例如left,top,这时候你还需要起一个class名

2.33 CSS中的1像素问题是什么?有哪些解决方案?

1px 边框问题的由来

苹果 iPhone4 首次提出了 Retina Display(视网膜屏幕)的概念,在 iPhone4 使用的视网膜屏幕中,把 2x2 个像素当 1 个物理像素使用,即使用 2x2 个像素显示原来 1 个物理像素显示的内容,从而让 UI 显示更精致清晰,这 2x2 个像素叫做逻辑像素。

像这种像素比(像素比(即dpr)= 物理像素 / 逻辑像素)为 2 的视网膜屏幕也被称为二倍屏,目前市面上还有像素比更高的三倍屏、四倍屏。

而 CSS 中 1px 指的是物理像素,因此,设置为 1px 的边框在 dpr = 2 的视网膜屏幕中实际占用了 2 个逻辑像素的宽度,这就导致了界面边框变粗的视觉体验。

使用 transform 解决

通过设置元素的 box-sizing 为 border-box,然后构建伪元素,再使用 CSS3 的 transform 缩放,这是目前市面上最受推崇的解决方法。这种方法可以满足所有的场景,而且修改灵活,唯一的缺陷是,对于已使用伪元素的元素要多嵌套一个无用元素。具体的实现如下:

.one-pixel-border {
  position: relative;
  box-sizing: border-box;
}

.one-pixel-border::before {
  display: block;
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200%;
  height: 200%;
  border: 1px solid red;
  transform: translate(-50%, -50%) scale(0.5, 0.5);
}

这样就可以得到 0.5px 的边框。

还可以结合媒体查询(@media)解决不同 dpr 值屏幕的边框问题,如下:

@media screen and (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {
  ...
}

@media screen and (-webkit-min-device-pixel-ratio: 3), (min-resolution: 3dppx) {
  ...
}

当然还有不少其他的解决方案:border-image、background-image、viewport + rem + js、box-shadow等,但都有各自的缺点,不进行推荐,此处也不做详细介绍。

2.34 css加载会造成阻塞吗?

先说下结论:

  • css加载不会阻塞DOM树的解析
  • css加载会阻塞DOM树的渲染
  • css加载会阻塞后面js语句的执行

为了避免让用户看到长时间的白屏时间,我们应该尽可能的提高css加载速度,比如可以使用以下几种方法:

  • 使用CDN(因为CDN会根据你的网络状况,替你挑选最近的一个具有缓存内容的节点为你提供资源,因此可以减少加载时间)
  • 对css进行压缩(可以用很多打包工具,比如webpack,gulp等,也可以通过开启gzip压缩)
  • 合理的使用缓存(设置cache-control,expires,以及E-tag都是不错的,不过要注意一个问题,就是文件更新后,你要避免缓存而带来的影响。其中一个解决防范是在文件名字后面加一个版本号)
  • 减少http请求数,将多个css文件合并,或者是干脆直接写成内联样式(内联样式的一个缺点就是不能缓存)

原理解析

浏览器渲染的流程如下:

  • HTML解析文件,生成DOM Tree,解析CSS文件生成CSSOM Tree
  • 将Dom Tree和CSSOM Tree结合,生成Render Tree(渲染树)
  • 根据Render Tree渲染绘制,将像素渲染到屏幕上。

从流程我们可以看出来:

  • DOM解析和CSS解析是两个并行的进程,所以这也解释了为什么CSS加载不会阻塞DOM的解析。
  • 然而,由于Render Tree是依赖于DOM Tree和CSSOM Tree的,所以他必须等待到CSSOM Tree构建完成,也就是CSS资源加载完成(或者CSS资源加载失败)后,才能开始渲染。因此,CSS加载是会阻塞Dom的渲染的。
  • 由于js可能会操作之前的Dom节点和css样式,因此浏览器会维持html中css和js的顺序。因此,样式表会在后面的js执行前先加载执行完毕。所以css会阻塞后面js的执行。

2.35 CSS 中有哪几种定位方式?

  • Static

这个是元素的默认定位方式,元素出现在正常的文档流中,会占用页面空间。

  • Relative

相对定位方式,相对于其父级元素(无论父级元素此时为何种定位方式)进行定位,准确地说是相对于其父级元素所剩余的未被占用的空间进行定位(在父元素由多个相对定位的子元素时可以看出),且会占用该元素在文档中初始的页面空间,即在使用top,bottom,left,right进行移动位置之后依旧不会改变其所占用空间的位置。可以使用z-index进行在z轴方向上的移动。

  • Absolute

绝对定位方式,脱离文档流,不会占用页面空间。以最近的不是static定位的父级元素作为参考进行定位,如果其所有的父级元素都是static定位,那么此元素最终则是以当前窗口作为参考进行定位。

可以使用top,bottom,left,right进行位置移动,亦可使用z-index在z轴上面进行移动。当元素为此定位时,如果该元素为内联元素,则会变为块级元素,即可以直接设置其宽和高的值;如果该元素为块级元素,则其宽度会由初始的100%变为auto。

注意:当元素设置为绝对定位时,在没有指定top,bottom,left,right的值时,他们的值并不是0,这几个值是有默认值的,默认值就是该元素设置为绝对定位前所处的正常文档流中的位置。

  • Fixed

绝对定位方式,直接以浏览器窗口作为参考进行定位。其它特性同absolute定位。

当父元素使用了transform的时候,会以父元素定位。

  • sticky

粘性定位,可以简单理解为relative和fixed布局的混合。

当粘性约束矩形在可视范围内为relative,反之,则为fixed粘性定位元素如果和它的父元素一样高,则垂直滚动的时候,粘性定位效果是不会出现的它的定位效果完全受限于父级元素们。

如果父元素的overflow属性设置了scroll,auto,overlay值,那么,粘性定位将会失效同一容器中多个粘贴定位元素独立偏移,因此可能重叠;位置上下靠在一起的不同容器中的粘贴定位元素则会鸠占鹊巢,挤开原来的元素,形成依次占位的效果。

2.36 如果需要手动写动画,你认为最小时间间隔是多久,为什么?

多数显示器默认频率是60Hz,即1秒刷新60次,所以理论上最小间隔为1/60*1000ms = 16.7ms。

2.37 CSS优化、提高性能的方法有哪些?

  • 避免过度约束
  • 避免后代选择符
  • 避免链式选择符
  • 使用紧凑的语法
  • 避免不必要的命名空间
  • 避免不必要的重复
  • 最好使用表示语义的名字。一个好的类名应该是描述他是什么而不是像什么
  • 避免!important,可以选择其他选择器
  • 尽可能的精简规则,你可以合并不同类里的重复规则

2.38 margin和padding分别适合什么场景使用?

何时使用margin:

  • 需要在border外侧添加空白
  • 空白处不需要背景色
  • 上下相连的两个盒子之间的空白,需要相互抵消时。

何时使用padding:

  • 需要在border内侧添加空白
  • 空白处需要背景颜色
  • 上下相连的两个盒子的空白,希望为两者之和。

2.39 为什么会出现浮动?什么时候需要清除浮动?清除浮动的方式有哪些?

浮动元素碰到包含它的边框或者浮动元素的边框停留。由于浮动元素不在文档流中,所以文档流的块框表现得就像浮动框不存在一样。浮动元素会漂浮在文档流的块框上。

浮动带来的问题:

  • 父元素的高度无法被撑开,影响与父元素同级的元素
  • 与浮动元素同级的非浮动元素(内联元素)会跟随其后
  • 若非第一个元素浮动,则该元素之前的元素也需要浮动,否则会影响页面显示的结构。

清除浮动的方式:

  • 父级div定义height
  • 最后一个浮动元素后加空div标签 并添加样式clear:both。
  • 包含浮动元素的父标签添加样式overflow为hidden或auto。
  • 父级div定义zoom

2.40 CSS3新增伪类有那些?

  • p:first-of-type 选择属于其父元素的首个元素
  • p:last-of-type 选择属于其父元素的最后元素
  • p:only-of-type 选择属于其父元素唯一的元素
  • p:only-child 选择属于其父元素的唯一子元素
  • p:nth-child(2) 选择属于其父元素的第二个子元素
  • :enabled :disabled 表单控件的禁用状态。
  • :checked 单选框或复选框被选中。

2.41 CSS中,box-sizing属性值有什么用?

用来控制元素的盒子模型的解析模式,默认为content-box

  • context-box:W3C的标准盒子模型,设置元素的 height/width 属性指的是content部分的高/宽
  • border-box:IE传统盒子模型。设置元素的height/width属性指的是border + padding + content部分的高/宽

使用 box-sizing 属性可以解决一些常见的布局问题,同时也提高了 CSS 代码编写的效率和可维护性。例如,当我们使用百分比或 rem 等相对单位来设置元素尺寸时,使用 border-box 盒模型更为合适;而在处理一些尺寸固定并且需要进行细粒度控制的情况下,content-box 盒模型可能更为实用。

需要注意的是,在不同的 box-sizing 设置下,元素的尺寸计算方式会发生变化,需要在编写 CSS 代码时充分考虑该属性对布局和样式的影响。

2.42 前端项目中为什么要初始化CSS样式?

因为浏览器的兼容问题,不同浏览器对标签的默认值是不同的,如果没有对浏览器的CSS初始化,会造成相同页面在不同浏览器的显示存在差异。

2.43 CSS匹配规则顺序是怎么样的?

相信大多数初学者都会认为CSS匹配是左向右的,其实恰恰相反。

CSS匹配发生在Render Tree构建时(Chrome Dev Tools将其归属于Layout过程)。此时浏览器构建出了DOM,而且拿到了CSS样式,此时要做的就是把样式跟DOM上的节点对应上,浏览器为了提高性能需要做的就是快速匹配。

首先要明确一点,浏览器此时是给一个"可见"节点找对应的规则,这和jQuery选择器不同,后者是使用一个规则去找对应的节点,这样从左到右或许更快。但是对于前者,由于CSS的庞大,一个CSS文件中或许有上千条规则,而且对于当前节点来说,大多数规则是匹配不上的,稍微想一下就知道,如果从右开始匹配(也是从更精确的位置开始),能更快排除不合适的大部分节点,而如果从左开始,只有深入了才会发现匹配失败,如果大部分规则层级都比较深,就比较浪费资源了。

除了上面这点,我们前面还提到DOM构建是"循序渐进的",而且DOM不阻塞Render Tree构建(只有CSSOM阻塞),这样也是为了能让页面更早有元素呈现。

考虑如下情况,如果我们此时构建的只是部分DOM,而CSSOM构建完成,浏览器就会构建Render Tree。

这个时候对每一个节点,如果找到一条规则从右向左匹配,我们只需要逐层观察该节点父节点是否匹配,而此时其父节点肯定已经在DOM上。

但是反过来,我们可能会匹配到一个DOM上尚未存在的节点,此时的匹配过程就浪费了资源。

2.44 如何使用css完成视差滚动效果?

一、是什么

视差滚动(Parallax Scrolling)是指多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验

我们可以把网页解刨成:背景层、内容层、悬浮层

当滚动鼠标滑轮的时候,各个图层以不同的速度移动,形成视觉差的效果

二、实现方式

使用css形式实现视觉差滚动效果的方式有:

  • background-attachment
  • transform:translate3D

background-attachment

作用是设置背景图像是否固定或者随着页面的其余部分滚动

值分别有如下:

  • scroll:默认值,背景图像会随着页面其余部分的滚动而移动
  • fixed:当页面的其余部分滚动时,背景图像不会移动
  • inherit:继承父元素background-attachment属性的值

完成滚动视觉差就需要将background-attachment属性设置为fixed,让背景相对于视口固定。及时一个元素有滚动机制,背景也不会随着元素的内容而滚动

也就是说,背景一开始就已经被固定在初始的位置

核心的css代码如下:

section {
    height: 100vh;
}

.g-img {
    background-image: url(...);
    background-attachment: fixed;
    background-size: cover;
    background-position: center center;
}

整体栗子如下:

<style>
div {
            height: 100vh;
            background: rgba(0, 0, 0, .7);
            color: #fff;
            line-height: 100vh;
            text-align: center;
           font-size: 20vh;
        }

        .a-img1 {
            background-image: url(https://images.pexels.com/photos/1097491/pexels-photo-1097491.jpeg);
            background-attachment: fixed;
            background-size: cover;
            background-position: center center;
        }

        .a-img2 {
            background-image: url(https://images.pexels.com/photos/2437299/pexels-photo-2437299.jpeg);
            background-attachment: fixed;
            background-size: cover;
            background-position: center center;
        }

        .a-img3 {
            background-image: url(https://images.pexels.com/photos/1005417/pexels-photo-1005417.jpeg);
            background-attachment: fixed;
            background-size: cover;
            background-position: center center;
        }
</style>
 <div class="a-text">1</div>
    <div class="a-img1">2</div>
    <div class="a-text">3</div>
    <div class="a-img2">4</div>
    <div class="a-text">5</div>
    <div class="a-img3">6</div>
    <div class="a-text">7</div>

transform:translate3D

同样,让我们先来看一下两个概念transformperspective

  • transform: css3 属性,可以对元素进行变换(2d/3d),包括平移 translate,旋转 rotate,缩放 scale,等等
  • perspective: css3 属性,当元素涉及 3d 变换时,perspective 可以定义我们眼睛看到的 3d 立体效果,即空间感

3D视角示意图如下所示:

举个栗子:

<style>
    html {
        overflow: hidden;
        height: 100%
   }

    body {
        /* 视差元素的父级需要3D视角 */
        perspective: 1px;
        transform-style: preserve-3d; 
        height: 100%;
        overflow-y: scroll;
        overflow-x: hidden;
    }
    #app{
        width: 100vw;
        height:200vh;
        background:skyblue;
        padding-top:100px;
    }
    .one{
        width:500px;
        height:200px;
        background:#409eff;
        transform: translateZ(0px);
        margin-bottom: 50px;
    }
    .two{
        width:500px;
        height:200px;
        background:#67c23a;
        transform: translateZ(-1px);
        margin-bottom: 150px;
    }
    .three{
        width:500px;
        height:200px;
        background:#e6a23c;
        transform: translateZ(-2px);
        margin-bottom: 150px;
    }
</style>
<div id="app">
    <div class="one">one</div>
    <div class="two">two</div>
    <div class="three">three</div>
</div>

而这种方式实现视觉差动的原理如下:

  • 容器设置上 transform-style: preserve-3d 和 perspective: xpx,那么处于这个容器的子元素就将位于3D空间中,
  • 子元素设置不同的 transform: translateZ(),这个时候,不同元素在 3D Z轴方向距离屏幕(我们的眼睛)的距离也就不一样
  • 滚动滚动条,由于子元素设置了不同的 transform: translateZ(),那么他们滚动的上下距离 translateY 相对屏幕(我们的眼睛),也是不一样的,这就达到了滚动视差的效果

2.45 如何实现单行/多行文本溢出的省略样式?

二、实现方式

单行文本溢出省略

理解也很简单,即文本在一行内显示,超出部分以省略号的形式展现

实现方式也很简单,涉及的css属性有:

  • text-overflow:规定当文本溢出时,显示省略符号来代表被修剪的文本
  • white-space:设置文字在一行显示,不能换行
  • overflow:文字长度超出限定宽度,则隐藏超出的内容

overflow设为hidden,普通情况用在块级元素的外层隐藏内部溢出元素,或者配合下面两个属性实现文本溢出省略

white-space:nowrap,作用是设置文本不换行,是overflow:hiddentext-overflow:ellipsis生效的基础

text-overflow属性值有如下:

  • clip:当对象内文本溢出部分裁切掉
  • ellipsis:当对象内文本溢出时显示省略标记(…)

text-overflow只有在设置了overflow:hiddenwhite-space:nowrap才能够生效的

举个例子

<style>
    p{
        overflow: hidden;
        line-height: 40px;
        width:400px;
        height:40px;
        border:1px solid red;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
</style>
<p 这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本</p >

可以看到,设置单行文本溢出较为简单,并且省略号显示的位置较好

多行文本溢出省略

多行文本溢出的时候,我们可以分为两种情况:

  • 基于高度截断
  • 基于行数截断

基于高度截断

伪元素 + 定位

核心的css代码结构如下:

  • position: relative:为伪元素绝对定位
  • overflow: hidden:文本溢出限定的宽度就隐藏内容)
  • position: absolute:给省略号绝对定位
  • line-height: 20px:结合元素高度,高度固定的情况下,设定行高, 控制显示行数
  • height: 40px:设定当前元素高度
  • ::after {} :设置省略号样式

代码如下所示:

<style>
    .demo {
        position: relative;
        line-height: 20px;
        height: 40px;
        overflow: hidden;
    }
    .demo::after {
        content: "...";
        position: absolute;
        bottom: 0;
        right: 0;
        padding: 0 20px 0 10px;
    }
</style>

<body>
    <div class='demo'>这是一段很长的文本</div>
</body>

实现原理很好理解,就是通过伪元素绝对定位到行尾并遮住文字,再通过 overflow: hidden 隐藏多余文字

这种实现具有以下优点:

  • 兼容性好,对各大主流浏览器有好的支持
  • 响应式截断,根据不同宽度做出调整

一般文本存在英文的时候,可以设置word-break: break-all使一个单词能够在换行时进行拆分

基于行数截断

css实现也非常简单,核心的css代码如下:

  • -webkit-line-clamp: 2:用来限制在一个块元素显示的文本的行数,为了实现该效果,它需要组合其他的WebKit属性)
  • display: -webkit-box:和1结合使用,将对象作为弹性伸缩盒子模型显示
  • -webkit-box-orient: vertical:和1结合使用 ,设置或检索伸缩盒对象的子元素的排列方式
  • overflow: hidden:文本溢出限定的宽度就隐藏内容
  • text-overflow: ellipsis:多行文本的情况下,用省略号“…”隐藏溢出范围的文本
<style>
    p {
        width: 400px;
        border-radius: 1px solid red;
        -webkit-line-clamp: 2;
        display: -webkit-box;
        -webkit-box-orient: vertical;
        overflow: hidden;
        text-overflow: ellipsis;
    }
</style>
<p>
    这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本
    这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本
</p >

可以看到,上述使用了webkitCSS属性扩展,所以兼容浏览器范围是PC端的webkit内核的浏览器,由于移动端大多数是使用webkit,所以移动端常用该形式

需要注意的是,如果文本为一段很长的英文或者数字,则需要添加word-wrap: break-word属性

还能通过使用javascript实现配合css,实现代码如下所示:

css结构如下:

p {
    position: relative;
    width: 400px;
    line-height: 20px;
    overflow: hidden;

}
.p-after:after{
    content: "..."; 
    position: absolute; 
    bottom: 0; 
    right: 0; 
    padding-left: 40px;
    background: -webkit-linear-gradient(left, transparent, #fff 55%);
    background: -moz-linear-gradient(left, transparent, #fff 55%);
    background: -o-linear-gradient(left, transparent, #fff 55%);
    background: linear-gradient(to right, transparent, #fff 55%);
}

javascript代码如下:

$(function(){
 //获取文本的行高,并获取文本的高度,假设我们规定的行数是五行,那么对超过行数的部分进行限制高度,并加上省略号
   $('p').each(function(i, obj){
        var lineHeight = parseInt($(this).css("line-height"));
        var height = parseInt($(this).height());
        if((height / lineHeight) >3 ){
            $(this).addClass("p-after")
            $(this).css("height","60px");
        }else{
            $(this).removeClass("p-after");
        }
    });
})

2.46 怎么使用 CSS3 实现动画?

一、是什么

CSS动画(CSS Animations)是为层叠样式表建议的允许可扩展标记语言(XML)元素使用CSS的动画的模块

即指元素从一种样式逐渐过渡为另一种样式的过程

常见的动画效果有很多,如平移、旋转、缩放等等,复杂动画则是多个简单动画的组合

css实现动画的方式,有如下几种:

  • transition 实现渐变动画
  • transform 转变动画
  • animation 实现自定义动画

二、实现方式

transition 实现渐变动画

transition的属性如下:

  • property:填写需要变化的css属性
  • duration:完成过渡效果需要的时间单位(s或者ms)
  • timing-function:完成效果的速度曲线
  • delay: 动画效果的延迟触发时间

其中timing-function的值有如下:

描述
linear 匀速(等于 cubic-bezier(0,0,1,1))
ease 从慢到快再到慢(cubic-bezier(0.25,0.1,0.25,1))
ease-in 慢慢变快(等于 cubic-bezier(0.42,0,1,1))
ease-out 慢慢变慢(等于 cubic-bezier(0,0,0.58,1))
ease-in-out 先变快再到慢(等于 cubic-bezier(0.42,0,0.58,1)),渐显渐隐效果
cubic-bezier(n,n,n,n) 在 cubic-bezier 函数中定义自己的值。可能的值是 0 至 1 之间的数值

注意:并不是所有的属性都能使用过渡的,如display:none<->display:block

举个例子,实现鼠标移动上去发生变化动画效果

<style>
       .base {
    
    
            width: 100px;
            height: 100px;
            display: inline-block;
            background-color: #0EA9FF;
            border-width: 5px;
            border-style: solid;
            border-color: #5daf34;
            transition-property: width, height, background-color, border-width;
            transition-duration: 2s;
            transition-timing-function: ease-in;
            transition-delay: 500ms;
        }

        /*简写*/
        /*transition: all 2s ease-in 500ms;*/
        .base:hover {
    
    
            width: 200px;
            height: 200px;
            background-color: #5daf34;
            border-width: 10px;
            border-color: #3a8ee6;
        }
</style>
<div class="base"></div>

transform 转变动画

包含四个常用的功能:

  • translate:位移
  • scale:缩放
  • rotate:旋转
  • skew:倾斜

一般配合transition过度使用

注意的是,transform不支持inline元素,使用前把它变成block

举个例子

<style>
    .base {
        width: 100px;
        height: 100px;
        display: inline-block;
        background-color: #0EA9FF;
        border-width: 5px;
        border-style: solid;
        border-color: #5daf34;
        transition-property: width, height, background-color, border-width;
        transition-duration: 2s;
        transition-timing-function: ease-in;
        transition-delay: 500ms;
    }
    .base2 {
        transform: none;
        transition-property: transform;
        transition-delay: 5ms;
    }

    .base2:hover {
        transform: scale(0.8, 1.5) rotate(35deg) skew(5deg) translate(15px, 25px);
    }
</style>
 <div class="base base2"></div>

可以看到盒子发生了旋转,倾斜,平移,放大

animation 实现自定义动画

animation是由 8 个属性的简写,分别如下:

属性 描述 属性值
animation-duration 指定动画完成一个周期所需要时间,单位秒(s)或毫秒(ms),默认是 0
animation-timing-function 指定动画计时函数,即动画的速度曲线,默认是 “ease” linear、ease、ease-in、ease-out、ease-in-out
animation-delay 指定动画延迟时间,即动画何时开始,默认是 0
animation-iteration-count 指定动画播放的次数,默认是 1
animation-direction 指定动画播放的方向 默认是 normal normal、reverse、alternate、alternate-reverse
animation-fill-mode 指定动画填充模式。默认是 none forwards、backwards、both
animation-play-state 指定动画播放状态,正在运行或暂停。默认是 running running、pauser
animation-name 指定 @keyframes 动画的名称

CSS 动画只需要定义一些关键的帧,而其余的帧,浏览器会根据计时函数插值计算出来,

通过 @keyframes 来定义关键帧

因此,如果我们想要让元素旋转一圈,只需要定义开始和结束两帧即可:

@keyframes rotate{
    from{
        transform: rotate(0deg);
    }
    to{
        transform: rotate(360deg);
    }
}

from 表示最开始的那一帧,to 表示结束时的那一帧

也可以使用百分比刻画生命周期

@keyframes rotate{
    0%{
        transform: rotate(0deg);
    }
    50%{
        transform: rotate(180deg);
    }
    100%{
        transform: rotate(360deg);
    }
}

定义好了关键帧后,下来就可以直接用它了:

1animation: rotate 2s;

三、总结

属性 含义
transition(过度) 用于设置元素的样式过度,和animation有着类似的效果,但细节上有很大的不同
transform(变形) 用于元素进行旋转、缩放、移动或倾斜,和设置样式的动画并没有什么关系,就相当于color一样用来设置元素的“外表”
translate(移动) 只是transform的一个属性值,即移动
animation(动画) 用于设置动画属性,他是一个简写的属性,包含6个属性

2.47 em/px/rem/vh/vw 这些单位有什么区别?

一、介绍

传统的项目开发中,我们只会用到px%em这几个单位,它可以适用于大部分的项目开发,且拥有比较良好的兼容性

CSS3开始,浏览器对计量单位的支持又提升到了另外一个境界,新增了remvhvwvm等一些新的计量单位

利用这些新的单位开发出比较良好的响应式页面,适应多种不同分辨率的终端,包括移动设备等

二、单位

css单位中,可以分为长度单位、绝对单位,如下表所指示

CSS单位
相对长度单位 em、ex、ch、rem、vw、vh、vmin、vmax、%
绝对长度单位 cm、mm、in、px、pt、pc

这里我们主要讲述px、em、rem、vh、vw

px

px,表示像素,所谓像素就是呈现在我们显示器上的一个个小点,每个像素点都是大小等同的,所以像素为计量单位被分在了绝对长度单位中

有些人会把px认为是相对长度,原因在于在移动端中存在设备像素比,px实际显示的大小是不确定

这里之所以认为px为绝对单位,在于px的大小和元素的其他属性无关

em

em是相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸(1em = 16px

为了简化 font-size 的换算,我们需要在css中的 body 选择器中声明font-size= 62.5%,这就使 em 值变为 16px*62.5% = 10px

这样 12px = 1.2em, 10px = 1em, 也就是说只需要将你的原来的 px 数值除以 10,然后换上 em 作为单位就行了

特点:

  • em 的值并不是固定的
  • em 会继承父级元素的字体大小
  • em 是相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸
  • 任意浏览器的默认字体高都是 16px

举个例子

<div class="big">
    我是14px=1.4rem
    <div class="small">我是12px=1.2rem</div>
</div>

样式为

<style>
html {font-size: 10px;  } /*  公式16px*62.5%=10px  */  
.big{font-size: 1.4rem}
.small{font-size: 1.2rem}
</style>

这时候.big元素的font-size为14px,而.small元素的font-size为12px

rem

rem,相对单位,相对的只是HTML根元素font-size的值

同理,如果想要简化font-size的转化,我们可以在根元素html中加入font-size: 62.5%

html {font-size: 62.5%;  } /*  公式16px*62.5%=10px  */ 

这样页面中1rem=10px、1.2rem=12px、1.4rem=14px、1.6rem=16px;使得视觉、使用、书写都得到了极大的帮助

特点:

  • rem单位可谓集相对大小和绝对大小的优点于一身
  • 和em不同的是rem总是相对于根元素,而不像em一样使用级联的方式来计算尺寸

vh、vw

vw ,就是根据窗口的宽度,分成100等份,100vw就表示满宽,50vw就表示一半宽。(vw 始终是针对窗口的宽),同理,vh则为窗口的高度

这里的窗口分成几种情况:

  • 在桌面端,指的是浏览器的可视区域
  • 移动端指的就是布局视口

vwvh,比较容易混淆的一个单位是%,不过百分比宽泛的讲是相对于父元素:

对于普通定位元素就是我们理解的父元素

  • 对于position: absolute;的元素是相对于已定位的父元素
  • 对于position: fixed;的元素是相对于 ViewPort(可视窗口)

三、总结

px:绝对单位,页面按精确像素展示

em:相对单位,基准点为父节点字体的大小,如果自身定义了font-size按自身来计算,整个页面内1em不是一个固定的值

rem:相对单位,可理解为root em, 相对根节点html的字体大小来计算

vh、vw:主要用于页面视口大小布局,在页面布局上更加方便简单

猜你喜欢

转载自blog.csdn.net/hyqhyqhyqq/article/details/130649407