浏览器渲染原理与 CSS 动画

1,345 阅读4分钟

1. 浏览器渲染

1.1 浏览器渲染的步骤

浏览器渲染流程大致分为以下几个步骤:

  1. 根据 HTML 构建 HTML 树(DOM);
  2. 根据 CSS 构建 CSS 树(CSSOM);
  3. 将两棵树合并成一棵渲染树(render tree);
  4. Layout 布局(文档流、盒模型、计算大小和位置);
  5. Paint 绘制(把边框颜色,文字颜色阴影等画出来);
  6. Composite 合成(根据层叠关系展示画面)。

HTML树CSS树合并

1.2 更新 CSS 样式的方法和步骤

一般用 JavaScript 更新样式,可以修改样式或者添加类,添加类更为方便常用。使用 JavaScript 更新样式要经过布局、绘制、合成这几个步骤中的一步、二步或者全部。

  • 经过布局、绘制、合成全部过程,如删除某 div 元素;
  • 经过绘制、合成过程,如改变某元素背景颜色,因为位置、大小未改变,不影响布局;
  • 经过合成过程,如改变 transform 属性,在 Chrome 浏览器只需要合成。

JS更新样式三种方式

1.3 各属性触发相应流程的查询方式

通过 csstriggers.com 查询各属性改变时在各浏览器触发了哪些的流程。

各主要浏览器内核标识如下:

  • Blink:Chrome 浏览器
  • Geckko:Firefox 浏览器
  • Webkit:Safari 浏览器
  • EdgeHTML:IE 最新版

1.4 浏览器渲染与 CSS 动画优化的关系

打开 Chrome 开发者工具,按 ESC,点击 Rendering,勾选 Paint flashing,此时页面会出现绿色部分闪烁,绿色闪烁代表重新渲染,就相当于在屏幕上这块区域重新画一下。

通过 JavaScript 定时器设置每秒移动固定位移实现的动画,对应元素一直在重新渲染。而通过设置 transform 实现的动画,在 Chrome 浏览器测试,元素只在起始位置渲染,优化了 CSS 性能。

CSS 动画优化经常采用以下方式

  • JS 优化:使用 requestAnimationFrame 代替 setTimeout 或 setInterval;
  • CSS 优化:使用 will-change 或 transform。

2. CSS 动画

每个静止的画面都是帧,连续的帧形成了动画。影视通常每秒 24 帧,游戏每秒 30 帧以上,有追求画面的游戏达到 120 ~ 140 帧甚至更高。

2.1 transform 变形

四个常用功能:

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

transform 一般需要配合 transition 使用。inline-block 元素不支持 transform,需要先变成 block。

针对 translateZ 的移动,需要设置 perspective(透视原点位置)才能观察到效果。如 perspective: 1000px,即透视原点位于距元素正中心 1000px 远处。

/* 通过 transform 实现绝对定位元素居中,不支持 IE */
 .demo {
     width: 200px;
     height: 200px;
     border: 1px solid red;
     position: absolute;
     left: 50%;
     top: 50%;
     /* 回半个身位 */
     /* transform: translateX(-50%) translateY(-50%);*/
     transform: translate(-50%, -50%);
 }

2.2 transition 过渡

  • 作用:补充中间帧
  • 语法: transition: 属性名 时长 过渡方式 延迟时间

可拆分设置 transition-property: width,background,transition-duration: 2s,10s。
可自定义贝塞尔曲线 cubic-bezier(0, 0.95, 1, 0.07)
延迟时间 transition-delay: 3s;
所有属性同样过渡 transition: all 2s;

  • 过渡方式:linear(线性) | ease(缓动)| ease in(缓入,从慢到快)| ease out(缓出,从快到慢)| ease-in-out(缓入缓出)| ...
  • display:none --> block 没办法过渡,一般改为 visibility:hidden --> visible
  • visibility: hidden 仍然占用域的空间
  • 过渡必须要有起始

2.3 animation 动画

声明关键帧,添加动画。@keyframes 两种写法。

.demo.strat {
    animation: moveExample 1s;
}

/* 写法一 */
@keyframes moveExample {
    from {
        transform: translateX(0%);
    }
    to {
        transform: translateX(100%);
    }
}

/* 写法二 */
@keyframes moveExample {
    0% {
        transform: translateX(0%);
    }
    30% {
        transform: rotate(45deg);
    }
    60%, 70% {
        transform: scale(1.2);
    }
    100% {
        transform: translateX(100%);
    }
}

animation 简写

  • animation: 时长 | 过渡方式 | 延迟 | 次数 | 方向 | 填充方式 | 是否暂停 | 动画名
  • animation-name: moveExample —— 动画名称
  • animation-duration: 3s —— 动画时间周期
  • animation-timing-function: ease —— 动画速度曲线
  • animation-delay: 1s —— 动画延迟时间
  • animation-iteration-count: 3(3次) | infinite(无限次)—— 动画循环次数
  • animation-direction: normal —— 运动的方向。

alternate交替运动(奇数次正常运动,偶数次反向),reverse 反向运动,alternate-reverse 奇数次反向偶数次正向运动。

  • animation-fill-mode: fowards; —— 使得动画保留最后一帧的效果

3. 小案例

  • 使用 transform、transition 和 :hover 实现会动的红心
  * {
    padding: 0;
    margin: 0;
  }
  .wrap {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }
  #heart {
    position: relative;
    transition: all 1s;
  }
  #heart:hover {
    transform: scale(1.2);
    transition: all 1s;
  }
  .left {
    position: absolute;
    bottom: 100%;
    right: 100%;
    width: 200px;
    height: 200px;
    background: red;
    border-radius: 50% 0 0 50%;
    transform: rotate(45deg) translateX(170px);
  }
  .right {
    position: absolute;
    bottom: 100%;
    left: 100%;
    width: 200px;
    height: 200px;
    background: red;
    border-radius: 50% 50% 0 0;
    transform: rotate(45deg) translateY(170px);
  }
  .bottom {
    width: 200px;
    height: 200px;
    background: red;
    transform: rotate(45deg);
  }
<body>
  <div class="wrap">
    <div id="heart">
      <div class="left"></div>
      <div class="right"></div>
      <div class="bottom"></div>
    </div>
  </div>
</body>
  • 使用 animation 实现会动的红心。
#heart {
  position: relative;
  animation: heart 0.7s ease infinite alternate;
}
@keyframes heart {
  0% {
    transform: scale(1);
  }
  100% {
    transform: scale(1.2);
  }
}

跳动红心