當遇到動畫需求的時候該使用什麼方式實現呢

背景

相信你們在平時的需求當中,都會遇到一些動畫需求,那麼你們又是如何抉擇實現方式呢?
這裏我大概分爲4種狀況,gif圖/animation/ae導出骨骼動畫/ae導出canvasjavascript

1. gif圖硬核解決

沒錯!最簡單的就是讓咱們的設計師直接給咱們gif圖,那麼在咱們前端看來就是一張圖片的問題,只需控制展現和隱藏便可。簡直是最舒服的方式了。
那麼gif圖播放動畫有什麼缺陷呢?css

  1. 在添加一些效果的時候會出現鋸齒,而且色值不豐富;
  2. 若是在同一時間存在多個gif圖並行播放,這時候就有問題了。下面咱們來看一下是什麼問題

假設如今有這樣的一個場景:
在一個列表裏,當用戶點擊每個列表中的單項,都在該單項中間出現一個綻開的煙花。前端

簡單啊!立刻寫給你!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

  1. 當用戶點擊了第一個item,此時第一個item的煙花綻開;
  2. 用戶在500ms後點擊了第二個item,此時第二個item上的煙花會和第一個item的煙花保持同樣的節奏,也就是第二個item的煙花從500ms開始播放,持續時間1000ms;
  3. 用戶在1000ms後點擊了第三個item,此時第三個item的煙花和前面兩個item的煙花節奏保持一致,也就是第三個item的煙花從1000ms開始播放,持續時間500ms;
  4. 可是咱們的定時器又傻乎乎的設定了1500ms,那麼第二個item上的煙花就是綻開一次後,還有500ms的時間播放煙花的前500ms,第三個item上的煙花同樣的道理;
  5. 這就存在問題了,不只不一樣時間點擊的播放的煙花節奏一致,並且會出現錯亂播放的狀況。

那麼咱們必定要使用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圖實現了。動畫

2. animation

相信你們都已經使用過@keyframeanimation實現過動畫了,該功能強大無比,能實現絕多數簡單的動畫需求。
那麼咱們依舊是拿煙花的例子來分析,既然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
    }
}

那麼此時咱們鎖定的是圖片的寬度,而改變的是縱向的位置,那麼問題就獲得瞭解決。

3. ae導出骨骼動畫/ae導出canvas

當動畫很是複雜的時候,好比咱們要實現一個豬的走動,咱們要在豬中止走動的的時候中止頭的晃動,在豬走路的時候讓他的頭晃動起來。利用序列幀使用上述animation也可也實現,可是畢竟序列幀圖片較大,可是有沒有更節省資源的方式呢。

設計師能夠利用ae導出一隻狗的分部位的動畫,就是豬的頭/身體/四肢/尾巴都是拆開的素材,這幾個部位各自旋轉/位移/縮放,組合在一塊兒便可完成一隻豬。

這種方式基本上將設計師的素材拿過來修修補補就能夠了,並且動畫效果很好,缺點就是transform能實現的效果以外的效果就不能使用了,好比形變。

ae導出canvas須要配合lottie庫使用,工做量也都是在設計師那邊。

相關文章
相關標籤/搜索