[Javascript] Animate機能を使ったの終了イベントの不具合対応

2023年7月25日

Javascript

eyecatch 先日ブログで書いた、javascriptのanimate機能を使って、cssアニメーションをjavascriptで自在にコントロールできていたのですが、 この度、とある問題点があることに気がついたので、その回避方法についてブログに書き残しておきたいと思います。 前回のブログ記事 : [Javascript] style記述せずにできるanimate機能がメタクソ便利

animate機能の問題点

客分は一見にしかずということで、サンプルコードを用意しました。 <link rel='stylesheet' href='style.css'/> <div class='test'></div> <script src='main.js'></script> .test{ width:100px; height:100px; background-color:red; } [data-color='red2blue']{ animation-name : red2blue; animation-duration:3000ms; animation-timing-function: ease-in-out; animation-iteration-count: infinite; } @keyframes red2blue{ from{ background-color:red; } to{ background-color:blue; } } const elm = document.querySelector('.test') elm.setAttribute('data-color' , 'red2blue') elm.animate([ { 'width' : '100px' }, { 'width' : '300px' } ],{ duration : 3000 }) Promise.all(elm.getAnimations().filter(e => e.finished)) .then(e => anim_end(e)) function anim_end(e){ console.log(e) }

問題点の解説

上記のプログラムは、htmlにかかれている、testというclass名を持ったelementのサイズをjavascriptのanimate()機能でセットしているのですが、それに、cssの@keyframeアニメーション機能を付与したものです。 javascriptのgetAnimations()では、javascriptのanimate()機能でセットしたアニメデータのみが取得できると思っていたんですが、どうやらcssの@keyframeデータもセットされているアニメーションとして取得できてしまうようです。 そのため、Promise.allでは、getAnimations()で検出されたアニメーションデータのどちらかが、finishedになった瞬間にanim_end()関数が実行されてしまいます。 しかも、anim_endで受け渡されるデータには、複数のアニメーションデータが送られていて、トラブルを招くこと間違いなしです。

解決方法

そもそも、animate()で実装したアニメ処理の終了をイベント取得したかっただけなので、任意のアニメーションデータを判別して、イベント取得できるようにする方法について解説したいと思います。

animte()にidをセットする

javascriptにできて、cssにできないポイントとして、animate()関数では、option設定の中に、任意のID値をセットすることができます。 なので、 { duration : 3000 } このように指定されているところに、 { id : 'sample', duration : 3000 } というように、id値をセットしてしまえば、getAnimations()で取得する時に、このID値以外は除外してしまえばいいワケです。

任意ID以外のアニメーションデータを除外する方法

const elm = document.querySelector('.test') elm.setAttribute('data-color' , 'red2blue') elm.animate([ { 'width' : '100px' }, { 'width' : '300px' } ],{ id : 'sample', duration : 3000 }) for(const anim of elm.getAnimations()){ if(anim.id !== 'sample'){continue} Promise.all([anim.finished]).then(e => anim_end(e)) } function anim_end(e){ console.log(e) } ※文字色が違う箇所が変更ポイントです。 上記のように、getAnimations()をforループで処理して、id='sample'以外を除外することで、Promise.add()をセットさせてあげればいいんですね。 ちなみに「、Promise.allは、配列しか受け付けないので、該当のアニメデータを、配列で処理してあげるところがポイントです。

効率的な記述方法

上記の変更ポイントをもう少しかっこよく書きたいと思った人は、次のように書くといいでしょう。 Promise.all([elm.getAnimations().find(e => e.id === 'sample').finished]) .then(e => anim_end(e)) for文を回さなくても、配列に対して、find関数で対象の要素を取得する特性を利用して、少しだけシンプルに書くことができます。

関数の特性と汎用性を理解して快適プログラミングライフを

今回は、getAnimations()関数の特性を理解していなかった自分が、便利なanimate()機能やら、Promise機能を使ったイベント処理を実装して、より便利な構造を作ることができて、不具合解消をすることができました。 他にも、setIntervalを常にぶん回しながら、animationの終了イベントを独自で取得する方法や、単にdurationでしてした秒数を、setTimeoutでキャッチして処理するなんて方法もあるかと思いますが、色々な端末におけるブラウザの機能なので、非力な端末の場合は、タイミングのズレが起きる可能性もありますからね。 そんな少しばかりの誤差にも気を配れるようになると、安定したシステム構築ができるハズです。

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。