javaScript :ブラウザに表示されたことの検知

●表示されてからアニメ

今回はanime.jsの勉強ではありませんが、内容的には前回の続きです。
前回作成した文字アニメは表示されてから開始するようにしたいです。cssの追加/削除で完結する場合は以下のライブラリが便利だと思います。しかし今回はanime.jsを利用するので、ビューポートに入ったら指定のfunctionを実行する部分を自作しようと思います。

→参考:要素がビューポートに表示されたか判定する -Emergence.js
 
 

●各種パラメータの取得

まずは表示している要素のY座標とスクロールしたときの表示範囲(Y座標)を取得しました。

要素位置と表示位置の取得
http://jquerystudy.info/anime/3_text/04_scroll.html/

 
まずアニメさせたい要素(今回はclass属性がanimeText)のY座標を以下のように取得しています。scrollTopの位置はブラウザによって書き方が変わるのが少し面倒です。

var elms = document.querySelectorAll('.animeText');
elms.forEach(function (elm){
	var elmRect = elm.getBoundingClientRect();	
	var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
	var elmY = elmRect.top + scrollTop;
	elm.innerHTML = elmY;
});

そして表示領域は以下のように記述しました。
スクロール時だけでなくリサイズ時にも取得します。

function setDebugText (){
	var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
	var scrollBottom = scrollTop + window.innerHeight;			
	document.getElementById('debug').innerHTML = scrollTop + ' - ' + scrollBottom;
}

 
 

●classの着脱

アニメを設定する前に表示領域には言ったらclassを追加するサンプルを作成しました。表示領域ギリギリに設定するとclassの着脱が確認できないので100pxのマージンを設定しています。

表示領域に入ったら赤くする
http://jquerystudy.info/anime/3_text/05_check.html/
function checkPos(_min,_max){
	var elms = document.querySelectorAll('.animeText');
	elms.forEach(function (elm){
		var elmRect = elm.getBoundingClientRect() ;
		var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
		var elmY = elmRect.top + scrollTop;		
		var _margin = 100;
		if (elmY > (_min + _margin) && elmY < (_max - _margin)){
			elm.classList.add('vis');
		}else{
			elm.classList.remove('vis');			
		}
	});
}

上記の青字の部分でclassを着脱しています。この処理に関連してaddがclassを重複して追加しないことを確認しました。

var test = document.getElementById('at1').classList.length;
console.log(test);
document.getElementById('at1').classList.add('vis');
document.getElementById('at1').classList.add('vis');
document.getElementById('at1').classList.add('vis');
var test = document.getElementById('at1').classList.length;
console.log(test);

前述のサンプルは最初から表示されている要素は赤くなりません。スクロールかリサイズで初めて判定され赤くなります。これは良くないので、最初に表示されている要素はスクロールしなくても赤くなるようにしました。とはいっても、最初にsetDebugTextを実行するだけです。

setDebugText();//----初期化

 
 

●アニメを付ける

ではアニメを付けていきます。
 

・表示されたときに1度だけアニメ

まずは最初に表示されるときにだけアニメさせるサンプルを作成しました。これはフラグ管理など必要なく、再再生が効かないplayメソッドを利用するだけで完成します。

最初に表示されるときに1回だけ再生
http://jquerystudy.info/anime/3_text/07_inAnime.html/
if (elmY > (_min + _margin) && elmY < (_max - _margin)){
	animeObjList[elm.id].play();//---restartではないので再度再生されることはありません
}

 

・範囲外に出たら逆再生

cssの着脱と同じロジックで簡単に実現できると思ったら躓いた。

逆再生のタイミングがバグってる…
http://jquerystudy.info/anime/3_text/08_reverse_ng.html/
var _margin = 100;
if (elmY > (_min + _margin) && elmY < (_max - _margin)){
	animeObjList[elm.id].play();
}else{
	animeObjList[elm.id].reverse();
}

 
reverseの挙動に疑問を持ったので代わりにseek(0)を利用してリセットするだけにした。これだと正常に機能しているように見える。

範囲外に出たらアニメをリセットする
http://jquerystudy.info/anime/3_text/09_seek0.html/
var _margin = 100;
if (elmY > (_min + _margin) && elmY < (_max - _margin)){
	animeObjList[elm.id].play();
}else{
	animeObjList[elm.id].seek(0);//---範囲外に出たら最初に戻す
}

 
しかしseek(0)ではなくreverseでアニメを戻したい。ということでcssをフラグのように利用して再挑戦。しかし失敗。reverseメソッドは私が考えているような使い方はできないのかもしれない…。

フラグを使ったけどダメでした…
http://jquerystudy.info/anime/3_text/10_reverse_flg.html/
if (elmY > (_min + _margin) && elmY < (_max - _margin)){
	if (elm.classList.contains('vis') == false){
		elm.classList.add('vis');
		animeObjList[elm.id].restart();
	}
}else{
	if (elm.classList.contains('vis') == true){
		elm.classList.remove('vis');
		animeObjList[elm.id].pause();
		animeObjList[elm.id].reverse();
	}
}

 
上記のサンプルではclassの着脱はflgとして利用しているので、視覚的にフラグの状態が分かる。ロジックは正しいのにreverseが処理されない…。ということでreverseを利用しない方向に変更。そもそも逆再生でアニメを戻す方法は融通が利かない、表示するときと消すときは別々のアニメを設定できるようにしたほうが実用的だと思う。
 
ということで最終形。

必要の都度、アニメを設定
http://jquerystudy.info/anime/3_text/11_outAnime.html/

 
上記のサンプルでは、アニメが重複しないようにremoveメソッドで事前のアニメは削除しています。ということで一応removeメソッドの動作を確認。animeオブジェクトをremoveするとアニメはリセットされるのではなく、その場で停止する。この仕様は好都合。

 
おしまい。
とりあえず学びたいことはすべて学んだ感じなので、
ひとまず更新は停止。
また学びたいことができたら再開します。