重新創建推ter點贊動效
最近,我在CodePen上看到了 一個推ter心形點贊動畫 。一般,如果我有時間就會研究案例代碼,看是否可以進行利用或更改。在這個案例中,我很驚訝的看到演示使用的是Sprites圖片。后來我學習了 推ter對其實現的原理 。當然,這可以不使用圖片就可以實現,不是嗎?
我決定自己動手試一試。當然我不決定使用JavaScript,因為這是一個 關于復選框的hack ,可以結合CSS,通過對表單元素進行簡單切換就可以實現。
實現的結果如下:
現在就讓我們看一下如何實現這個動畫效果。
看一下原始的雪碧圖
如上圖所示,它有 29 幀—— 一個用來計算時就會出現問題的數字。因為 29 是一個大的質數,我不能用一個小的數字對其整除,如: 2 、 4 或 5 。但是,這又似乎是一個很好的數字,因為 29 接近于 28 ,也就是 4 的倍數( 4 * 7=28 ),同時,它也接近于 30 也就是 5 的倍數( 5 * 6=30 )。所以我們可以將 29 當做 28 或者 30 進行使用。
我們會注意到雪碧圖主要有三部分組成:
- 心形
- 心形背后產生的氣泡圖形
- 心形周圍的顆粒物圖形
這就意味著可以只使用一個元素和兩個偽元素就可以實現。心形就是那個元素,心形背后的氣泡圖形就是 ::before 偽元素,而心形周圍的顆粒物圖形就是 ::after 偽元素。
巧用復選框技巧
整顆心形和其周圍部分就是復選框的 <label> 標簽。點擊標簽將切換復選框,使我們能夠對 兩種狀態進行處理 。在這種情況下,HTML的結構如下,一個復選框和一個包含Unicode心的標簽。
HTML
<input id="toggle-heart" type="checkbox" />
<label for="toggle-heart">?</label>
讓復選框消失在我們的視野之中:
CSS
[id='toggle-heart'] {
position: absolute;
left: -100vw;
}
根據復選框是否被選中對心形進行顏色設置。我們使用拾色器獲取了雪碧圖真實的顏色值。
CSS
[for='toggle-heart'] {
color: #aab8c2;
}
[id='toggle-heart']:checked + label {
color: #e2264d;
}
居中和放大
我們還在標簽上設置了 cursor:pointer ,并增加了字體的大小,不然我們的心看起來實在太小了。
CSS
[for='toggle-heart'] {
font-size: 2em;
cursor: pointer;
}
之后我們將整顆心進行居中,這里使用的是flexbox。
CSS
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
}
[for='toggle-heart'] {
align-self: center;
}
現在我們就制作了一顆在復選框為未選中狀態時為灰色,選中狀態時為深紅色的心:
制作心大小改變的動畫效果
觀看雪碧圖,心從 2 幀、 6 幀縮放為。 6 幀之后它開始變大,之后的某一點它又縮小了一點點。這種增長效果是使用 easeOutBack 定時功能的完美用例。我們將開始的增長設置為 17.5% ,因為這是一個很好的數字,看起來近似于我們的總幀數。接下來就要思考如何實現縮放。我們不可以使用 scale() 變換,因為其會對后代元素以及對我們的偽元素產生影響。我們不想讓心一下子縮放為0,所以我們決定使用 font-size 。
CSS
@keyframes heart {
0%, 17.5% {
font-size: 0;
}
}
[id='toggle-heart']:checked + label {
will-change: font-size;
animation: heart 1s cubic-bezier(.17, .89, .32, 1.49);
}
上述我們的代碼所實現的效果為:
如果我們不包含 0% 或 100% 幀,它們就會自動獲取元素所設置的值(案例中的 font-size:2em; ),或者,如果我們沒有進行值設置就會獲取默認值(案例中為 1em 的字體大小)。
心形周圍的氣泡圖形
現在我們轉向偽元素進行心形周圍的泡沫圖形(以及下面將要講述的顆粒圖形)的制作。我們將心形標簽設置為 position: relative ,所以我們可以對其進行絕對定位。我們使用 z-index: -1 實現其在心的下方。將 top 以及 left 位置設置為 50% 使其居中顯示。泡沫圖形以及顆粒圖形均為圓形,所以我們設置 border-radius: 50%; 。這里我們使用了SCSS的語法,需要進行一些計算。
SCSS
[for='toggle-heart'] {
position: relative;
&::before,
&::after {
position: absolute;
z-index: -1;
top: 50%;
left: 50%;
border-radius: 50%;
content: '';
}
}
觀看雪碧圖,最大的氣泡圖形是心形的兩倍大小多一點,這里我們將它的直徑設置為 4.5rem 。使用 rem 而不是 em 是因為我們使用 font-size 改變的心形的大小。設置偽元素的大小并將 ::before 偽元素居中。這里我們添加了一個背景色進行測試偽元素是否正確的顯示(稍后就會對其刪除)。
SCSS
$bubble-d: 4.5rem;
$bubble-r: .5 * $bubble-d;
[for='toggle-heart']::before {
margin: -$bubble-r;
width: $bubble-d;
height: $bubble-d;
background: gold;
}
目前為止,效果如下:
從 2 幀到 5 幀,氣泡圖形從無增長到完整大小,顏色也由深紅變為紫色。之后,從 9 幀之后在中間出現了一個孔間隙,它逐漸變大直到和氣泡圖形一般大小。這個增長的大小效果就如動畫的 scale() 轉變實現的效果一樣。我們可以對 border-width 進行動畫設置,從 $bubble-r 到 0 ,實現孔間隙增長的動畫效果。需要注意的是我們需要在 ::before 設置 box-sizing:border-box; 使其工作。
CSS
[for='toggle-heart']::before {
box-sizing: border-box;
border: solid $bubble-r #e2264d;
transform: scale(0);
}
@keyframes bubble {
15% {
border-color: #cc8ef5;
border-width: $bubble-r;
transform: scale(1);
}
30%, 100% {
border-color: #cc8ef5;
border-width: 0;
transform: scale(1);
}
}
這里可以使用一個Mixin書寫這個關鍵幀:
SCSS
@mixin bubble($ext) {
border-color: #cc8ef5;
border-width: $ext;
transform: scale(1);
}
@keyframes bubble {
15% {
@include bubble($bubble-r);
}
30%, 100% {
@include bubble(0);
}
}
使偽元素繼承心形的動畫效果,切換 easeOutCubic 函數并改變動畫的名稱:
SCSS
[id='toggle-heart']:checked + label {
&::before, &::after {
animation: inherit;
animation-timing-function: cubic-bezier(.21, .61, .35, 1);
}
&::before {
will-change: transform, border-color, border-width;
animation-name: bubble;
}
&::after {
animation-name: particles;
}
}
目前的效果如下所示:
心形周圍的顆粒物圖形
觀看雪碧圖可以發現心形周圍有七組兩個圓形的顆粒物圖形,并且分布在一個圓上。
它們的不同之處在于 opacity 、位置以及大小。我們使用多個框的陰影(每一個陰影用于一個顆粒物圖形),之后對偽元素的 opacity 以及框陰影的偏移量進行動畫處理。
我們要做得第一件事情是決定顆粒物圖形的尺寸,之后設置 ::after 偽元素的大小以及位置。
SCSS
$particle-d: 0.375rem;
$particle-r: 0.5 * $particle-d;
[for='toggle-heart']:after {
margin: -$particle-r;
width: $particle-d;
height: $particle-d;
}
我們將七組顆粒物圖形分布在圓周上,如下所示我們有一個 360° 的圓圈:
我們將 360° 盡可能多的進行分組以滿足我們的需要。下面的演示中,多邊形的每一個頂點標志著一個群組。
我們按照順時針,在 x 軸(鐘表的 3 時刻)的 + 開始。如果我們從 y 軸(鐘表的 12 時刻)的 - 開始,對應每一組的位置就需要減去 90° 的視角。
現在看看我們的編碼成果,于頂部開始(鐘表的 12 時刻),在初始化半徑為 $bubble-r 的圓周上分布著我們的泡沫圖形群組。將每一群組的組分視為一個粒子,所需代碼如下:
SCSS
$shadow-list: ();
$n-groups: 7;
$group-base-angle: 360deg/$n-groups;
$group-distr-r: $bubble-r;
@for $i from 0 to $n-groups {
$group-curr-angle: $i*$group-base-angle - 90deg;
$xg: $group-distr-r*cos($group-curr-angle);
$yg: $group-distr-r*sin($group-curr-angle);
$shadow-list: $shadow-list, $xg $yg;
}
在 ::after 偽元素上設置 box-shadow: $shadow-list :
現在處理每一個群組中的兩個粒子。
將每一個群組中的中間點定位在一個圓周(半徑為: ::after 偽元素直徑—— $particle-d )上。
接下來考慮起始角度。因為想要從頂部開始,在每一個群組中起始角度為 -90° 。對于單個粒子,起始角度為組加上所有粒子相對于心形相同的偏移角度。美觀起見,我們選取這個角度為 60° 。
如上所述的代碼實現如下:
SCSS
$shadow-list: ();
$n-groups: 7;
$group-base-angle: 360deg/$n-groups;
$group-distr-r: $bubble-r;
$n-particles: 2;
$particle-base-angle: 360deg/$n-particles;
$particle-off-angle: 60deg;
@for $i from 0 to $n-groups {
$group-curr-angle: $i*$group-base-angle - 90deg;
$xg: $group-distr-r*cos($group-curr-angle);
$yg: $group-distr-r*sin($group-curr-angle);
@for $j from 0 to $n-particles {
$particle-curr-angle: $group-curr-angle +
$particle-off-angle + $j*$particle-base-angle;
$xs: $xg + $particle-d*cos($particle-curr-angle);
$ys: $yg + $particle-d*sin($particle-curr-angle);
$shadow-list: $shadow-list, $xs $ys;
}
}
現在的效果實現如下所示:
彩虹顆粒
現在的位置效果看起來還不錯,只不過所有的顆粒顏色均為所設置的心形 color 值。根據顆粒在 ($i) 中的索引以及 ($j) 中的索引,設置 hsl() 我們可以實現彩虹效果。
SCSS
$shadow-list: $shadow-list, $xs $ys
hsl(($i + $j) * $group-base-angle, 100%, 75%);
簡單的改變就可以實現漂亮的彩虹顆粒效果:
這里也可以實現隨機色相的選取,獲取更加令人滿意的效果。
給顆粒添加動畫的時候,我們想要其起始位置在初始設置的位置稍稍向外一點,也就是半徑為 $bubble-r 的圓周上向外一點。比方說在半徑為 1.25 * $bubble-r 的圓周上。意味著我們需要更改 $group-distr-r 變量。
同時我們希望它們會從當前的尺寸收縮為 0 。收縮框的陰影沒有失去立刻失去焦點,意味著需要設置一個絕對值至少等于元素或者它們所在偽元素的最小尺寸的一半的一個負的半徑擴展值。 ::after 偽元素的尺寸應該為 $particle-d (顆粒直徑),所以我們的傳播半徑為 -$particle-r (顆粒半徑)。
回顧一下,狀態 0 ,在半徑為 $bubble-r 的圓周上散布著顆粒群組并且傳播半徑為 0 。狀態 1 ,群組散布于一個半徑為 1.25 * $bubble-r 的圓周上并且傳播半徑為 -$particle-r 。
如果使用 $k 變量,代碼如下:
SCSS
$group-distr-r: (1 + $k * 0.25) * $bubble-r;
$spread-r: -$k * $particle-r;
我們就需要創建一個Mixin,這樣就不再需要兩次 @for 循環:
SCSS
@mixin particles($k) {
$shadow-list: ();
$n-groups: 7;
$group-base-angle: 360deg / $n-groups;
$group-distr-r: (1 + $k * 0.25)*$bubble-r;
$n-particles: 2;
$particle-base-angle: 360deg / $n-particles;
$particle-off-angle: 60deg; // offset angle from radius
$spread-r: -$k * $particle-r;
@for $i from 0 to $n-groups {
$group-curr-angle: $i * $group-base-angle - 90deg;
$xg: $group-distr-r * cos($group-curr-angle);
$yg: $group-distr-r * sin($group-curr-angle);
@for $j from 0 to $n-particles {
$particle-curr-angle: $group-curr-angle +
$particle-off-angle + $j * $particle-base-angle;
$xs: $xg + $particle-d * cos($particle-curr-angle);
$ys: $yg + $particle-d * sin($particle-curr-angle);
$shadow-list: $shadow-list, $xs $ys 0 $spread-r
hsl(($i + $j) * $group-base-angle, 100%, 75%);
}
}
box-shadow: $shadow-list;
}
這個時刻讓我們再看一遍雪碧圖。顆粒直到第 7 幀才會出現。 7 為 28 (已經很接近我們實際的 29 幀)的四分之一。這意味著我們的基本動畫看起來如下所示:
SCSS
@keyframes particles {
0%, 20% {
opacity: 0;
}
25% {
opacity: 1;
@include particles(0);
}
}
[for='toggle-heart']:after {
@include particles(1);
}
實現的效果所下所示:
調整
除了在Edge/IE瀏覽器中,其余瀏覽器中效果均不錯。在Edge/IE中顆粒幾乎看不到收縮因為其太小,幾乎看不到。一個解決方案為一點點增加它們的擴展半徑的絕對值:
SCSS
$spread-r: -$k * 1.1 * $particle-r;
存在的另外一個問題是某些操作系統會將unicode心轉換為emoji。我找到了一個防止其發生的方案,它看起來很丑陋并且最終被證實不可用。所以最后我決定當復選框沒有被選中以及被選中刪除的時候應用 grayscale(1) 的一個 filter 。
更多的調整就是設置一個好看的 background , font 以及防止心形被選中:
可訪問性
還有一個可訪問性問題: 當使用鍵盤導航時,沒有出現心形切換的視覺效果(此時已經看不見復選框)。出現在腦海的第一個解決方案為,當心形處于焦點狀態時,添加一個 text-shadow 效果。白色似乎是最佳選擇:
CSS
[id='toggle-heart']:focus + label {
text-shadow:
0 0 3px #fff,
0 1px 1px #fff, 0 -1px 1px #fff,
1px 0 1px #fff, -1px 0 1px #fff;
}
使用初始的灰色好像沒有足夠的色彩對比度,所以決定修改為比雪碧圖深一點的灰色。
更新: 在標簽上添加了 arial-label = 'like' 。
最終實現效果
總結
本文作者根據Codepen上的案例進行分析,并且不通過JavaScript代碼實現了推ter心形點贊動畫效果。而且將整個制作過程記錄在案,大家根據文章的講解,可以很好的掌握其實現過程和原理。
其實早在這篇文章之前 @Nicolas Escoffier 寫了一篇有關于 推ter心形點贊的動效 。其實他們制作原理是一樣的,如果你對此感興趣的話,可以閱讀早前翻譯的一篇文章《使用CSS制作Heart動畫》。
本文根據 @ANA TUDOR 的《 Recreating the 推ter Heart Animation 》所譯,整個譯文帶有我們自己的理解與思想,如果譯得不好或有不對之處還請同行朋友指點。如需轉載此譯文,需注明英文出處: https://css-tricks.com/recreating-the-推ter-heart-animation/ 。
靜子
在校學生,本科計算機專業。一個積極進取、愛笑的女生,熱愛前端,喜歡與人交流分享。想要通過自己的努力做到心中的那個自己。微博:@靜-如秋葉
來自: http://www.w3cplus.com/animation/recreating-the-推ter-heart-animation.html