アニメーションをCSSで書くようになって数年になりますが、凝ったアニメーションを書こうと思うと年々つらくなってきました。
ADs
transitionならcubic-bezierを書くだけでいいですが、@keyframesでイージングを書こうと思うとつらい。
というか人力で書けるものなのだろうか。
transitionend/animationendでクラスを足していってそのクラスに対してアニメーション…つらい。
transitionend/animationendではなくX秒のアニメーションAが終わったらX秒のディレイ後にアニメーションB…とやれば多少は楽ですが、妥協感と将来的に破綻する未来が見える。
単純に行数が増えてつらい。
…この状況を打開するために、JSで制御してみてはどうかと思い、anime.jsというライブラリを試してみました。
「anime.js」でググってみると実装例が見つかります。
公式ドキュメントの日本語訳もおすすめです。
基本的な使い方や概要の説明は他サイトにお願いするとして、当記事では実際の案件で使いそうな実装を試しに書いてみました。
いずれもjQueryを併用していますので、anime.jsと共にロードします。
1 2 |
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/animejs@3.0.1/lib/anime.min.js"></script> |
$(function){…});でロード完了までに見せるアニメーションを定義し、$(window).on(‘load’,function(){….});でロード完了後のエンディング的なアニメーションを定義しています。
ローディングのアニメーションは四角を回しているだけですが、無限ループするアニメーションであれば何でも使えると思います。
1 2 3 4 5 6 |
<div id="loading"> <div></div> </div> <div id="main"> ここが本文 </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
div#loading { display: flex; align-items: center; justify-content: center; position: fixed; left: 0; top: 0; width: 100%; height: 100%; background: #222; } div#loading > div { width: 50px; height: 50px; background: #fff; } |
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 40 41 42 43 |
$(function(){ //ローディング loading = anime({ targets: 'div#loading > div', rotate: '360deg', loop: true, easing: 'linear', duration: 1000 }); }); $(window).on('load',function(){ //ローディングのアニメーションを一旦止める loading.pause(); var loadingTimeline = anime.timeline({ targets: 'div#loading > div', }); //ローディングと同じアニメーションを1回実行しないと変な位置で止まっているおそれがある //横100% → 縦100% → フェードアウト というアニメーションです loadingTimeline.add({ rotate: '360deg', easing: 'linear', duration: 1000 }).add({ width: '100%', easing: 'easeOutExpo', duration: 500 }).add({ height: '100%', easing: 'easeOutExpo', duration: 500 }).add({ targets: 'div#loading', opacity: 0, easing: 'linear', duration: 1000, complete: function(){ $('div#loading').remove(); } }); }); |
anime.jsはプロパティにbackground-positionを取ることもできるため、背景画像のサイズ分横方向に移動させてループすればエンドレスで背景が流れていくアニメーションが実装できます。
1 2 3 |
<div class="loop"> <!-- div.loopにX方向にリピートする背景を設定 --> </div> |
1 2 3 4 5 |
.loop { background: url(house1.png) repeat-x; background-size: auto 200px; height: 200px; } |
1 2 3 4 5 6 7 8 9 |
$(function(){ anime({ targets: '.loop', loop: true, duration: 2000, backgroundPosition: ['0 0','-200px 0'], //背景画像の横幅を指定 easing: 'linear' //linearでないとイージングがかかって滑らかに流れていかない }); }); |
クリックやhoverでの停止を実装したい場合はinfiniteslidev2.jsの利用もご検討ください。
CSS3アニメーションによる無限スクロールを実装するjQueryプラグイン「infiniteslide.js v2」を作成しました
sliderProは機能も豊富で使いやすいカルーセルプラグインです。slick.jsやbxSliderなどを使っていた時期もありましたが、最近はほぼsliderProを使っています。
そのまま設置するだけでは芸がないので、画像が切り替わる前後でアニメーションを入れてアイキャッチ的な演出を狙いました。
div.slidemaskの装飾を工夫すれば(たとえばカーテンや窓の画像にすれば面白いかも)もっと印象深くなると思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<div class="slider-pro"> <div class="sp-slides"> <div class="sp-slide"> <img src="slide1.jpg" alt="" class="sp-image" /> </div> <div class="sp-slide"> <img src="slide2.jpg" alt="" class="sp-image" /> </div> <div class="sp-slide"> <img src="slide3.jpg" alt="" class="sp-image" /> </div> <div class="sp-slide"> <img src="slide4.jpg" alt="" class="sp-image" /> </div> <div class="sp-slide"> <img src="slide5.jpg" alt="" class="sp-image" /> </div> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
.slidemask { position: absolute; right: 0; top: 0; width: 100%; height: 100%; z-index: 10; background: #e91e63; } /* ロード時にカルーセルになっていない画像が表示されるのを防ぐ */ .slider-pro { opacity: 0; } .slider-pro.sp-horizontal { opacity: 1; } |
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(){ $('.slider-pro').append('<div class="slidemask"></div>'); var $slider = $('.slider-pro').sliderPro({ autoplayDelay: 5000, slideAnimationDuration: 1000, init: function(e){ anime({ targets: '.slidemask', width: 0, duration: 1000, easing: 'easeOutExpo' }); }, gotoSlide: function(e){ anime({ targets: '.slidemask', width: '100%', duration: 1000, easing: 'easeOutExpo' }); }, gotoSlideComplete: function(e){ anime({ targets: '.slidemask', width: 0, duration: 1000, easing: 'easeOutExpo' }); } }); }); |
テキストを一文字ずつspanで囲い、順番に拡大されて表示されます。
今回はspanで囲う部分もJavascriptでやってみました。
1 2 3 4 5 |
<div class="wrap"> <div class="text"> あいうえおかきくけこ </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* .wrapはど真ん中に置くためだけのものなので通常は不要と思われる */ .wrap { display: flex; align-items: center; justify-content: center; height: 100vh; } .text { font-size: 2rem; font-weight: 700; text-align: center; } .text > span { display: inline-block; transform: scale(0); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$(function(){ //文字を一文字ずつ囲う処理(タグは入っていない前提です) var text = $('.text').text().replace(/[\s\n ]/g, ''); var html = ''; $.each(text.split(''),function(key,value){ if(value){ html += '<span>' + value + '</span>'; } }); $('.text').html(html); //アニメーション実行 anime({ targets: '.text > span', scale: [0,1], duration: 500, easing: 'easeInElastic(10,1)', delay: anime.stagger(100) }); }); |
横に並べた画像が順番にポコポコと出てきて、その後一定時間ごとにぷよぷよします。
前述の一文字ずつ出てくるアニメーションと考え方は同じです。
1 2 3 4 5 6 7 8 |
<div class="bounce"> <div><img src="house1.png" alt="" /></div> <div><img src="house2.png" alt="" /></div> <div><img src="house3.png" alt="" /></div> <div><img src="house1.png" alt="" /></div> <div><img src="house2.png" alt="" /></div> <div><img src="house3.png" alt="" /></div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
.bounce { display: flex; align-items: flex-end; } .bounce > div { flex: 1; /* 下から出てくるアニメーションなので基準を下に */ transform-origin: center bottom; transform: scaleY(0); backface-visibility: hidden; } .bounce > div img { width: 100%; height: auto; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$(window).on('load',function(){ anime({ targets: '.bounce > div', scaleY: [0,1], duration: 1200, delay: anime.stagger(100), easing: 'easeOutElastic(0,0.3)' }).finished.then(function(){ anime({ targets: '.bounce > div', scaleY: [ {value: [1,0.8],easing: 'easeInElastic(0,2)'}, {value: [0.8,1],easing: 'easeOutElastic(0,2)'} ], duration: 1000, loop: true, delay: anime.stagger(100,{start: 2000}) }); }); }); |
スクロールに応じて要素にアニメーションを実行します。
DEMOではあらかじめtranslateXで文字と写真をずらしておき、表示領域に入ったらtranslateXを0にします。
anime.jsではtransformを使う場合は初期値の設定が必要になるため、現在のtranslateXの値を取るためにコールバック関数を使用しています。
1 2 3 4 5 6 7 8 |
<div class="module inview"> <div class="pict"> <img src="inview.jpg" class="img-fluid" alt="" /> </div> <div class="text"> <p>あああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああ</p> </div> </div> |
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 |
.module { display: flex; align-items: center; background: #eee; padding: 10px; overflow: hidden; } .module + .module { margin-top: 40px; } .module > div { flex: none; } .module .text { width: 60%; padding: 10px; opacity: 0; transform: translateX(100px); box-sizing: border-box; } .module .pict { width: 40%; transform: translateX(-100px); opacity: 0; } .module:nth-child(odd) .pict { order: 10; transform: translateX(100px); } .module:nth-child(odd) .text { transform: translateX(-100px); } .img-fluid { width: 100%; height: auto; } |
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 |
$(function(){ $('.module').on('inview',function(event, visible, topOrBottomOrBoth){ if(visible){ anime({ targets: $(this).find('.text').get(0), opacity: 1, translateX: function(el){ var matrix = $(el).css('transform'); var matrix = matrix.split(','); var matrix = matrix[4].trim(); return [matrix,0]; }, easing: 'easeOutExpo', duration: 1000 }); anime({ targets: $(this).find('.pict').get(0), opacity: 1, translateX: function(el){ var matrix = $(el).css('transform'); var matrix = matrix.split(','); var matrix = matrix[4].trim(); return [matrix,0]; }, delay: 200, easing: 'easeOutExpo', duration: 1000 }); } else { $(this).find('.text').removeAttr('style'); $(this).find('.pict').removeAttr('style'); } }); }); $(window).on('load',function(){ $(window).trigger('checkInView'); }); |
実際に仕事で使いそうな実装をいくつか書いてみましたが、jQueryのanimate()に似ているものの記述はよりシンプル、動作は滑らかという印象です。
(めったに書かないけど)(アニメーション制作費が取れるわけでもないし)長尺のアニメーションが必要なときはanime.jsでやってみようかな、と思わせてくれるものでした。
引き続き使ってみたいと思います。
ADs