實現前端彈簧動效

人人網FED發表於2018-10-08

彈簧動效是IOS系統原生自帶的一個效果,如在iPhone上面的照片點開大圖的展示效果就是一個彈簧動畫,如下圖所示:

它有一個彈閃的過程,一大一小交替縮放就像一個彈簧在彈動一樣,而不是以往那種簡單的線性變大。

如果使用CSS的animation-timing-function只是改變運動的速度,不能改變運動的方向。

而自己手動寫CSS模擬這種先變大再變小的效果:

@keyframes spring-show {
    0% {
        transform: scale(0);
    }
    90% {
        transform: scale(1);
    }
    /* 先放大一點 */
    95% {
        transform: scale(1.1);
    }
    /* 然後再縮回去 */
    100% {
        transform: scale(1);
    }
}複製程式碼

是沒有這種彈性動感的。

因為要實現一個彈簧振動效果,需要有兩個引數,一個是阻尼係數damping ration,另一個是剛度stiffness,阻尼係數決定了衰減的快慢,剛度決定了往返的週期長短。給定這兩個引數和彈簧的始末位移,根據一些物理公式可以推匯出任意時刻彈簧的位移,這個位移就可以當作上面的scale縮放的值,或者是translate、rotate等的值。

那怎麼算呢?大漠在《CSS如何實現彈簧動畫效果》也詳細地討論了這種效果,並寫一個SASS函式實現,不過這種方式生成的CSS普遍比較大,所以我改用了JS實現,原理都是計算一個CSS的keyframes關鍵幀動畫的在1%, 2%, 3%, ..., 100%的時候屬性值應該是多少,然後再動態地插入一個style標籤。這裡借用了一個css spring的庫,這個庫gzip後只有3KB,使用方法如下:

import spring, { toString } from 'css-spring';
const keyframes = spring(
    { scale: 0 }, // from
    { scale: 1 }, // to
    // precision表示精度有2位
    { damping: 14, stiffness: 170, precision: 2}
);
const keyframeString = toString(keyframes);
console.log(keyframeString);複製程式碼

生成的CSS如下圖所示:

它會有一個大小的變化過程:0 -> 1 -> 1.1 -> 0.99 -> 1,把這些值畫成一個圖表看起來更加直觀:

可以看到它有一個抖動且週期衰減的過程,實際的效果如下圖所示:

除了放大,縮小也能這樣處理,還可以應用於旋轉,效果如下圖所示:

這個是用下面的程式碼生成的:

const keyframes = spring(
    { rotateZ: 30 }, // from
    { rotateZ: 0 }, // to
    { damping: 14, stiffness: 170, precision: 3}
);複製程式碼

當我們需要藉助animation-delay讓3個星星逐個出現的時候,需要先visibility: hidden隱藏然後再出現,這個時候需要在keyframes裡面新增visibility屬性,如下程式碼所示:

let from = {rotateZ: '30', visibility: 'hidden' },
    to = {rotateZ: '0', visibility: 'visible' };
if (from.visibility) {
    keyframes['0%'].visibility = from.visibility;
    keyframes['1%'].visibility = to.visibility;
    // 最後結束animate-fill-mode: forwards使用
    keyframes['100%'].visibility = to.visibility;
}複製程式碼

最後生成一個keyframes:

@keyframes spring-rotate {
    0% {transform:rotateZ(29.1deg);visibility:hidden;}
    1% {transform:rotateZ(27.507deg);visibility:visible;}
    /* ... */
    100% {transform:rotateZ(0deg);visibility:visible;}
}複製程式碼

再讓每個star星星的animation-delay依次增大:

.star {
    visibility: hidden;
    animation: spring-rotate .59s linear forwards;
}
.star:nth-of-type(2) {
    animation-delay: .15s;
}
.star:nth-of-type(3) {
    animation-delay: .3s;
}複製程式碼

這樣就能實現逐個出現的效果了,如下圖所示:

這種彈簧動效能夠增強動感,比普通的單向效果看起來更帶感。

在實際的實現中我寫了一個util,當頁面初始化的時候就生成keyframes,然後插入一個style標籤放在head裡面。因為如果再加上webkit字首,一個keyframes有4KB,10個就有40KB,直接用JS動態計算的方式,會更省空間,靈活性也更強一點。


【人人網招聘中高階前端】

1. 專案背景: 我們在做一個企業級海外的SAAS CRM(客戶管理系統)產品, 前端的技術挑戰很大, 比如在我們的網站讓客戶直接打網路電話(直接打手機那種), 發email, 自動根據使用者場景處理業務等。

2. 技術棧背景: 也是採用比較流行的vue, vuex等框架, 通訊是WebRTC, 訊息分發系統用Google的FCM和蘋果的APN。服務部署在亞馬遜或谷歌雲上。服務全球客戶。

3. 另外因為產品是一個企業級使用者產品所以個方面要求比較高(比如效能,安全,多工處理等)。所以對候選人技術要求比較高,如果您對技術特別在意,那麼我們的空缺提供了很好的才能發揮空間和鍛鍊成長的機會。

請將簡歷發至shanshan.zhu@renren-inc.com


【其它文章】實現WebRTC P2P連線


相關文章