相信你們在平時的需求當中,都會遇到一些動畫需求,那麼你們又是如何抉擇實現方式呢?
這裏我大概分爲4種狀況,gif圖/animation/ae導出骨骼動畫/ae導出canvasjavascript
沒錯!最簡單的就是讓咱們的設計師直接給咱們gif圖,那麼在咱們前端看來就是一張圖片的問題,只需控制展現和隱藏便可。簡直是最舒服的方式了。
那麼gif圖播放動畫有什麼缺陷呢?css
假設如今有這樣的一個場景:
在一個列表裏,當用戶點擊每個列表中的單項,都在該單項中間出現一個綻開的煙花。前端
簡單啊!立刻寫給你!java
export class Item extends Component { timer: NodeJS.Timeout playing() { if (this.timer) { clearTimeout(this.timer); } this.setState({ fire: true }); this.timer = setTimeout(() => { this.setState({ fire: false }); }, 1500); } render() { return ( <div> <div>我要放煙花!</div> { this.state.fire ? <img src="fire.png「 /> : null } </div> ); } } export class List extends Component { render() { return ( <div> { dataList.map((data) => { return <Item key={data.id} /> }) } </div> ); } }
假設咱們如今的煙花gif是1500ms循環播放,那麼用戶按照列表順序點擊item現象是什麼?canvas
那麼咱們必定要使用gif圖來實現呢,可不能夠呢?
能夠。因爲同一張gif圖瀏覽器只會保存一個播放進度,致使不一樣時間開始的gif圖都是第一張播放的進度。那麼咱們只須要讓這一張煙花gif變成多張gif就能夠。瀏覽器
export class Item extends Component { ... render() { return ( <div> <div>我要放煙花!</div> { this.state.fire ? <img src={`fire.png?${this.props.id}`} /> : null } </div> ); } } export class List extends Component { render() { return ( <div> { dataList.map((data) => { return <Item key={data.id} id={data.id} /> }) } </div> ); } }
在gif後面加個惟一的id,那麼每個item上都是一張不一樣的gif圖,這樣每一個煙花的播放進度各自維護,不會共享,上述問題就解決了。
可是引來了另一個問題,由於加了id,若是列表有10個單項,就會加載10張fire.png,因此若是是這樣的場景,我不推薦你們使用gif圖實現了。動畫
相信你們都已經使用過@keyframe
和animation
實現過動畫了,該功能強大無比,能實現絕多數簡單的動畫需求。
那麼咱們依舊是拿煙花的例子來分析,既然gif圖片有多個播放進度共享的問題,那麼使用animation切換background-position是否是ok了。this
是的,咱們讓設計師將煙花導出一個序列幀,咱們利用step進行位移背景,能夠播放動畫設計
$frame: 20; $ratio: 100% / $frame; @keyframes playing { @for $i from 0 to $frame { #{$i * $ratio} { background-position-x: -88px * $i; } } 100% { background-position-x: 0; } } .fire { width: 88px; height: 88px; background: fire.png; background-size: 88px; &.play { animation: playing 1500ms step-end } }
是否是也很簡單!
不是!這裏仍是有坑的!
你們想一想,序列圖跟background-position改變的方向是同一個方向仍是不一樣方向好?(ps:序列圖是左往右,background-position-x改變是同向。background-position-y改變是不一樣向)code
答案是不一樣向的沒問題。
在我一開始接觸一個需求咱們使用以上方式進行實現的時候,鬱悶地發現動畫播放的時候在一些機型上會左右抖動,立刻就知道了是因爲編譯轉化成rem單位,致使在一些寬度的機型上rem轉化爲px的時候存在無敵的小數點取捨,致使上一幀向左偏移了1px,下一幀向右偏移了1px。
因此個人第一個想法就是不讓編譯幫忙轉化成rem了,可是這樣作的話不一樣機型又不能作到適配。
後面將序列幀的方向改爲了縱向的試試,驚奇地發現不抖動了。
忽然一道閃電閃過,想明白了,原來是background-size在做祟。由於咱們的序列幀圖片比較長,因此咱們要設置background-size爲圖片大小,而後改變background-position-x來實現動畫。
因爲我將序列幀改爲了縱向排列
$frame: 20; $ratio: 100% / $frame; @keyframes playing { @for $i from 0 to $frame { #{$i * $ratio} { // 縱向改變位置 background-position-y: -88px * $i; } } 100% { background-position-y: 0; } } .fire { width: 88px; height: 88px; background: fire.png; background-size: 100%; &.play { animation: playing 1500ms step-end } }
那麼此時咱們鎖定的是圖片的寬度,而改變的是縱向的位置,那麼問題就獲得瞭解決。
當動畫很是複雜的時候,好比咱們要實現一個豬的走動,咱們要在豬中止走動的的時候中止頭的晃動,在豬走路的時候讓他的頭晃動起來。利用序列幀使用上述animation也可也實現,可是畢竟序列幀圖片較大,可是有沒有更節省資源的方式呢。
設計師能夠利用ae導出一隻狗的分部位的動畫,就是豬的頭/身體/四肢/尾巴都是拆開的素材,這幾個部位各自旋轉/位移/縮放,組合在一塊兒便可完成一隻豬。
這種方式基本上將設計師的素材拿過來修修補補就能夠了,並且動畫效果很好,缺點就是transform能實現的效果以外的效果就不能使用了,好比形變。
ae導出canvas須要配合lottie庫使用,工做量也都是在設計師那邊。