CSSアニメーションのイージングはcubic-bezierを指定すれば簡単にできますが、ElasticやBounceのような行ったり来たりするイージングはcubic-bezierだけでは指定することはできません。
イージングについてはEasing Functions Cheat Sheetが分かりやすいです。
Easing Functions Cheat Sheet
ほとんどはcubic-bezierで事足りるのですが、より凝ったアニメーションのためにElasticやBounceを使いたいと思い、Sassで簡単に作れるfunctionを書いてみました。
ADs
イージングを計算式で指定することができればなんとかなりそうです。
そこでjQuery Easing Pluginは計算でイージングを定義していたことを思い出し、プラグイン内で使っている関数をパク…参考にしました。
jQuery.easingの関数は一部三角関数を使っているので、Sassで三角関数や各種計算ができるMathSassという関数ライブラリをダウンロードします。
mathsassフォルダ内の「_math.scss」をインポートすれば、JavascriptのMath関数のようにsinやcosが使えるようになります。
1 |
@import "mathsass/math"; |
JavascriptのMathに相当する関数はDart Sassならネイティブにサポートされています。私はLibSassなので…。
sass:math
jQuery.easingのElastic,BounceイージングをSassで使えるように書き直します。
timing-functionが使えるので使うことはないと思いますが、easeOutQuitやeaseInExpoなども関数化することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
@function easeInElastic($t, $b, $c, $d) { $s: 1.70158; $p: 0; $a: $c; @if($t==0) { @return $b; } $t: $t / $d; @if ($t==1) { @return $b+$c; } @if($p==0) { $p: $d * 0.3; } @if ($a < abs($c)) { $a: $c; $s: $p / 4; } @else { $s: ($p / (2 * $PI)) * asin($c / $a); } $t: $t - 1; @return -($a * pow(2, 10 * $t) * sin((($t * $d - $s) * (2 * $PI)) / $p))+$b; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
@function easeOutElastic($t, $b, $c, $d) { $s: 1.70158; $p: 0; $a: $c; @if ($t==0) { @return $b; } $t: $t / $d; @if ($t==1) { @return $b+$c; } @if ($p==0) { $p: $d*0.3; } @if ($a < abs($c)) { $a: $c; $s: $p/4; } @else { $s: $p/(2*$PI) * asin($c/$a); } @return $a*pow(2, -10*$t) * sin(($t * $d - $s)*(2*$PI)/$p)+$c+$b; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
@function easeInOutElastic($t, $b, $c, $d) { $s: 1.70158; $p: 0; $a: $c; @if ($t==0) { @return $b; } $t: $t / ($d/2); @if ($t==2) { @return $b+$c; } @if ($p==0) { $p: $d * (0.3 * 1.5); } @if ($a < abs($c)) { $a: $c; $s: $p/4; } @else { $s: $p/(2*$PI) * asin($c/$a); } @if ($t < 1) { //$t: $t - 1; @return -0.5*($a*pow(2, 10*($t - 1)) * sin(($t * $d - $s)*(2*$PI)/$p))+$b; } @return $a*pow(2, -10*($t - 1)) * sin(($t * $d - $s)*(2*$PI)/$p)*0.5+$c+$b; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@function easeOutBounce($t, $b, $c, $d) { $t: $t / $d; @if ($t < (1/2.75)) { @return $c*(7.5625*$t*$t)+$b; } @else if ($t < (2/2.75)) { $t: $t - (1.5/2.75); @return $c*(7.5625*($t)*$t + .75)+$b; } @else if ($t < (2.5/2.75)) { $t: $t - (2.25/2.75); @return $c*(7.5625*($t)*$t + .9375)+$b; } @else { $t: $t - (2.625/2.75); @return $c*(7.5625*($t)*$t + .984375)+$b; } } |
1 2 3 |
@function easeInBounce($t, $b, $c, $d) { @return $c - easeOutBounce($d - $t, 0, $c, $d)+$b; } |
1 2 3 4 5 6 7 |
@function easeInOutBounce($t, $b, $c, $d) { @if ($t < $d/2) { @return easeInBounce($t*2, 0, $c, $d) * .5+$b; } @return easeOutBounce($t*2 - $d, 0, $c, $d) * .5+$c*.5+$b; } |
これで各イージングを定義するfunctionができました。
次にこれらのイージングをanimationに適用します。
animationプロパティの指定は通常のCSSと同じです。
1 2 3 |
.easing1 { animation: easing1 2s linear forwards; } |
次に「easing1」に適用するためのkeyframesを書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
@keyframes easing1 { /* ここから */ $start: 0; //開始 $end: 400; //終了 $startp: 0; //開始% $endp: 100; //終了% $step: 20; //分割数 $i: 0; $c: $end - $start; @while $i <=$step { $t: (($endp - $startp) / $step / 100) * $i; $d: ($endp - $startp) / 100; $percent: $startp + (($endp - $startp) / $step) * $i; #{$percent *1%} { //ここにプロパティを書く transform: translateX(round(easeInElastic($t, $start, $c, $d) * 100) / 100 * 1px); } $i: $i+1; } /* ここまで */ } |
長くなりましたが、変更箇所は
$wtart,$end,$startp,$endp,$stepの各変数
アニメーションさせるプロパティ(ここではtransform)
の2点です。
$stepを細かくするとなめらかになりますが、その分keyframesを細かく出力するためCSSが長くなります。
プロパティ部分は
easeInElastic
easeOutElastic
easeInOutElastic
easeInBounce
easeOutBounce
easeInOutBounce
から選びます。
コピペで済むもののもうちょっとシンプルにしたい……。
Sassにヒアドキュメントかechoするだけの機能があればなんとかなったのですが。
これらのイージングは近似値をkeyframesに当てはめていますので、$stepと移動距離の兼ね合いによってはなめらかにならない場合があります。
しかし手作業でkeyframesを書くより少しは楽に…なっているでしょうか(;´∀`)
…ここまでやるならanime.jsでいいかな、とも思います。
ADs
こんにちは。まったく同じことをしようとしていて検索して見つけまして、とても参考になりました。
@keyframes内はそのままmixinにしてあげれば、実装はシンプルに済みますよ。具体的には以下のような感じで…
// 追加function、mixin内でeasingを出し分けるための関数
@function customEasingValue($eas, $t, $s, $c, $d, $u: px){
$return: ”;
@if $eas == ie {
$return: easeInElastic($t, $s, $c, $d);
} @elseif $eas == oe {
$return: easeOutElastic($t, $s, $c, $d);
} @elseif $eas == ioe {
$return: easeInOutElastic($t, $s, $c, $d);
} @elseif $eas == ib {
$return: easeInBounce($t, $s, $c, $d);
} @elseif $eas == ob {
$return: easeOutBounce($t, $s, $c, $d);
} @elseif $eas == iob {
$return: easeInOutBounce($t, $s, $c, $d);
}
@return round($return * 100) / 100 * 1#{$u};
}
/**
* 追加mixin、@keyframesの中で呼び出す
* $eas : easing関数の指定
* $prop : アニメーションさせるプロパティ
* $prop2: transform時専用、関数値の指定
* $s : 開始値
* $e : 終了値
* $sp : 開始%
* $ep : 終了%
* $step : 分割数
* $u : 値の単位(px、em、%など)
*/
@mixin customEasingKeyframes($eas, $prop, $prop2: ”, $s: 0, $e: 400, $sp: 0, $ep: 100, $step: 20, $u: px){
$i: 0;
$c: $e – $s;
@while $i <=$step {
$t: (($ep – $sp) / $step / 100) * $i;
$d: ($ep – $sp) / 100;
$percent: $sp + (($ep – $sp) / $step) * $i;
#{$percent * 1%} {
$val: customEasingValue($eas, $t, $s, $c, $d, $u);
$property: $prop;
@if $property == transform {
$val: #{$prop2}#{"("}#{$val}#{")"};
}
#{$property}: #{$val};
}
$i: $i+1;
}
}
こうすれば、@keyframesは以下のような感じで1行で済みます。
@keyframes uekara_ochiru {
@include customEasingKeyframes(ob, margin-top, $s: -60, $e: 0, $u: px);
}
@keyframes araburu_kaiten {
@include customEasingKeyframes(ioe, transform, rotate, -240, 0, $u: deg);
}
2つ以上のプロパティを組み合わせたい場合はcustomEasingKeyframesを増やせばOKです。ただし出力結果がすごい長くなるのが難点ですが…
@keyframes ochi_nagara_araburu {
@include customEasingKeyframes(ob, margin-top, $s: -60, $e: 0, $u: px);
@include customEasingKeyframes(ioe, transform, rotate, -240, 0, $u: deg);
}
transformを複数組み合わせる場合はceky()の$prop2以降を配列にして渡して、$prop2の数だけeachして計算してcustomEasingValue()の結果を半角スペースでつないで…、みたいな感じにすればたぶんいけますが、面倒なのと僕の用途では必要なかったのでやっていません…