摘要:如上面代碼,我們實現了一個連續的軌跡動畫,它效果如下圖所示:。然後我們把它和前面的軌跡動畫結合一下,就實現了扇翅膀飛行的效果。

前幾天我們在文章中討論了 如何優雅地實現軌跡動畫 軌跡動畫,只是最簡單的一類動畫,可以用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

這些內容的前兩部分,在課程《前端進階十日談》中有詳細介紹,後兩部份在不久後也會有專門的課程來講解的。

關於動畫,還有什麼想要討論的,歡迎給我們留言。更多內容,盡在《前端進階十日談》

相關文章