Wonderful! WordPress

JavaScript で keyframes の登録と Web Animations API

Page No.1

ちょっと必要になって思ったことなのだけれど、ページのロードが終わってから、JavaScript CSS animation@keyframes の生成というか登録はどうやるのだろうかと。

CSSStyleSheet.insertRule

まず、やってみたのは既存のロード済みのスタイルシートに追加する方法。でも、どこかが間違っているのかこの方法ではうまくいかなかった。
で、次にやってみたのが以下の、新規に 生成した ”style” に登録する方法。こちらはちゃんと機能した。
2つの keyframes を登録してる。ちなみに keyframes だけじゃなくて、普通の css のプロパティも当然可能。

let dynamicStyles = null;

function addAnimation( cssstr ) {

    if ( ! dynamicStyles ) {
        dynamicStyles = document.createElement( 'style' );
        // dynamicStyles.type は非推奨とのことで下の一行はなくても機能した
        // dynamicStyles.type = 'text/css';
        document.head.appendChild( dynamicStyles );
    }
    
    dynamicStyles.sheet.insertRule( cssstr, dynamicStyles.length);
}

addAnimation(
    '@keyframes snowfalla{' +
        'from { opacity: 0; top:0px; }' +
        '10% { left: 5px;}' +
        '20% { opacity: 1; top: 100px; transform: rotate( -15deg ); }' +
        '30% { left: -5px; transform: rotate( 15deg ); }' +
        '40% { opacity:0.3; top: 200px;transform: rotate( 80deg ); }' +
        '50% { left: 5px; transform: rotate( 160deg );}' +
        '60% { opacity: 1; top: 300px; transform: rotate( 240deg );  }' +
        '70% { left: -5px;}' +
        '80% { opacity:0.3; top: 500px; }' +
        '90% { left: 5px; opacity: 1; top: 600px;}' +
        'to { opacity:0; top: 700px; }' +
    '}'
);

addAnimation(`
    @keyframes snowfallb{
        from { opacity: 0; top:0px; }
        10% { left: -5px;}
        20% { opacity: 1; top: 100px; transform: rotate( 15deg ); }
        30% { left: 5px; transform: rotate( -15deg ); }
        40% { opacity:0.3; top: 200px; transform: rotate( -80deg ); }
        50% { left: -5px; transform: rotate( -160deg ); }
        60% { opacity: 1; top: 400px; transform: rotate( -240deg ); }
        70% { left: 5px;}
        80% { opacity:0.3; top: 500px; }
        90% { left: -5px; opacity: 1; top: 600px;}
        to { opacity:0; top: 700px; }
    }	
`);
JavaScript
CopyExpand

関数自体は実に簡単なもの。
insertRule の詳細はこちらをご覧あれ → CSSStyleSheet.insertRule()

8行目のコメントアウトしてある type を指定する文は、参考にさせていただいた元のページにあったものだけれど、VScode に「非推奨」とのことで否定線を引かれてしまう。試しにコメントアウトしてみたが、この文は無くても問題なく機能してくれた。
insertRule() の2つ目の引数は、挿入する場所の指定。0 なら先頭に、dynamicStyles.length は最後に挿入するということ。
とりあえず作ったとき、16行目からの keyframes の指定の文字列のように、わかりやすいように改行をつけるために、従来のES5でクォートと+を使って書いていた。が、実際に IE11 で試してみたところ、それでも結局動かなかったので、あえてES5で書く必要は全くなく、31行目からのようにES6のバッククォートを使った書き方なら、実に手間いらずで CSS からそのまま貼り付けるだけですむ。
※注:後日、違うところで普通のスタイル設定をES5で書いてやっていたらIE11でも動いているので、keyframes でなければ、ということのようである。
いやいや違う違う、keyframes でも動きました。ということなので、ES5 で書けば IE11 でも動いてます。と・・・、なぜ初めにやったときは動かなかったのだろう。どこか他にエラーがあったのでしょうね。なんのことやら。

上にも書いたけれど、もちろん普通の css のプロパティも可能なんだけれど、ただ、複数の要素に対してのスタイルの登録に関して、全部まとめて一気にということはできないよう。実際にあれこれやってみたがエラーとなってしまいうまくいかなかった。こういう場合は一つの要素づつ、関数を呼んでやればうまくいった。下のような具合に。

const styles = [
    '#comment_check{visibility:visible;}',
    '#please_check{visibility:hidden;}',
    '#dircomfrm{padding:10px;border:4px double #A09E9E;border-radius: 10px;}',
    '#dircomfrm label{font-size:0.8em;}',
    '#dircomfrm input[type=submit]{display:inline;}',
    '#comeditsec{position:fixed;width:100%;height:100%;top:0px;left:0px;z-index:30;text-align:center;background:linear-gradient(to right,#2F0301,#7F2A28,#DF7C6C,#7F2A28,#2F0301);}',
    '#comeditfrm{position:relative;margin:3% auto;padding:20px;background-color:#f5f5f5;border:4px double #ebebeb;border-radius: 10px;box-shadow:0px 5px 5px 5px rgba(0,0,0,0.4);}',
    '@media screen and (min-width:770px){#comeditfrm{width:600px;}#dircomfrm input[type=text]{display:inline;}}',
    '@media screen and (max-width:770px){#comeditfrm{width:80%;}#dircomfrm input[type=text]{display:block;}}',
    '.comeditorderbt{display:block;width:50px;margin:10px 0;padding:2px;color:#B8A329;text-align:center;background:linear-gradient(#ffffff 0%,#CCCACA 100%);border:solid 1px #aaa;border-radius:5px;text-shadow: 0 1px #fff;box-shadow:0px 1px 2px 2px rgba(50,50,50,0.2);}',
    '.comeditorderbt:hover{cursor:pointer;background-color:#01DFB3;background:linear-gradient(#ffffff 0%,#01DFB3 100%);}'
];

for( let i = 0, ss_len = styles.length; i < ss_len; ++ i ) {
    addAnimation( styles[ i ] );
}
JavaScript
CopyExpand

Web Animations API

ところで、別のやり方として Web Animations API というものもあるとのこと。これを使えば JavaScript からアニメーションを設定したり、動作を制御したり、本格的にアニメーションを操作することができるってことである。
こんなのがちゃんとあったんだね~!って、知らなかったのかよっ!ってか!
いまはメジャーなブラウザなら対応してるよう。
使い方等、詳細はこちらをご覧あれ → Web Animations API を利用する

使うのは何も難しいことはなく、対象の要素にアニメーションの keyframes とその長さやら動きなどのオプションというかプロパティの設定を引数にして animate() を実行させるだけ。

const target = document.getElementById( 'target' );

const snowFalla= [// keyframes
        { opacity: 0, top: '0px' },
        { left: '5px' },
        { opacity: 1, top: '100px', transform: 'rotate( -15deg )' },
        { left: '-5px', transform: 'rotate( 15deg )' },
        { opacity: 0.3, top: '200px', transform: 'rotate( 80deg )' },
        { left: '5px', transform: 'rotate( 160deg )' },
        { opacity: 1, top: '300px', transform: 'rotate( 240deg )'  },
        { left: '-5px' },
        { opacity: 0.3, top: '500px' },
        { left: '5px', opacity: 1, top: '600px' },
        { opacity: 0, top: '700px' }
    ],
    animOpt = {// animation effect timing property
        duration: 3000,// 3s ではなく単位はms
        iterations: Infinity,// "infinite" ではなく代わりに JavaScript の予約語である Infinity を使う
        delay: 1000,
        easing: 'linear'// linear はデフォルトなのでこの行は省略可
    };

target.animate( snowFalla, animOpt );
JavaScript
CopyExpand

CSS とは細かいところで色々と書き方が違ってる。
まず、keyframes において、CSS では動作のタイミングを ”from” とか ”%” で指定していたけれど、それは使わない。何も指定しなければ、ポイントの数で均等割、中間ポイントで50%。指定するなら ”offset:” を使って、たとえば、二行目を30%のタイミングに指定するなら、{ left: ‘5px’, offset: 0.3 } のようにする。

オプションにおける CSS 側との対応は以下のよう。

  • javascript => CSS
  • duration => animation-duration
  • delay => animation-delay
  • fill => animation-fill-mode
  • iterations => animation-iteration-count
  • direction => animation-direction
  • easing => animation-timing-function
  • iterationStart => なし
  • endDelay => なし

CSS側にないもので、iterationStart と endDelay なるものがある。
これはそれぞれ・・・。

iterationStart
英語の説明を読んでもよくわからなかったけれど、ちゃんと説明してくれてるサイトがちゃんとあるものです。
要は、keyframe のどこのポイントからアニメーションをスタートさせるかっていう設定。そしてそこから一周するわけ。途中から終わりまで行って最初に戻って始まったポイントの一つ前まで。0.5 を指定すれば 50%のポイントからスタートする。
これはいいね~、同じアニメーションで変化をつけたい時なんかにつかえそうだ。
endDelay
これは繰り返し動作させてるときに、次のアニメーションの開始との間隔の設定だと思う。実はこれはこれまで何度も設定したい場面があったので、これもありがたい。
と、上のように思っていたのだけれど、実際にやってみたら違っていた。どうもアニメーションが終わったことを引き伸ばすためのものではなかろうか。そこまで確認はしなかったからよくわからないのだけど。
iterationComposite, composite
あと iterationComposite と composite なんてのもあるけれど。
説明を読んだけれど、試したことがないせいかよくわからない。iterationComposite は、たぶんデフォルトではアニメーションを始めるたびに、変化させる価を元にもどして始めるのを、価をそのまま加算させて始めるかという設定だと思う。composite の方は、同時に動いている別のアニメーションで変化させる価を、両方の価を加算させるということだろうか。まぁ、実際にやってみればわかることなのだろうけれど・・・。

全く関係のないことだけれど、この記事をブロックエディタで書いていて気がついた。<dl>タグがないじゃないか。なんで?
と、すぐに innerblock を使って blockを作るということになったのである。

と、まぁ、それはおいといて。

  • iterations : 繰り返し回数。default 1。繰り返しの “infinite” は、代わりに JavaScript の予約語である Infinity を使うというのがちょっと特殊なところ、予約語なのでうっかりクォートをつけないよう。
  • duration と delay の単位は s(秒)ではなく ms
  • direction : ‘normal’, ‘reverse’, ‘alternate’, ‘alternate-reverse’
  • easing : ‘linear'(default), ‘ease’, ‘ease-in’, ‘ease-out’, ‘ease-in-out’, ‘cubic-bexier()’, ‘steps()’
  • fill : ‘forwards’, ‘backwards’, ‘both’, default ‘none’

で、ちょっとここで easing に関しての注釈を。
easingcss animation だと animation-timing-function ということなんだけれど、同じように使えば良いものと思い、実際に Javascript の方でも animation のプロパティの方へ指定してみたところ、全くもって意図したようには動いてくれなかった。css で意図したように動いていたものをそのまま移植したにもかかわらず。いったいこれはどういうことか?となかなか出口を見つけることができなかった。
で、今一度、ネットで確認したところ、「MDN WebDocs Keyframe Formats」 にその答えがしっかりと書いてあった。easingkeyframe の方で指定すればよかった。

const sankochofly = [// animation keyframes
        { backgroundPosition: '0 -10px', offset: 0, easing: 'steps(1)' },
        { backgroundPosition: '0 -245px', offset: 0.25, easing: 'steps(1)' },
        { backgroundPosition: '0 -735px', offset: 0.5, easing: 'steps(1)' },
        { backgroundPosition: '0 -490px', offset: 0.75, easing: 'steps(1)' },
        { backgroundPosition: '0 -10px', offset: 0.95, easing: 'steps(1)' },
        { backgroundPosition: '0 -1470px', offset: 1, easing: 'steps(1)' }
    ];        
JavaScript
CopyExpand

そして、Web Animations API なら、一時停止や再生など、動作の制御もできるメソッドが用意されてる。
たとえば、下のように変数に入れておいて・・・。

const anim = target.animate( snowFalla, animOpt );
PHP
CopyExpand
  • anim.pause()
  • anim.play()
  • anim.finish() :アニメーションの最後までスキップ。
  • anim.cancel():アニメーションを中止し、そのエフェクトを削除。
  • anim.reverse():Animation.playbackRate に負数を設定し、アニメーションの再生方向を逆方向にする。

という具合で使えるとのこと。
finish() においては、Infinity が設定してある animation ではエラーとなってしまい使えなかった( FireFox & chrome )。これは回数である数字を指定すれば使える。で、Infinity の場合、 cancel() が使えた。

そして実際に使ってみて便利だったのが、アニメーションの終了イベントハンドラーである、onfinishjavascript にはもともと css animation が終わったことのイベントとして、animationend というものがあるけれど、それだと全ての animation において引っかけてしまう。使いかたは、上の pausefinish と同じ。あと、oncancel の方もある。
私的には、その対象の animation が終わったら、click イベントと scroll イベントにて処理を指定という具合に使用した。


と、いうことでこれは便利なものがあった。
stylecss を挿入なんてことはしていないで、これを使うべし!って感じですな。

Leave a Reply!

JavaScript is necessary to send a comment.
You can edit and delete your comment if you input a edit key.
Edit key is necessary for attesting you when you edit and delete it.
The tag of HTML cannot be used in comment.
When you comment for the first time, it is displayed after the approval of the administrator.
Because I cannot speak English so much, it takes time to answer.
Required fields are marked *.

※Please enter more than 5 characters only alphabets.
※Edit or delete are possible for 2000 days after approval.

*

♠Simplistic Comment User Editable v4.0

♠When visitors leave comments on the site this site collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.
♠This site does not use cookie when visitors leave comments and commenter edit comment.
♠This site uses Akismet to reduce spam. Learn how your comment data is processed.

Comments feed

Trackback URL : https://strix.main.jp/wp-trackback.php?p=163407

Sanbanse Funabashi

Top

スクロールさせるか画像をクリックすると元に戻ります。