摘要:如上面代码,我们实现了一个连续的轨迹动画,它效果如下图所示:。然后我们把它和前面的轨迹动画结合一下,就实现了扇翅膀飞行的效果。

前几天我们在文章中讨论了 如何优雅地实现轨迹动画 轨迹动画,只是最简单的一类动画,可以用CSS也可以用JS来实现。

实际上轨迹动画只是最简单的一类动画。在今天,我们继续来看一看Web上动画究竟能做到什么程度。

更复杂的轨迹动画

假设我们要让一个物体连续地经过固定的一系列点,完成一个完整的动画,那么我们可以用Promise来简单实现:

function lerp(start, end, p) {

return start  * ( 1 - p ) + end  * p ;

}

function animate ( target , duration , progress ) {

const startTime  = Date . now ( ) ;

return new Promise ( ( resolve ) = > {

function update ( ) {

const= Date . now ( ) - startTime ;

const= Math . min (/ duration , 1 ) ;

progress ( target , p ) ;

if (< 1 ) {

requestAnimationFrame ( update ) ;

} else {

resolve ( p ) ;

}

}

update ( ) ;

} ) ;

}

const spots  = Array . from ( document . querySelectorAll ( '.target' ) )

. map (= > [ t . offsetLeft , t . offsetTop ] ) ;

( async function ( ) {

// noprotect

for ( let= 0 ;< spots . length ; i ++ ) {

let left  = bird . offsetLeft ;

let top  = bird . offsetTop ;

await animate ( bird , 1000 , ( target , p ) = > {

const= lerp ( left , spots [ i ] [ 0 ] , p ) ;

const= lerp ( top , spots [ i ] [ 1 ] , p ) ;

bird . style . left  = ` ${ x } px` ;

bird . style . top  = ` ${ y } px` ;

} ) ;

}

} ( ) ) ;

如上面代码,我们实现了一个连续的轨迹动画,它效果如下图所示:

不过这个动画实际上是一段折线动画,它虽然连续,但是不平滑。如果我们要想保证连续且平滑,我们可以用贝塞尔曲线函数拟合。

我们用一下连续的二阶贝塞尔曲线:

function lerp(start, end, p) {

return start  * ( 1 - p ) + end  * p ;

}

function quadraticBezier ( [ x0 , y0 ] , [ x1 , y1 ] , [ x2 , y2 ] , p ) {

const= 1 - p ;

const= x0  *** 2 + 2 * x1  **+ x2  *** 2 ;

const= y0  *** 2 + 2 * y1  **+ y2  *** 2 ;

return [ x , y ] ;

}

function animate ( target , duration , progress ) {

const startTime  = Date . now ( ) ;

return new Promise ( ( resolve ) = > {

function update ( ) {

const= Date . now ( ) - startTime ;

const= Math . min (/ duration , 1 ) ;

progress ( target , p ) ;

if (< 1 ) {

requestAnimationFrame ( update ) ;

} else {

resolve ( p ) ;

}

}

update ( ) ;

} ) ;

}

const spots  = Array . from ( document . querySelectorAll ( '.target' ) )

. map (= > [ t . offsetLeft , t . offsetTop ] ) ;

// noprotect

( async function ( ) {

let p0  = [ bird . offsetLeft , bird . offsetTop ] ,

p1 = spots [ 0 ] ,

c0 = [ 0.5 * ( p0 [ 0 ] + p1 [ 0 ] ) ,

0.5 * ( p0 [ 1 ] + p1 [ 1 ] ) ] ;

await animate ( bird , 1000 , ( target , p ) = > {

const [ x , y ] = quadraticBezier ( p0 , c0 , p1 , p ) ;

bird . style . left  = ` ${ x } px` ;

bird . style . top  = ` ${ y } px` ;

} ) ;

for ( let= 1 ;< spots . length ; i ++ ) {

p0 = p1 ;

c0 = [ p1 [ 0 ] * 2 - c0 [ 0 ] ,

p1 [ 1 ] * 2 - c0 [ 1 ] ] ;

p1 = spots [ i ] ;

await animate ( bird , 1000 , ( target , p ) = > {

const [ x , y ] = quadraticBezier ( p0 , c0 , p1 , p ) ;

bird . style . left  = ` ${ x } px` ;

bird . style . top  = ` ${ y } px` ;

} ) ;

}

} ( ) ) ;

效果如下图所示:

我们看到,飞鸟是平滑地经过这些点的,但是,它的轨迹偏离点的幅度有点大,尤其是第三个点到第四个点,要解决这个问题,可以改用三阶贝塞尔曲线,或者用Catmull Rom样条来做插值。

总之我们还有其他办法改进动画效果,让它变得更完美,在这里限于篇幅就不一一赘述了。

帧动画

上面的动画有一个遗憾,就是鸟在飞的过程中翅膀没有动,这可以通过帧动画来实现。

帧动画比固定轨迹动画更简单,我们只要用CSS将元素的背景切换一下就可以了。

@keyframes flap {

0%  {

background-position : 0 0 px ;

}

33%  {

background-position : 0 - 62 px ;

}

66%  {

background-position : 0 - 124 px ;

}

100%  {

background-position : 0 0 px ;

}

}

#bird {

position : absolute ;

background-image : url(https://p1.ssl.qhimg.com/t01dc66725cf859e983.png) ;

width : 86 px ;

height : 60 px ;

background-position : 0 - 62 px ;

transform : translate ( - 50% ,- 50% ) scale ( 0.5 ) ;

z-index : 100 ;

animation : flap  .5 s step-end infinite ;

}

我们给飞鸟的元素加一个切换背景 background-position 的关键帧动画,让这个动画的缓动函数设为 step-end ,这样就实现了飞鸟的扇翅膀动画。

然后我们把它和前面的轨迹动画结合一下,就实现了扇翅膀飞行的效果。

gradient 和 mask 动画

CSS的渐变和遮罩,是非常有用的两个属性,用它们配合,可以实现各种比较好玩的动画。

比如我们在前面介绍CSS gradients这篇文章中,就介绍过遮罩实现百叶窗渐显的效果。

像素动画

要实现更加复杂的动画,我们还可以用Canvas2D或者WebGL直接操作图像的像素,对像素应用动画。这个就更加复杂了,但是能实现更有趣的效果。

Canvas2D因为CPU处理能力的限制,处理不了太复杂的效果。但是用WebGL的shader,就可以处理更加炫酷的效果了,比如类似下面的水纹滤镜:

还有更加炫酷的效果可以做出来哦。

这些动画效果有没有让你觉得惊艳?

这些效果都可以实现,它的基础是以下这些:

  • JavaScript固定轨迹动画和连续动画

  • CSS特殊属性的特殊动画

  • Canvas的像素操作

  • WebGL的FragmentShader

这些内容的前两部分,在课程《前端进阶十日谈》中有详细介绍,后两部份在不久后也会有专门的课程来讲解的。

关于动画,还有什么想要讨论的,欢迎给我们留言。更多内容,尽在《前端进阶十日谈》

相关文章