CSS动画学习心得

994 阅读4分钟

浏览器渲染原理

在实际开始学习CSS动画之前,需要先对浏览器渲染有个简单的了解

浏览器渲染过程

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

三棵树.png

三种更新方式

  1. JS / CSS > 样式 > 布局 > 绘制 > 合成

更新方式1.png

如果修改了元素的“layout”属性,也就是改变了元素的几何属性(例如宽度、高度、左侧或顶部位置等),那么浏览器将必须检查所有其他元素,然后“自动重排”页面。任何受影响的部分都需要重新绘制,而且最终绘制的元素需进行合成。

  1. JS / CSS > 样式 > 绘制 > 合成

更新方式2.png

如果修改了“paint only”属性(例如背景图片、文字颜色或阴影等),即不会影响页面布局的属性,则浏览器会跳过布局,但仍将执行绘制。

  1. JS / CSS > 样式 > 合成

更新方式3.png

如果更改了一个既不要布局也不要绘制的属性,则浏览器将跳到只执行合成。这个最后的版本开销最小,最适合于应用生命周期中的高压力点,例如动画或滚动。

注意:如果想知道更改任何指定 CSS 属性将触发上述三个版本中的哪一个,可以查看CSStriggers

以上图文,来自渲染树构建、布局及绘制

一个简单的例子

怎么实现div从左往右移动,有三种方法:

左移1.gif

left

left实现示例 JS代码

var n = 1
var id = setInterval(() => {
  console.log(n)
  if (n <= 100) {
    demo.style.left = n / 100 * 300 + 'px'
    n = n + 1
  }else{
    clearInterval(id) //n到200取消动画
  }
}, 1000 / 30)  //30帧

原理:每过一段时间(用setInterval实现)将div移动一小段距离,直到移动到目标地点

但我们一般不会使用left做动画,如图

left左移渲染.gif

绿色表示重新绘制(repaint),此过程便是上述三种更新方式的第一种,它需要走完布局,绘制,合成,导致动画的性能不好。

注意:查看渲染的方式

  1. 打开开发者工具
  2. 键盘"ESC"键
  3. 新窗口左边三个点,点击Rendering
  4. 勾选Paint flashing
  5. 刷新动画查看

transform + translate

transform实现示例

#demo{
  width: 100px;
  height: 100px;
  border: 1px solid red;
  transition: all 1s linear; /* 过渡属性,可以自动补充中见帧 */
}

#demo.end{
  transform: translateX(300px);
}
setTimeout(()=>{
  demo.classList.add('end')
},0)

原理:通过 transform:translateX(0==>300),运用transition过渡属性自动补齐中间帧

如图所示:

transform左移渲染.gif

此方法没有repaint(重新绘制),而是直接合成,所以它的性能较好

animation

animation实现示例 此方法可以仅用CSS实现动画

#demo {
  width: 100px;
  height: 100px;
  border: 1px solid red;
  animation:run 4s forwards;
}

@keyframes run {
  0% {transform:none;} 
  100% {transform:translateX(300px);}
}

原理:定义动画的名字run,并写明运动过程0%与100%方框的位置,便可通过animation来实现方框运动

此方法与transform一样,拥有较好的性能

transform总结

完整指南->transform-MDN

translateZ

  • transform: translateZ()
    • 我们把X作为横轴,Y作为纵轴,那么Z便是从屏幕方向指向我们的轴
    • 此用法直接写看不出效果
    • 举个例子:

Z轴.gif

#demo:hover{
  transform: translateZ(-200px);/* 靠近原点 */
}
 .wrapper{
  perspective: 1000px; /* 透视图原点所在位置 */
  border: 1px solid black;
} 

我们需要给demo添加一个父容器wrapper,并给出perspective: Xpx;,它表示我们构建的一个坐标轴原点距离屏幕有X个px,这时我们便可以看到translateZ的效果:

鼠标经过,它在以Z轴方向往原点靠拢 Z轴示例

transition总结

完整指南->transition-MDN

  • 作用:补充中间帧
  • 语法:transition:属性名 时长 过渡方式 延迟
    • 可以用all代表所有属性

并不是所有属性都能用transition

如display:block->none 没法过渡 如何实现动画消失的效果?

方框消失.gif

  1. opacity:1->0
#demo {
  width: 100px;
  height: 100px;
  border:1px solid red;
  transition: all 1s;
   opacity: 1;
}

#demo.end {
  width: 200px;
  height: 200px;
  opacity: 0; 
}
x.onclick = ()=> {
  demo.classList.add('end');
}

demo.addEventListener('transitionend', function(){
  console.log(1)
  demo.remove()
}) //删掉原有位置

注意:opacity从0变为1,只是看不见,但它原有的位置还在,我们需要使用上述JS代码删除其原有位置

  1. visibility:hidden->visible 将上述代码opacity部分替换为visibility即可

方框消失示例

animation总结

完整指南->animation-MDN

缩写语法:
animation:时长 过渡方式 延迟 次数 方向 填充方式 是否暂停 动画名;

以上所有属性都有对应的单独属性

如何让动画停在最后一帧?

在animation加上一个forwards即可

@keyframes完整语法

keyframes-MDN

第一种

@keyframes xxx {
  from {transform:none;}
  
  to {transform:translateX(100px);}
}

第二种

@keyframes xxx {
  0% {transform:none;}
  50%{transform:translateX(50px)}
  100% {transform:translateX(100px);}
}

中间点

有了以上总结,我们可以实现一个稍微复杂的例子:

中间点transform.gif

使用两次transform

transform中间点示例

#demo.b{
  transform: translateX(200px);
}
#demo.c{
  transform: translateX(200px) translateY(100px);
}

button.onclick = ()=>{
  
  demo.classList.add('b')
  setTimeout(()=>{
    demo.classList.remove('b')
    demo.classList.add('c')
  },1000)
}

我们通过JS和按钮,可以实现先将方框移动到b的位置,在以b的基础移动到c的位置

使用animation

animation中间点示例
使用animation,不仅可以实现中间点,还可以实现往复,暂停,继续的功能:

中间点animation1.gif

#demo.start{
  animation: xxx 1.5s infinite alternate;
}


@keyframes xxx {
  0% {
    transform: none;/*  第1帧位置 */
  }
  66.66%{
    transform: translateX(200px);/*  第2帧位置 */
  }
   100%{
    transform: translateX(200px)      translateY(100px);/*  第3帧位置   */     
  } 
}

infinite表示动画无限次播放 alternate让动画往复运动

button.onclick = ()=>{
  demo.classList.add('start')
}
paused.onclick = ()=>{ 
  demo.style.animationPlayState = 'paused'
}
running.onclick = ()=>{
  demo.style.animationPlayState = 'running'
}

我们通过JS运用animation的animationPlayState属性值,即可实现动画的暂停与继续

其他补充

实现一颗跳动的心

transform版红心
animation版红心

CSS绝对居中的方法

CSS绝对居中