Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

大前端之动画(一) #8

Open
kekobin opened this issue Jul 17, 2019 · 0 comments
Open

大前端之动画(一) #8

kekobin opened this issue Jul 17, 2019 · 0 comments

Comments

@kekobin
Copy link
Owner

kekobin commented Jul 17, 2019

写在前面

我们都直到前端的动画分为css3动画和js动画,有些动画可以纯css3实现,有些可以纯js实现,还有一些可以结合两者一起实现。
接下来会分别说明css3动画和js动画的不同用法,和具体的使用案例。

一 css3动画

css3动画分为补间动画和帧动画。
其中,补间动画只有开始和结束两种状态,补齐中间的动画(即中间是一个渐变的过程);帧动画不仅有开始和结束状态,还可以用关键帧来定义中间的状态,做出比较复杂的动画。

transition(过渡)

为补间动画,先来看看它的用法:

transition:transition-property  transition-duration  transition-timing-function  transition-delay

其它属性值没什么好讲的,主要看看transition-property。它可以是三种值:all(任何属性改变都会应用该过渡效果)、none、某个具体属性(该属性在触发过渡的前后有变化才会应用过渡效果)。
很明显可以看到,这种动画是必须要有触发的,其触发方式如下:

  • 伪类触发::hover : focus :checked :active
  • js触发:toggleClass

具体示例,可查看transition.html

注意:transition-property只有在过渡的前后样式中都有设置,才能实现双向的过渡效果,即怎么过渡过来的怎么过渡回去。如果transition中有一个transition-property没有过渡前后都设置,则只能实现单向的过渡效果,返回去的会是生硬的效果。

animation(动画)

animation为帧动画,一般结合@Keyframes使用,先来看看它的用法:

@keyframes anime {
  from {}
  to {}
}

可以是用from to,也可以使用百分比的形式。
而animation常见的使用方式如下:

animation: animation-name  animation-duration  animatino-timing-function  animation-delay animation-iteration-count animation-direction animtion-play-state  animation-fill-mode

其中,animation-name就是上面@Keyframes的anime。
animation-iteration-count: 定义动画的播放次数,其通常为整数,默认是1,;取值为infinite,动画将无限次的播放。
animation-direction:主要用来设置动画播放方向,normal每次循环都是顺序播放,alternate则是正反正反播放。
animtion-play-state:属性是用来控制元素动画的播放状态。runninig(重新播放)、paused(暂停).
animation-fill-mode: 控制动画结束后元素的样式.none(回到最初)、forwards(停留在结束时的状态)、backwords(动画第一帧)、both(轮流应用forwards和backwards).

具体示例,可查看animation.html

注意:如果@Keyframes中使用的是百分比,则每一个百分比后面的属性动画,都是从0s开始的,如 %5、20%、50%、100%,动画总共10s,那么每个阶段的执行状况是: 0s-0.5s、0s-2s、0s-5s、0s-10s。

由上面可以看出transition(用A代替)和animation(用B代替)的差异:

  • A需要借助交互,B既可以自动播放,也可以借助交互;
  • A只有开始和结束状态,B即有开始和结束状态,中间还能定义多个帧动态;
  • A不可以控制暂停和播放,B可以控制暂停和播放;
  • A只能通过transition-timing-function定义固定的几种效果,B的效果通过帧变得多样;
  • A是一次性的,不能重复发生,除非一再触发,B可以无限动画。

不过,B消耗的资源会比较多,所以能使用A的情况下尽量使用A。

硬件加速

我们都知道,css3动画相对于js动画有几个优势:

  • 不占用JS主线程
  • 可以开启硬件加速

其中的硬件加速又叫做 GPU 加速,是利用 GPU 进行渲染,减少 CPU 操作的一种优化方案。由于 GPU 中的 transform 等 CSS 属性不会触发 repaint,所以能大大提高网页的性能。
下面的示例用来显示没开启硬件加速和开启硬件加速的效果对比,从中可以看到两者差别明显:
transform-3d.html

所以使用css3实现复杂动画时,可以适当开启硬件加速,能够开启这个特性的属性包含下面几个:

  • transform
  • opacity
  • filter
  • will-change

而现实很多动画场景并不会使用到这些属性,那怎么办呢?其实还是有办法去诱导浏览器开启加速的:

transform: translateZ(0); 
// or 
transform: translate3d(0, 0, 0);
// or 
transform: rotateZ(360deg);

注意:

  • 过多地开启硬件加速可能会耗费较多的内存,因此什么时候开启硬件加速,给多少元素开启硬件加速,需要用测试结果说话。
  • GPU 渲染会影响字体的抗锯齿效果。这是因为 GPU 和 CPU 具有不同的渲染机制,即使最终硬件加速停止了,文本还是会在动画期间显示得很模糊。

二 JS动画

css动画一般用来实现比较简单的“一次性转换”,为UI元素转换比较小的独立状态。例如从模态框从底部弹出,logo无限循环转动等。 要实现高级效果时,例如弹跳,加速,减速等比较复杂的动画,则使用JS动画。
而JS动画分为匀速动画和缓动动画,下面就详述两种动画的区别和使用场景!

先来了解一些基础知识:
动画的实现原理,是利用了人眼的“视觉暂留”现象,在短时间内连续播放数幅静止的画面,使肉眼因视觉残象产生错觉,而误以为画面在“动”。
动画相关的几个概念:

  • 帧:在动画过程中,每一幅静止画面即为一“帧”。
  • 帧率:即每秒钟播放的静止画面的数量,单位是fps(Frame per second)。
  • 帧时长:即每一幅静止画面的停留时间,单位一般是ms(毫秒)。
  • 跳帧(掉帧/丢帧):在帧率固定的动画中,某一帧的时长远高于平均帧时长,导致其后续数帧被挤压而丢失的现象。
    由于浏览器渲染刷新频率为60fps,所以帧率稳定在60fps左右的流畅动画最让人舒适。

匀速动画

匀速动画就是所谓的线性运动,符合类似 s = vt 公式的计算方式。虽然没有缓动带给人的效果舒适,但在很多场景中还是会用到。
按照前面"基础知识"的概念,现在我们来模拟一下动画的形成过程:
首先,设置一个正方形的方块作为目标,按钮用来触发方块移动:

<button id="btn">start</button>
<div id="box" style="width:100px;height:100px;background:green;position:absolute;left: 30px;top:50px;"></div>

我们的目标是:让这个方块以动画的方式向右移动到300。
当然,最简单的方式肯定是直接设置它的left属性为300px了,但是这样子是没有任何动画效果的,很僵硬的体验。
前面说了,"动画"是由一帧一帧的静止画面组成的,所以我们来产生这个一帧帧的画面:

// 引入"步长"的概念,即每一步走的距离,相当于隔多久出来一张静态画面。这里我们设置为10(当然也可以是别的标准,不过一般都是10),那么我们需要走的步数就是 (300-30)/10=27   
const step = 10;

每一帧的画面出来了,那么所有帧的画面行程就可以通过定时器来完成了。我们先假设每一帧画面花费的时间为 400:

const btn = document.getElementById('btn')
const el = document.getElementById('box')
const step = 10;

btn.addEventListener('click', function() {
       // 每隔400ms向右移动10px,相当于每隔400ms出来一张静止画面,也即"一帧"
	setInterval(function() {
		el.style.left = el.offsetLeft + step + 'px';
	}, 400)
})

可以很明显的看到,方块在一帧帧的向右移动,但我们的肉眼能看出每一帧中间有明显的卡顿。现在我们连续的画面有了,之所以出现肉眼能见的卡顿,很明显是因为我们设置的时间还不够短,当画面出来的频率(也即“刷新频率”)快到我们肉眼看不出来画面与画面之间的切换的时候,即“动画”。让我们把每一帧画面花费的时间设置为40看看:

setInterval(function() {
	el.style.left = el.offsetLeft + step + 'px';
}, 40)

哇,瞬间舒适多了!整个的动画效果就出来了。
不过还有个问题:我们是希望的是向右动画到300位置,而现在是没有限制的,所以要加上这个条件,也很简单,如下:

setInterval(function() {
	// 如果el距离最左边的距离跟300比都小于步长了,说明已经基本接近300那个位置了,即设定el位置为300
	if (Math.abs(300 - el.offsetLeft) < step) {
		el.style.left = 300 + 'px';
	} else {
		el.style.left = el.offsetLeft + step + 'px';
	}
}, 40)

这样,一个匀速动画的实现过程就完成了。
总结来看:“动画”的核心,其实是每一帧画面如何生成,以及"刷新频率"的设定。
综合实例,可以查看slider轮播的实现

由于篇幅有点长,“缓动动画”部分放到下一篇讲解:
大前端之动画(二)
参考:
Web 性能优化-CSS3 硬件加速(GPU 加速)
使用css3实现动画来开启GPU加速
Web动画性能指南

@kekobin kekobin changed the title 大前端之动画 大前端之动画(一) Jul 19, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant