アニメーションの基本原理#
アニメーションとは#
アニメーションは、互いに差異が極めて小さい連続した画像を迅速に連続して配置することによって、運動の錯覚と変化の錯覚を生み出すプロセスです。
—— ウィキペディア
- 迅速
- 連続配置
- 互いに差異が極めて小さい
- 「錯覚」を生み出すプロセス
アニメーションの発展史#
現在のフロントエンドアニメーション技術は普及しています。
-
一般的なフロントエンドアニメーション技術
- スプライトアニメーション、CSS アニメーション、JS アニメーション、SVG アニメーション、WebGL アニメーション
-
アプリケーションによる分類
- UI アニメーション、Web ベースのゲームアニメーション、アニメーションデータの可視化
GIF や Flash の登場は一時的に主流となりましたが、2000 年頃、Apple は Flash が CPU の負荷を引き起こし、電力消費を加速させると考え、Flash の全面的な放棄を発表しました。その結果、すべての Apple デバイスのバッテリー寿命が著しく向上しました。現在の Web アニメーションは主に CSS と JS アニメーションが中心です。
コンピュータアニメーション#
コンピュータグラフィックス
コンピュータビジョンの基礎であり、点、線、面、体、場の数学的構造方法を含みます。
- 幾何学とグラフィックデータの入力、保存、圧縮。
- テクスチャ、曲線、光影などのアルゴリズムを記述。
- 物体グラフィックのデータ出力(グラフィックインターフェース、アニメーション技術)、ハードウェアとグラフィックの相互作用技術。
- グラフィック開発ソフトウェアの関連技術標準。
コンピュータアニメーションはコンピュータグラフィックスの一分野であり、主に 2D および 3D アニメーションを含みます。アニメーションがどれほど単純であっても、常に 2 つの基本状態、すなわち開始状態と終了状態を定義する必要があります。それらがなければ、補間状態を定義することができず、両者の間の空白を埋めることができません。
迅速√ 連続配置 × 互いに差異が極めて小さい × 錯覚を生み出す ×
上記のアニメーションは迅速であるだけで、錯覚を生み出していないことがわかります。これにはフレームレートという概念が関係しています~~(ゲームをする際にこの概念は皆さんもご存知でしょう)~~
-
フレーム:連続して変化する複数の画面であり、その中の各画面は 1 フレームです。
-
フレームレート:一定の時間内のフレーム数を測定するために使用され、通常の測定単位は **FPS(フレーム毎秒)** です。
-
フレームレートと人間の目:一般的に毎秒 10-12 フレームであれば、人は画面が連続していると認識します。この現象は視覚的残像と呼ばれます。一部のコンピュータアニメーションやゲームでは、30 FPS を下回ると明らかなカクつきを感じます。現在の主流の画面やグラフィックカードの出力は 60FPS であり、効果は明らかに滑らかです。
次に、開始点と終了点の間の空白を埋め、アニメーションを連続させることを試みます。
空白を埋める方法は以下の 2 つです。
- 補間アニメーション
- 伝統的なアニメーションでは、主なアーティストがキーフレームを描き、清書部門に渡し、清書部門の補間アニメーターがキーフレームを補完して納品します。
- (ここでの類似点は、フロントエンドアニメーションの補間アニメーターはブラウザが担います。例えば
@keyframes
、transition
)
- フレームバイフレームアニメーション
- 言葉の意味としては、全てのフレームが手描きであることを意味します。(例えば CSS の steps を使用してスプライトアニメーションを実現)
フロントエンドアニメーションの分類#
CSS アニメーション#
CSS(カスケーディングスタイルシート)は、HTML や XML(SVG、MathML、XHTML などの XML 派生言語を含む)を記述するためのスタイルシート言語です。CSS のanimation
プロパティは、animation-name
、animation-duration
、animation-timing-function
、animation-delay
、animation-iteration-count
、animation-direction
、animation-fill-mode
、およびanimation-play-state
プロパティの簡略形です。
animation-name#
animation-name
プロパティは、適用される一連のアニメーションを指定します。各名前は@keyframes
で定義されたアニメーションシーケンスを表します。その値は以下の通りです。
none
(初期値)特別なキーワードで、キーフレームがないことを示します。他の識別子の順序を変更せずにアニメーションを無効にするか、重なったアニメーションスタイルを無効にします。IDENT
アニメーションを識別する文字列で、大文字と小文字を区別する a-z、数字 0-9、アンダースコア (_) および / またはハイフン (-) で構成されます。最初の非ハイフン文字は文字でなければならず、数字は文字の前に来ることはできず、2 つのハイフンが開始位置に現れることは許可されません。
複数のアニメーション定義はカンマで区切ります。
/* 単一アニメーション */
animation-name: none;
animation-name: test_05;
animation-name: -specific;
animation-name: sliding-vertically;
/* 複数のアニメーション */
animation-name: test1, animation4;
animation-name: none, -moz-specific, sliding;
/* グローバル値 */
animation-name: initial
animation-name: inherit
animation-name: unset
animation-duration#
animation-duration
プロパティは、アニメーションの周期の長さを指定します。デフォルト値は 0s で、アニメーションがないことを示します。
その値はアニメーション周期の長さで、単位は秒 (s) またはミリ秒 (ms) です。単位のない値は無効です。また、複数の値を指定することもでき、その複数の値は animation-name と一対一で対応します。
** 注意:** 負の値は無効であり、ブラウザはその宣言を無視しますが、一部の初期のプレフィックス付きの宣言は負の値を 0s として扱います。
/* 単一アニメーション */
animation-duration: 6s
animation-duration: 120ms
/* 複数のアニメーション */
animation-duration: 1s, 15s
animation-duration: 10s, 30s, 230ms
animation-timing-function#
animation-timing-function
プロパティは、CSS アニメーションが各アニメーション周期内で実行されるリズムを定義します。値は 1 つ以上のものであり、その複数の値も animation-name と一対一で対応します。CSS は幾つかのイージング関数を定義しており、これらのイージング関数を呼び出してスムーズな効果を得ることができます。
キーフレームアニメーションの場合、タイミング関数はキーフレーム周期に作用し、全体のアニメーション周期には作用しません。つまり、キーフレームが開始してからキーフレームが終了するまでの間です。
キーフレームブロックに定義されたイージング関数(アニメーションタイミング関数)はそのキーフレームに適用されます。また、そのキーフレームにイージング関数が定義されていない場合は、全体のアニメーションに定義されたイージング関数が使用されます。
/* キーワード値 */
animation-timing-function: ease;
animation-timing-function: ease-in;
animation-timing-function: ease-out;
animation-timing-function: ease-in-out;
animation-timing-function: linear;
animation-timing-function: step-start;
animation-timing-function: step-end;
/* 関数値 */
animation-timing-function: cubic-bezier(0.1, 0.7, 1.0, 0.1);
animation-timing-function: steps(4, end);
animation-timing-function: frames(10);
/* 複数のアニメーション */
animation-timing-function: ease, step-start, cubic-bezier(0.1, 0.7, 1.0, 0.1);
/* グローバル値 */
animation-timing-function: inherit;
animation-timing-function: initial;
animation-timing-function: unset;
animation-delay#
animation-delay
は、アニメーションがいつ開始されるか、すなわちアニメーションが要素に適用されてからアニメーションが開始されるまでの時間の長さを定義します。(つまり、どれくらい遅れて開始するか)
0s
はこのプロパティのデフォルト値であり、アニメーションが要素に適用された後、すぐに実行されることを示します。そうでない場合、このプロパティの値はアニメーションスタイルが要素に適用された後、開始実行前の時間の長さを示します。
** 負の値を定義すると、アニメーションは即座に開始します。ただし、アニメーションはそのアニメーションシーケンスの特定の位置から開始します。** 例えば、値を - 1s に設定すると、アニメーションはそのアニメーションシーケンスの 1 秒位置から即座に開始します。
アニメーション遅延に負の値を指定した場合、開始値が非表示であれば、アニメーションが要素に適用された瞬間から開始値を取得します。
animation-delay: 3s;
animation-delay: 2s, 4ms;
animation-iteration-count#
animation-iteration-count
は、アニメーションが終了する前に実行される回数を定義します。1 回または無限にループすることができます。
-
infinite
アニメーションを無限にループ再生します。
-
<number>
アニメーションの再生回数;デフォルト値は
1
です。小数でループを定義することができ、アニメーション周期の一部を再生します:例えば、0.5
はアニメーション周期の半分まで再生します。負の値は許可されません。
/* 値はキーワード */
animation-iteration-count: infinite;
/* 値は数字 */
animation-iteration-count: 3;
animation-iteration-count: 2.4;
/* 複数の値を指定 */
animation-iteration-count: 2, 0, infinite;
その複数の値は duration とは異なり、各アニメーションの開始と終了の際に実行回数を切り替えます。
animation-direction#
animation-direction
プロパティは、アニメーションが逆方向に再生されるかどうかを示します。
-
normal
(デフォルト値)各ループ内でアニメーションが前方にループします。言い換えれば、各アニメーションループが終了すると、アニメーションは起点にリセットされて再スタートします。これがデフォルトのプロパティです。
-
alternate
アニメーションが交互に逆方向に実行され、逆方向に実行されると、アニメーションは後退します。同時に、タイミング関数も逆方向になります。例えば、
ease-in
は逆方向ではease-out
になります。カウントは開始時が奇数の反復か偶数の反復かによって決まります。 -
reverse
逆方向にアニメーションを実行し、各周期の終了時にアニメーションが尾から頭に向かって実行されます。
-
alternate-reverse
逆方向に交互に、逆方向から交互に開始します。
アニメーションが最初に逆方向に実行され、次に正方向に、以降は順次ループします。奇数回または偶数回のカウントは 1 から始まります。
animation-direction: normal
animation-direction: reverse
animation-direction: alternate
animation-direction: alternate-reverse
animation-direction: normal, reverse
animation-direction: alternate, reverse, normal
animation-fill-mode#
animation-fill-mode
プロパティは、CSS アニメーションが実行される前後にどのようにスタイルをターゲットに適用するかを設定します。
/* 単一アニメーション */
animation-fill-mode: none;
animation-fill-mode: forwards;
animation-fill-mode: backwards;
animation-fill-mode: both;
/* 複数のアニメーション */
animation-fill-mode: none, backwards;
animation-fill-mode: both, forwards, none;
-
none
(デフォルト)アニメーションが実行されていない場合、アニメーションはターゲットに対して何のスタイルも適用しません。代わりに、その要素に与えられた CSS ルールがその要素を表示します。これがデフォルト値です。
-
forwards
ターゲットは、実行中に遭遇した最後のキーフレームの計算値を保持します。最後のキーフレームは
animation-direction
とanimation-iteration-count
の値によって決まります(つまり、最後のキーフレームがどのように見えるかがその後もずっとそのようになります)。 -
backwards
アニメーションは、ターゲットに適用されたときに最初のキーフレームで定義された値を即座に適用します。そして
animation-delay
の期間この値を保持します。(これは重要です、遅延が数秒かかります)最初のキーフレームはanimation-direction
の値によって決まります:animation-direction
first relevant keyframenormal
oralternate``0%
orfrom``reverse
oralternate-reverse``100%
orto
-
both
アニメーションは
forwards
とbackwards
のルールに従い、アニメーションプロパティを両方向に拡張します。(つまり、上記の二者を兼ね備えています)
注意:
animation-*
プロパティに複数のカンマ区切りの値を指定すると、それらは指定されたアニメーションのanimation-name
プロパティに指定されたアニメーションに対して、値の数に応じて異なる方法で割り当てられます。詳細については、複数のアニメーションプロパティ値を設定するを参照してください。
animation-play-state#
animation-play-state
プロパティは、アニメーションが実行中か一時停止中かを定義します。アニメーションが実行中かどうかを確認するためにクエリすることができます。さらに、その値は一時停止したアニメーションの再生に設定できます。一時停止されたアニメーションを再開すると、一時停止したときから再開され、アニメーションシーケンスの起点からではありません。
-
running
現在のアニメーションが実行中です。
-
paused
現在のアニメーションが停止しています。
/* 単一アニメーション */
animation-play-state: running;
animation-play-state: paused;
/* 複数のアニメーション */
animation-play-state: paused, running, running;
/* グローバル値 */
animation-play-state: inherit;
animation-play-state: initial;
animation-play-state: unset;
この大御所の他のプロジェクトを見てみましたが、どれも面白いです!#codevember - 19 - CSS Eggs (codepen.io)、Periodic Table of Elements - HTML/CSS (codepen.io)
transform API#
transform
プロパティは、指定された要素を回転、スケーリング、傾斜、または平行移動することを可能にします。これは CSS 視覚フォーマットモデルの座標空間を変更することによって実現されます。
transform-origin
は原点の位置を指定し、デフォルトの変換原点はcenter
です。
transform
プロパティは、キーワード値none
または 1 つ以上の<transform-function>
値として指定できます。
-
適用する 1 つまたは複数の CSS 変換関数。変換関数は左から右の順序で掛け算され、これは合成変換が右から左の順序で効果的に適用されることを意味します。
-
scale
(スケーリング)その中心はtransform-origin
です。// x軸を50%に縮小 transform: scale(0.5); // x軸を50%に縮小し、y軸を以前の2倍に拡大 transform: scale(0.5, 2);
-
rotate
(回転)要素を変形せずに原点の周りに回転させます(指定されたtransform-origin
プロパティによって)。移動量は指定された角度によって定義されます。正の値の場合、運動は時計回り、負の値の場合は反時計回りです。180° の回転は点反射 (point reflection) と呼ばれます。transform: rotate(30deg);
-
skew
(傾斜)パラメータは傾斜角度を示し、単位は deg です。一つのパラメータは水平方向の傾斜角度(ax)を示します。
二つのパラメータは水平方向と垂直方向(ax, ay)を示します。
transform: skew(ax) transform: skew(ax, ay)
-
-
none
何の変換も適用しません。
注意:これはボックスモデルで位置付けられた要素のみを変換できます。経験則として、要素が display: block を持つ場合、ボックスモデルで位置付けられた要素です。
transition
は、DOM が読み込まれたときやクラスが変更されたときにトリガーされる遷移アニメーションであり、このプロパティはtransition-property
、transition-duration
、transition-timing-function
、およびtransition-delay
の簡略形です。
/* 1つのプロパティに適用 */
/* プロパティ名 | 持続時間 */
transition: margin-right 4s;
/* プロパティ名 | 持続時間 | 遅延 */
transition: margin-right 4s 1s;
/* プロパティ名 | 持続時間 | タイミング関数 */
transition: margin-right 4s ease-in-out;
/* プロパティ名 | 持続時間 | タイミング関数 | 遅延 */
transition: margin-right 4s ease-in-out 1s;
/* 2つのプロパティに適用 */
transition: margin-right 4s, color 1s;
/* 変更されたすべてのプロパティに適用 */
transition: all 0.5s ease-out;
/* グローバル値 */
transition: inherit;
transition: initial;
transition: unset;
keyframe によるアニメーションの実現#
@keyframes
キーフレーム @keyframes at-rule ルールは、アニメーションシーケンス内のキーフレーム(またはウェイポイント)のスタイルを定義することによって、CSS アニメーションシーケンス内の中間ステップを制御します。 変換 transitionと比較して、キーフレームはアニメーションシーケンスの中間ステップを制御できます。
// 左側からスライドイン
@keyframes slidein {
from {
transform: translateX(0%);
}
to {
transform: translateX(100%);
}
}
//
@keyframes identifier {
0% { top: 0; }
50% { top: 30px; left: 20px; }
50% { top: 10px; }
100% { top: 0; }
}
例:my CSS Animation pratice (codepen.io)
@keyframes identifier {
0% { top: 0; left: 0; }
50% { top: 60%; left: 60%;}
100% { top: 0; left: 0; }
}
@keyframes slidein {
from {
transform: translateX(0%);
}
to {
transform: translateX(100%);
}
}
body >div {
position: absolute;
display:flex;
align-items:center;
justify-content: center;
color: #fafafa;
background-color: #141414;
padding: 10px;
width:20%; height:20%;
/* 左上から右下へ、持続時間5s、遅延1s、無限ループ */
/* animation: identifier 5s linear 1s infinite; */
/* 右にスライド、持続時間1s、2回 */
animation: slidein 1s 2;
}
まとめ:
CSS アニメーションの利点:シンプル、高効率、宣言的です。メインスレッドに依存せず、ハードウェアアクセラレーション(GPU)を使用し、キーフレームアニメーションの再生と一時停止を簡単に制御できます。
欠点:動的にアニメーションを変更または定義することができず、内容が異なるアニメーションは同期を実現できず、複数のアニメーションが互いに重ねることができません。
適用シーン:シンプルな H5 イベント / プロモーションページ。
推奨ライブラリ:Animate.css、CSShakeなど。
SVG によるアニメーションの実現#
SVG は XML ベースのベクターグラフィック記述言語であり、CSS や JS と良好に組み合わせることができ、SVG アニメーションを実現する方法は主に 3 つあります:SMIL、JS、CSS
- SMIL:同期マルチメディア統合言語
- 結論:互換性は理想的ではなく、ここでは多くは議論しませんが、もちろん polyfill のソリューションがあります:https://github.com/ericwilligers/svg-animation
- JS を使用して SVG アニメーションを操作することは言うまでもなく、現在も多くの既存のライブラリがあります。例えば、老舗の Snap.svg や anime.js など、これらを使用することで SVG アニメーションを迅速に作成できます。もちろん、これらのライブラリの他にも、HTML 自体にもネイティブの Web Animation 実装があります。先生の 2 つの例:
- テキスト変形:https://codepen.io/jiangxiang/pen/MWmdjeY
- パスによる文字を書くアニメーション:SVG 書字アニメーション (codepen.io)
最初のアニメーションの実現原理
テキスト溶解原理 - filter#
filter
プロパティは、ぼかしや色の偏移などのグラフィック効果を要素に適用します。フィルターは通常、画像、背景、ボーダーのレンダリングを調整するために使用されます。基本的なケース:https://codepen.io/jiangxiang/pen/XWeQGQK
- blur を徐々に小さくし、blur がほとんどなくなったときに opacity を 0 に設定して隠すことで、溶解効果を実現できます。
JS 筆画原理 - stroke#
stroke-dashoffset、stroke-dasharray を組み合わせて使用して筆画効果を実現します。
stroke-dasharray
プロパティは、描画に使用される点線のパターンを制御します。これは数列であり、数と数の間はカンマまたは空白で区切られ、短い線とギャップの長さを指定します。奇数個の値が提供された場合、その値の数列は 1 回繰り返され、偶数個の値になります。したがって、5,3,2 は 5,3,2,5,3,2 と同じです。
stroke-dashoffset
プロパティは、ダッシュパターンがパスの開始からの距離を指定します。
先生の例:stroke-dasharray&stroke-dashoffset (codepen.io)
// 5px実線 5px空白 x1y1 -> x2y2
<line stroke-dasharray="5, 5" x1="10" y1="10" x2="190" y2="10" />
// 5px実線 10px空白
<line stroke-dasharray="5, 10" x1="10" y1="30" x2="190" y2="30" />
// 10px実線 5px空白
<line stroke-dasharray="10, 5" x1="10" y1="50" x2="190" y2="50" />
// 5px実線 1px空白...
<line stroke-dasharray="5, 1" x1="10" y1="70" x2="190" y2="70" />
<line stroke-dasharray="1, 5" x1="10" y1="90" x2="190" y2="90" />
<line stroke-dasharray="0.9" x1="10" y1="110" x2="190" y2="110" />
<line stroke-dasharray="15, 10, 5" x1="10" y1="130" x2="190" y2="130" />
<line stroke-dasharray="15, 10, 5, 10" x1="10" y1="150" x2="190" y2="150" />
<line stroke-dasharray="15, 10, 5, 10, 15" x1="10" y1="170" x2="190" y2="170" />
// 総長180 180実線 180空白(全て実線)この時、dashoffsetの値を変更することで筆画効果を実現できます。
<line stroke-dasharray="180" stroke-dashoffsetの値を実現できます="0" x1="10" y1="190" x2="190" y2="190" />
直線のような比較的単純なものは、その総長を直接知り、その後 dashoffset を初期化することで筆画効果を実現できますが、不規則な形状はどうでしょうか?
path.getTotalLength();
path パス- d属性を定義します。
大文字の文字は絶対座標 x,y に従い、小文字は相対座標 dx,dyM/m で始点を描画します。
-
L/l は線分を描画します。
C/c はベジェ曲線を描画します。
Z/z は現在の点を始点に直線で接続します。 -
path の長さを計算する - path.getTotalLength ();
-
path 上の特定の点の座標を計算する - path.getPointAtLength (lengthNumber);
先生の例:SVG stroke-dashoffset と stroke-dasharray を使用して筆画効果を実現 (codepen.io)
-
SVG アニメーションの利点:ベクター要素を使用してアニメーションを実現し、異なる画面で良好な鮮明さを得ることができます。特別な効果を実現できます:文字描画、変形、インクの拡散など。
-
欠点:使用方法が複雑であり、過度の使用はパフォーマンスの問題を引き起こす可能性があります。
JS によるアニメーションの実現#
JS は複雑なアニメーションを実現でき、CSS、SVG、さらにはキャンバスアニメーション API 上で描画を操作できます。
どのように選択するか?#
CSS による実現
-
利点
- ブラウザは CSS3 アニメーションに対していくつかの最適化を行い、CSS3 アニメーションのパフォーマンスにわずかな利点をもたらします(アニメーションを実行するために新しいレイヤーを作成します)。
- CSS3 アニメーションのコードは比較的シンプルです。
-
欠点
-
アニメーション制御が柔軟性に欠けます。
-
互換性が悪い。
-
一部のアニメーションは実現できません(視差効果、スクロールアニメーション)。
-
-
シンプルなアニメーションは CSS で実現できます。
JS による実現
-
利点
-
柔軟に使用でき、アニメーションのキーフレームシーケンスを定義する際に、さまざまな条件に応じていくつかのパラメータ(JS アニメーション関数)を調整してアニメーションの方法を変更できます。(CSS では非常に多くのコードの冗長性があります)。
-
CSS のキーフレームよりも粒度が粗い。CSS 自体の時間関数は限られており、この点でJS が補うことができます。
-
CSS は2 つ以上の状態変化を実現するのが難しい(キーフレームを使用するか、複数のアニメーション遅延をトリガーする必要があります。さらに、アニメーションのループ再生や一時停止、逆再生などを考えると、複雑さが非常に高くなります)。
-
-
欠点
- JS を使用すると、調整が CSS よりも簡単ではありません。CSS の調整方法は固定されています。
- パフォーマンスや互換性が悪いブラウザに対して、CSS は優雅にデグレードできますが、JS は追加のコードが必要です。これにより、バンドル後の製品のサイズに影響を与える可能性があります。
まとめ:
- UI 要素に小さな独立した状態を採用する場合は、CSS を使用します。
- アニメーションを大量に制御する必要がある場合は、JavaScript を使用します。
- 特定のシーンでは SVG を使用し、CSS または JS を使用して SVG の変化を操作できます。(例えば、上記の溶解、筆画効果など)。
フロントエンドアニメーションの実現#
JS アニメーション関数の封装#
function animate({easing, draw, duration}) {
let start = performance.now(); // 現在の時間を取得
return new Promise(resolve => {
requestAnimationFrame(function animate(time) {
let timeFraction = (time - start) / duration;
if(timeFraction > 1) timeFraction = 1;
let progress = easing(timeFraction);
draw(progress);
if(timeFraction < 1) {
requestAnimationFrame(animate);
} else {
resolve();
}
});
});
}
この関数では、最初にperformance.now()を使用して現在のシステム時間を取得します。これは一定の速度で徐々に増加し、システム時間の影響を受けず、精度はマイクロ秒単位であり、改ざんされにくいです。引数の説明:
-
draw 描画関数
-
これは、関数の実行に伴い、この描画関数が繰り返し呼び出され、現在の実行進捗 progress が渡されると考えることができます。progress は easing の値に依存します。例えば、線形に増加する場合は 0〜1 です。例えば:
const ball = document.querySelector('.ball'); const draw = (progress) => { ball.style.transform = `translate(${progress}px, 0)`; }
-
-
easing イージング関数
-
イージング関数はアニメーションの時間を変更(または歪め)て、線形 / 非線形、または多次元に変更します。例えば:
easing(timeFraction) { return timeFraction ** 2; //timeFractionの平方 }
-
-
duration 持続時間、言うまでもなく、単位はミリ秒です。
-
最後に Promise を返す理由:
-
Promiseは、非同期操作の最終的な完了または失敗を表すオブジェクトです。
-
アニメーションは連続的であり、Promise は then 関数や await を通じて順次呼び出すことをサポートし、このアニメーションの最終状態を簡単に取得できます。
-
-
このアニメーション関数は、有限時間のアニメーションを封装します。
-
RequestAnimationFrame (rAF) vs SetTimeout vs SetInterval
-
requestAnimationFrameを使用しましょう!なぜ?
この組み込みメソッドは、ブラウザが再描画の準備ができたときに実行されるコールバック関数を設定することを許可します。通常、これは迅速ですが、正確な時間はブラウザによって異なります。
setTimeout や setInterval は、ページがバックグラウンドにあるとき、全く再描画されないため、コールバックは実行されません:アニメーションは一時停止し、リソースを消費しません。参考:javascript - requestAnimationFrame loop not correct FPS - Stack Overflow再配置:レンダリングツリーの一部が更新され、サイズが変化すると再配置が発生します。
再描画:一部のノードが更新される必要がありますが、他の集合の形状は変わりません。例えば、特定の要素の visibility、outline、背景色などを変更すると再描画が発生します。
-
シンプルなアニメーション#
JS でアニメーションを実行する核心的な考え方
∆r = ∆v∆t
シンプルに理解すると:r は距離、v は速度、t は時間です。比例係数を使用してアニメーションのリアル感を保証します。
例えば、等速運動、先生の例は非常に包括的ですJS 封装动画函数 (codepen.io)(ぜひ見てください!)
const ball = document.querySelector('.ball');
const draw = (progress) => {
ball.style.transform = `translate(${progress}px, 0)`;
}
// x軸に沿って等速運動
animate({
duration: 1000,
easing(timeFraction) {
return timeFraction * 100;
},
draw
})
- 重力:h = g * t ^2^
t^2^// 重力
const gravity = () => {
const draw = (progress) => { // 高さ500
ball.style.transform = `translate(0, ${500 * (progress - 1)}px)`;
};
animate({
duration: 1000,
easing(timeFraction) {
return timeFraction ** 2; // tの平方
},
draw,
});
};
- 摩擦力:時間を 2t - t^2^
// 摩擦力
const friction = () => {
// 初期高さ500px
const draw = (progress) => {
ball.style.transform = `translate(0, ${500 * (progress - 1)}px)`;
};
animate({
duration: 1000,
easing(timeFraction) {
// 初期速度係数は2
return timeFraction * (2 - timeFraction);
},
draw,
});
};
- 平抛(x 軸は等速、y 軸は加速)つまり、y 軸は重力の t^2^ に似ており、x 軸は速度が変わりません。
// 平抛 x
const horizontalMotion = () => {
const draw = (progress) => {
ball.style.transform = `translate(${500 * progress.x}px, ${500 * (progress.y - 1)}px)`;
};
// 2つの方向があり、x軸は等速運動、y軸は加速運動
animate({
duration: 1000,
easing(timeFraction) {
return {
x: timeFraction,
y: timeFraction ** 2,
}
},
draw,
});
};
残りのものもたくさんあります。さらに多くの場合、easing の戻り値のオブジェクトに新しいプロパティを追加します。例えば、回転:
-
回転 + 平抛
// 回転 + 平抛 const horizontalMotionWidthRotate = () => { const draw = (progress) => { ball.style.transform = `translate(${500 * progress.x}px, ${500 * (progress.y - 1)}px) rotate(${2000 * progress.rotate}deg)`;// ここでの2000も比例係数です }; // 2つの方向があり、x軸は等速運動、y軸は加速運動 animate({ duration: 2000, easing(timeFraction) { return { x: timeFraction, y: timeFraction ** 2, rotate: timeFraction, } }, draw, }); };
-
弓を引く(x 軸は等速、y 軸の初期速度は負の等加速)
// 弓を引く const arrowMove = () => { // 抽象化し、初期値は2、ある臨界点で正の1に変わり、等速で増加します。 const back = (x, timeFraction) => { return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x); } const draw = (progress) => { ball.style.transform = `translate(${200*progress.x}px, ${ - 500 * progress.y}px)`; }; animate({ duration: 1000, easing(timeFraction) { return { x: timeFraction, y: back(2, timeFraction), }; }, draw, }); };
-
ベジェ曲線 cubic-bezier(0,2.11,1,.19) ✿ cubic-bezier.com、Animated Bézier Curves - Jason Davies
- ps:ここまで来ると徐々にハードコアになってきました、tql
// ベジェ const bezier = () => { const bezierPath = (x1, y1, x2, y2, timeFraction) => { const x = 3 * x1 * timeFraction * (1 - timeFraction) ** 2 + 3 * x2 * timeFraction ** 2 * (1 - timeFraction) + timeFraction ** 3; const y = 3 * y1 * timeFraction * (1 - timeFraction) ** 2 + 3 * y2 * timeFraction ** 2 * (1 - timeFraction) + timeFraction ** 3; return [x, y]; } const draw = (progress) => { const [px, py] = bezierPath(0.2, 0.6, 0.8, 0.2, progress); // 実際に2つの次元で描画します ball.style.transform = `translate(${300 * px}px, ${-300 * py}px)`; } animate({ duration: 2000, easing(timeFraction) { return timeFraction * (2 - timeFraction); }, draw, }); }
複雑なアニメーション#
-
バウンドボール(イージング関数の実装 / 自動減衰の実現)
-
直接先生の例を見てください:JS 封装动画函数 (codepen.io)、自動減衰では、なぜ Promise を使用するのかという以前の穴を埋めました:なぜ毎回実行が完了すると、ハンドルを上の関数に渡して速度が減衰するかどうかを判断し、速度が 0 になるまで自動的に終了します。
-
自動減衰:より複雑です。
-
楕円運動
-
式を適用するだけで、x = a*cos (a)、y = b * sin (a)
// 楕円 const ellipsis = () => { const draw = (progress) => { const x = 150 * Math.cos(Math.PI * 2 * progress); const y = 100 * Math.sin(Math.PI * 2 * progress); ball.style.transform = `translate(${x}px, ${y}px)`; } animate({ duration: 2000, easing(timeFraction) { return timeFraction * (2 - timeFraction); }, draw, }); };
-
関連実践リソース#
アニメーションコードの例:
- CodePenは多くのデザインインスピレーションを提供できます。
- CodeSandboxは SDK の導入に便利です。
デザインサイト:
アニメーション制作ツール(一般的に UE、UI の同僚が使用):
- 2D : Animate CC、After Effects
- 3D 4D、Blender、Autodesk Maya
SVG:
-
Snap.SVG - 現代 SVG グラフィックスの JavaScript ライブラリ
-
Svg.js - SVG を操作およびアニメーションするための軽量ライブラリ。
JS:
-
GSAP - JavaScript アニメーションライブラリ。
-
TweenJS - シンプルでありながら強力な JavaScript 補間 / アニメーションライブラリ。CreateJS ライブラリスイートの一部。
-
Velocity - 加速された JavaScript アニメーション。
CSS:
- Animate.css - CSS アニメーションのクロスブラウザライブラリ。簡単に使用できます。
canvas:
-
EaselJS - EaselJS は HTML5 で高性能なインタラクティブ 2D コンテンツを構築するためのライブラリです。
-
Fabric.js - アニメーションをサポートする JavaScript キャンバスライブラリ。
-
Paper.js - ベクターグラフィックススクリプトのスイスアーミーナイフ。
-
Scriptographer - HTML5
Canvas を JavaScript とブラウザに移植します。
-
Pixijs – 最も速く、最も柔軟な 2D WebGL レンダラーを使用して美しいデジタルコンテンツを作成します。
実際の作業では、UI から提供されたアニメーションフレーム / デザインファイルをコードに変換することがよくあります。
-
フロントエンドが完全にデザインし、開発する必要がある場合:
- 封装されたアニメーションライブラリを使用し、開発コストと体験の観点から取捨選択を行います。
-
デザインがあまり時間がない場合:
- 明瞭さ、画像形式を指定でき、アニメーションはできるだけ示唆または類似のケースを参考に提供することが重要です。スプライト画像リソースなどを要求し、圧縮を手伝う必要があります。(モバイル端末のリソース適応など)
-
デザインリソースが十分な場合:
-
デザインに Lottie 形式のファイルをエクスポートするように要求します。
Lottie は Android、iOS、Web、Windows に適用できるライブラリであり、
Bodymovin を使用して AE アニメーションを解析し、モバイル端末と Web 端末でアニメーションをレンダリングするための json ファイルをエクスポートします。import lottie from 'lottie-web' ; this.animation = lottie.loadAnimation({ container: this.animationRef.current, renderer: 'svg', loop: false, autoplay: false, aninationData: dataJson, path: URL, });
-
最適化#
-
パフォーマンスの観点から
- 重点:再描画、再配置を減らすことが、全体のプロセスで最も時間がかかる 2 つの環節です。
ページレンダリングの一般的なプロセスは JS -> CSS -> スタイル計算 --> レイアウト -> 描画 -> レンダリングレイヤーの統合です。
その中で、Layout(再配置)と Paint(再描画)は全体のプロセスで最も時間がかかる 2 つの環節であるため、これらの 2 つの環節をできるだけ避けるようにします。パフォーマンスの観点から、最も理想的なレンダリングパイプラインはレイアウトと描画環節がないことで、レンダリングレイヤーの統合だけが必要です。
- CSS Triggersを通じて、CSS プロパティとその影響を調べることができます。
提案#
- 実際のアプリケーションでは、最もシンプルな注意点は、アニメーションの開始を display属性値でトリガーしないことです。なぜなら、それは Layout、Paint 環節を引き起こすからです。クラス名を切り替えるだけでも非常に良い方法です。
ps:学びました!これを修正します。
- CSS3 ハードウェアアクセラレーションは GPU アクセラレーションとも呼ばれ、GPU を使用してレンダリングし、CPU 操作を減らす最適化手法です。GPU 内の transform などの CSS プロパティは再描画を引き起こさないため、ウェブページのパフォーマンスを大幅に向上させることができます。CSS の以下のプロパティはハードウェアアクセラレーションを引き起こす可能性があります:
- transform
- opacity
- filter
- Will-change
- 上記のプロパティを必要としない要素がある場合でも、ハードウェアアクセラレーション効果を引き起こすために、ブラウザを誘導するための小さなテクニックを使用できます。
- - アルゴリズムの最適化
- 線形関数を使用して実際の計算を置き換える - 幾何学モデルの最適化
- 衝突検出の最適化 622
- メモリ / キャッシュの最適化 - オフスクリーン描画
まとめと感想#
今日の授業も非常にハードコアで、フロントエンドアニメーションの基本原理、フロントエンドアニメーションの分類、およびフロントエンドアニメーションの実現方法を紹介し、関連リソースと実践方法を紹介しました。フロントエンドアニメーションについてより深く理解できました。最後に提供されたリソースの推薦も非常に役立ちました~
本文で引用された内容の大部分は蒋翔先生の授業、MDN(CSS アニメーションプロパティの紹介を翻訳しました。MDN には非常に包括的で生き生きとした例がたくさんありますので、ぜひご覧ください)から来ています。