首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >疯狂操作 CSS3 实现 60 FPS 动画效果,CodeReview 时同事直呼:细节!

疯狂操作 CSS3 实现 60 FPS 动画效果,CodeReview 时同事直呼:细节!

作者头像
掘金安东尼
发布2022-09-22 12:11:18
发布2022-09-22 12:11:18
7290
举报
文章被收录于专栏:掘金安东尼掘金安东尼

theme: awesome-green

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

前言小叙

FPS 全称:Frames per second,即 【每秒帧数】 的意思。

通常来讲,当动画的每秒帧数低于 12 (即 12 FPS 以下)时,我们的大脑就能快速从动画中区分出一些静止的图片,所以此时的动画并不是无缝动画。

一旦播放速率(每秒帧数)达到 16-24 FPS 时,大脑就会认为这些画面是连续移动的场景,看起来就是影片的效果了(大部分数字电影拍摄是每秒 24 帧)。

所以,我们可以大致了解到:FPS 越高,画面越流畅!

明白这些后,我们可以便可开始本篇正题了:**CSS3 实现 60 FPS 的动画效果!**

常规操作

本瓜知道你会说:“CSS3 动画,有手就行!”

知道我们的目标是实现下图动画效果时,

你信手拈来,一顿操作!

很棒,恭喜你能轻松写出以下这段超级常规的 CSS3 动画~

代码语言:javascript
复制
<div class="layout">
    <div class="app-menu"></div>
    <div class="header">
        <div class="menu-icon"></div>
    </div>
</div>

.app-menu {
    left: -300px;
    transition: left 300ms linear;
}

.app-menu-open .app-menu {
    left: 0px;
    transition: left 300ms linear;
}

它的实现效果:

你确实做到了,但,到此为止吗?这次,不止于此!

于是,咱打开控制台 Performance Ctrl+E 进行录制这段动画来进一步分析分析

我们可以看到它的 TimelineFPS 指标(绿色部分)

  • 图中绿色部分的高点表示以 60 FPS 进行渲染,低点则表示以低于 60 FPS 进行渲染;
  • 红色条表示卡顿;

我们发现它存在问题:

1. 绿色部分的高度并不一致,说明 FPS 不稳定;

2. 绿色部分有相当一部分处在低点,说明 FPS 不流畅;

3. 存在很多红色条,说明动画很卡;

所以,结论是:我们这种方法实现的 CSS3 动画,并不流畅!!

我们期望的是:高度齐平,绿色都处于高点,红色条越少越好。 别方,带着期望,继续往下看!

进阶操作

你非常棒,并且异于常人!!你想到了使用 Transform 来实现!!

CSS 代码:

代码语言:javascript
复制
.app-menu {
    -webkit-transform: translateX(-100%);
            transform: translateX(-100%);
    transition: transform 300ms linear;
}
.app-menu-open .app-menu {
    -webkit-transform: none;
            transform: none;
    transition: transform 300ms linear;
}

实现效果:

控制台看下 Timeline:

的确不一样了,跟【常规操作】中的 Timeline 图作比,高度都更加齐平了!更多绿色部分处于高点了!红色条也减少了!

这表示我们的动画效果,更流畅了!

  • 这里动画效果为什么更流畅了呢?暂时按下不表,后文解释。高级操作

噢噢噢,原来你是位高级前端,你还会这样 will-change 这样高级的操作!

CSS 代码:

代码语言:javascript
复制
.app-menu {
    -webkit-transform: translateX(-100%);
            transform: translateX(-100%);
    transition: transform 300ms linear;
    will-change: transform;
}

实现效果:

控制台看下 Timeline:

天呐噜!

相较于【进阶操作】,我们更加接近目标:高度基本齐平了!绿色位置绝大部分都处在高位!红条减的更少了!

这么厉害的嘛?不妨再往下看!

顶级操作

至此,你的手中还有牌吗?

或许我们还能从 DOM 结构找找突破口!

将前文的 DOM 改造成:

代码语言:javascript
复制
<div class="menu">
    <div class="app-menu"></div>
</div>
<div class="layout">
    <div class="header">
        <div class="menu-icon"></div>
    </div>
</div>

在 layout 区域之外创建 menu,然后使用 js 中的 transitionend 函数来监听,使 menu--animatable 类在过渡时间结束时被移除。

// js

代码语言:javascript
复制
function toggleClassMenu() {
    myMenu.classList.add("menu--animatable");	
    if(!myMenu.classList.contains("menu--visible")) {		
        myMenu.classList.add("menu--visible");
    } else {
        myMenu.classList.remove('menu--visible');		
    }	
}

function OnTransitionEnd() {
    myMenu.classList.remove("menu--animatable");
}

var myMenu = document.querySelector(".menu");
var oppMenu = document.querySelector(".menu-icon");
myMenu.addEventListener("transitionend", OnTransitionEnd, false);
oppMenu.addEventListener("click", toggleClassMenu, false);
myMenu.addEventListener("click", toggleClassMenu, false);

// 完整 css

代码语言:javascript
复制
menu {
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
    pointer-events: none;
    z-index: 150;
}

.menu--visible {
    pointer-events: auto;
}

.app-menu {
    background-color: #fff;
    color: #fff;
    position: relative;
    max-width: 400px;
    width: 90%;
    height: 100%;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
    -webkit-transform: translateX(-103%);
            transform: translateX(-103%);
    display: flex;
    flex-direction: column;
    will-change: transform;
    z-index: 160;
    pointer-events: auto;            
}

.menu--visible .app-menu {
    -webkit-transform: none;
            transform: none;
}

.menu--animatable .app-menu {
    transition: all 130ms ease-in;
}

.menu--visible.menu--animatable  .app-menu {
    transition: all 330ms ease-out;
}

.menu:after {
    content: '';
    display: block;
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.4);
    opacity: 0;
    will-change: opacity;
    pointer-events: none;
    transition: opacity 0.3s cubic-bezier(0,0,0.3,1);
}

.menu--visible.menu:after {
    opacity: 1;
    pointer-events: auto;
}

它的 Timeline:

这就是 CSS3 实现 60 FPS动画的顶级操作!

高度全部齐平!!全部处在高位!!没有红条!!

这是完美的 FPS 动画!如丝般顺滑!

你做到了!(๑•̀ㅂ•́)و✧

原理探寻

我们在此做简单回顾:

级别

实现核心方式

FPS 绿块效果

动画顺滑程度

常规操作

transition: left 300ms linear;

高度不齐平、较多处于低点、很多红条

30% 顺滑

进阶操作

transition: transform 300ms linear;

高度比较齐平、较少处于低点、较少红条

60% 顺滑

高级操作

will-change: transform;

高度基本齐平、很少处于低点、很少红条

80% 顺滑

顶级操作

transitionend 函数

高度完全齐平、全部处于高点、没有红条

100% 顺滑

原理呢?原理还得回归底层,来看看浏览器的渲染机制!

上图是浏览器渲染的关键步骤,相信大家都很熟悉:

  1. Styles(样式): 浏览器计算要应用于元素的样式;
  2. Layout(布局):浏览器计算每个元素生成形状和位置,比如 width、height、margin、left/top/right/bottom 这些;
  3. Paint(渲染):浏览器将每个元素的像素填充到图层中;
  4. Composite(合成):浏览器开始在屏幕上绘制所有图层的时候;

第 4 步“合成”给了我们很多操作空间:

我们可以使用 transformopacity 两个属性实现四种最常用的动画:

  1. 位置动画:translateX(n) translateY(n) translateZ(n);
  2. 大小动画:transform: scale(n);
  3. 旋转动画:transform: rotate(ndeg);
  4. 不透明度动画:opacity: n;

在第 4 步操作为什么具有魔法呢?

笨蛋,当然是避免重绘、回流增加渲染负担啦~

所以,我们的【进阶操作】控制 transform 比【常规操作】控制 left 属性更优!

而【高级操作】中的 will-change 能告知浏览器元素会有哪些变化,这样浏览器能在变化之前做好优化准备工作,将一部分复杂计算提前准备好,所以页面的反应更加快速灵敏。

【顶级操作】中改造分离 DOM,然后用 JS控制,手动添加 menu--animatable 类,然后用到 transitionend 事件 会在 CSS transition 结束后触发,移除 menu--animatable 类。实现完全体的 60FPS 动画!

通常来说,我们能做到进阶操作或高级操作中的实现应该就够了,如果在特殊情景下,如对 FPS 要求特别高,我们再开拓思路,启用顶级操作!(顶级操作是把宝刀,一般不拿出来~)

写在结尾

我们都知道编码习惯非常重要,但同时又一直苦于思考,如何将一些原理知识与实际编码结合?!

而本篇是基于浏览器渲染原理,对 CSS3 动画的一个很好结合实践!

CodeReview 时,同事都直呼:细节!

“秒啊~ 我理解的原理知识用来面试,你理解的原理知识用来工作中日常编码,细节!高级!”

ok,以上便是本次分享~ 都看到这儿了,撰文不易、需要鼓励~ b( ̄▽ ̄)d

我是掘金安东尼,输出暴露输入,技术洞见生活,下期再会~

参考:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • theme: awesome-green
  • 前言小叙
  • 常规操作
  • 进阶操作
  • 顶级操作
  • 原理探寻
  • 写在结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档