實現前端彈簧動效
彈簧動效是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等的值。
那怎麼算呢?大漠在《 ofollow,noindex" target="_blank">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動態計算的方式,會更省空間,靈活性也更強一點。
Post Views: 5