まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:animate

jQuery の animate 関数を使う機会があります。

この animate 関数を使うと HTML 要素に比較的簡単にアニメーション要素を加えることができます。サンプルとして 200 x 200 の矩形の左下から右上に <p> 要素を移動させる例を紹介します("animate1" ボタンをクリックすると移動します):

 




このアニメーションのために記述した CSS および JavaScript は以下になります:
<style>
div.sample{
  position: relative;
  border: 1px solid #ccc;
  width: 200px;
  height: 200px;
}
div.sample p.point{
  position: absolute;
  left: 0px;
  top: 180px;
  margin: 0;
  border-radius: 50%;
  width: 20px;
  height: 20px;
  background-color: #d00;
}
</style>
<script>
function animateStraight(){
  var $point = $('#point1');

  var startAnimate1 = function(){
    $point.animate( { left: 180, top: 0 } );
  };

  startAnimate1();
}

</script>

<div  class="sample">
  <p  id="point1" class="point"> </p>
</div>
<button  onclick="animateStraight();">animate1</button>

</body>

初期状態で { left: 0, top: 180 } (矩形の左下)に設置されている id='#point1' の <p> 要素に対して animate() 関数を実行し、そのパラメータに { left: 180, top:0 } (矩形の右上)を指定しています。これだけで現在位置から目的位置までの間を(直線的に)移動するアニメーションが実現できます。

#細かく説明すると、#point1 の <p> 要素の left パラメータを 0 から 180 へ、top パラメータを 180 から 0 へ少しずつ移動させることで実現できています。

ここで紹介したのは位置に関するパラメータでしたが、位置以外でも数字で指定できる要素を使って現在の値から目的の値までアニメーション処理させるだけであれば同様に実現できます。


さて、本ブログエントリで紹介するのは曲線的な軌道でアニメーション移動処理させたい場合の方法です。例えば左下から右上まで、円軌道でアニメーション処理させたい場合にどうすればよいか、という場合の方法を紹介します。

この実現のために animate() 実行時のパラメータの中で step 関数を使います:
    $point.animate(
      { count: 1 },
      {
        step: function( current ){
             :
        }
      }
    );

上の例では値を変化させるパラメータに count を指定しています。値を(初期値の 0 から)1 へ変化させるという指定ですが、この count 自体は位置には関係ありません(つまりこの animate 関数自体では画面上のアニメーションの処理は行っておらず、単に count 変数を 0 から 1 へ変化させているだけです)。

アニメーション処理を行っているのは animate 関数実行時の2つ目のパラメータである step 関数です。この関数は count 関数の値が少しずつ変化するたびに、その値を引数(current)にして実行されるます。なのでこの step 関数の中で current の値を使って要素の移動位置となる x 座標および y 座標を計算して css で位置移動する、という方法によってアニメーション処理を実現します。

具体的にはこのようになります("animate2" ボタンをクリックすると移動します):

 





<style>
div.sample{
  position: relative;
  border: 1px solid #ccc;
  width: 200px;
  height: 200px;
}
div.sample p.point{
  position: absolute;
  left: 0px;
  top: 180px;
  margin: 0;
  border-radius: 50%;
  width: 20px;
  height: 20px;
  background-color: #d00;
}
</style>
<script>
function getXY( t ){
  var r = 180;

  var x = r * t;
  var y = Math.sqrt( ( r * r ) - ( x * x ) );

  return { x: x, y: y };
}

function animateCurve(){
  var $point = $('#point2');

  var current = 0;
  var startAnimate2 = function(){
    $point.animate(
      { count: 1 },
      {
        step: function( current ){
           var point = getXY( current );
           $point.css( { left: point.x, top: point.y } );
        }
      }
    );
  };

  startAnimate2();
}
</script>

<div  class="sample">
  <p  class="point" id="point2"> </p>
</div>
<button  onclick="animateCurve();">animate2</button>

step 関数の中で current の値をパラメータに指定して getXY() 関数を実行しています。getXY() 関数では x2 + y2 = 1802 (中心座標 [0, 0] 、半径 180 の円)上において、[ 0, -180 ] から [ 180, 0 ] まで移動する間の [ x, y ] の軌跡を計算して返しています(※)。また、この getXY() 関数によって返された値を使って、<p> 要素の位置を変える、という処理を加えています。これによって animate() 関数で count の値を 0 から 1 まで直線的に移動させながら、<p> 要素の位置を円曲線に沿って移動させる、という処理を実現しています。

※この円における x 座標は 0 から 180 まで変化します。したがって、x = current * 180 で求めることができます。また x2 + y2 = 1802 より y = (1802 - x2 )の平方根なので、この数値を計算して求めています。これが getXY() 関数の中身です。

この方法を応用することで(曲線の軌道計算式がわかっていれば)animate() 関数で要素を曲線軌道でアニメーション移動させることができます。



jQuery を使って HTML の iframe の内側コンテンツを、iframe の外側の JavaScript からスクロールさせる方法を紹介します。

今回は2つの HTML ファイル(main.html と sub.html)を使います。main.html が iframe の外側(というか、iframe を記述する側)のファイルで、main.html の中の iframe に sub.html が指定されて埋め込まれて表示される、という関係だと想定します。sub.html はどんな内容でも構いませんが、強制スクロールさせて表示することを想定しているので、それなりに縦長の、普通に見ても縦スクロールさせないと全体が見えないくらいの大きさのページを使ってください:
2017113001

一方、iframe の外側である main.html が今回の主役で、main.html 内の JavaScript で sub.html をスクロールさせて表示させることが今回の目的です。実現するためには以下のような内容で記述します:
<html>
<head>
<script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script>
$(function(){
  $('#sub').load( function(){
    iframe_scroll( 100 );
  });
});

function iframe_scroll( top ){
  //. 「0.8 秒で 100 ピクセルスクロールし、0.2 秒待つ」を繰り返す
  $('body,html', $('#sub').contents() ).animate( {scrollTop: top}, 800, 'swing' );
  setTimetout( iframe_scroll, 1000, top + 100 );
}
</script>
<title>main</title>
</head>
<body>
<h1>main</h1>
<hr/>
<iframe id="sub" src="./sub.html" width="90%" height="50%"></iframe>
</body>
</html>

jQuery を使って、まず sub.html がロードされた直後に( $('#sub').load() 関数内で)iframe_scroll 関数を実行しています。この関数では iframe 要素のコンテンツを取り出し、animate 関数を使ってスクロールさせています。この例では 0.8 秒かけて 100 ピクセル下にスクロールし、0.2 秒(1000 ミリ秒 - 800 ミリ秒)止まって繰り返す、という例を実装しています。

なお、sub.html をローカルで用意する代わりに外部コンテンツ(http://www.xxx.com/xxxxx.html など)を指定した場合、iframe 内に表示はされますが、クロスドメイン制約にかかって iframe 内の要素にアクセスすることができず、スクロールは行われません。ご注意ください。


HTML 内のオブジェクトを移動させるには jQuery の animate メソッド を使うのが一般的(というか簡単)ですが、CSS3 の transition を使ってもできます。

プログラマー/デベロッパーの立場だと jQuery を使うことは珍しくないんですが、CSS はちと遠い存在で、基本は理解しているつもりでしたが、あまり接することがありませんでした。いい機会なので勉強がてら使ってみました。


さて、目的は似てるけどアプローチの異なる2つの手法、その違いはどこにあるのでしょう?

簡単に言えば animate は CPU 処理、transition は GPU 併用の処理になります。PC だと見た目にもあまり差はないかもしれませんが、比較的 CPU 性能の劣るスマホ(それもちと古めの)だと、この差がバッチリでそう。グラフィックチップを併用することで非力なCPU環境でもスムーズなアニメーション処理が実現できます。また見た目に差がなくてもCPUの負荷を軽減することにもなります。


実際に2種類のメソッドで同じアニメーションを実装したのが以下の例です。上の "Animate" をクリックすると jQuery.animate で、下の "Transition" をクリックすると CSS3.transition で、それぞれ重ねあわせスライドスクロール処理が行われます。PC ブラウザだとあまり差を感じないかもしれませんが、非力な環境ほど下の方がスムーズな動きになるはず。



Animate









Transition










ソースコード全文はこのページのソースを参照していただきたいのですが、大まかにはこんな感じになります:

共通の CSS クラス:
<style type="text/css">
.c1{
	position: absolute;
	z-index: 1;
	left: 100px;
	opacity: 1;
	width: 100px;
	height: 100px;
	background: yellow;
}
.c2{
	position: absolute;
	z-index: 0;
	left: 100px;
	opacity: 1;
	width: 100px;
	height: 100px;
	background: red;
}
</style>


jQuery.animate を使った場合の HTML & JavaScript コード:
<a  href="javascript:void(0);" id="aa">Animate</a>
<div  class="c1"></div>
<div  id="c2a" class="c2"></div>
<script type="text/javascript">
var ba = true;
$('#aa').on('click', function(){
	ba = !ba;
	var l = 200;
	if( ba ){ l = 100; }
	$('#c2a').animate(
		{ left: l + 'px' },
		{ duration: '700', easing: "linear" }
	);
});
</script>


CSS3.transition を使った場合の HTML & JavaScript コード:
<a  href="javascript:void(0);" id="at">Animate</a>
<div  class="c1"></div>
<div  id="c2t" class="c2"></div>
<script type="text/javascript">

var bt = true;
$('#at').on('click', function(){
	bt = !bt;
	var l = 200;
	if( bt ){ l = 100; }
	$('#c2t').css({
		left: l + 'px',
		WebkitTransition: 'left 0.7s linear',
		MozTransition: 'left 0.7s linear',
		MsTransition: 'left 0.7s linear',
		OTransition: 'left 0.7s linear',
		transition: 'left 0.7s linear'
	});
});
</script>

どちらのケースもクリック時にトグルのような動きをするのでそのための余分なコードがありますが、jQuery.animate では左座標(left)を指定して 700 ミリ秒でリニアにアニメートを、CSS3.transition でも左座標と効果(transition)を指定して 0.7 秒間で遷移するように css を変えています。違いはこの2箇所です。

もちろん全てのケースで有用というわけではないと思います。またこの例はかなりシンプルな内容なので違いが少なく見えていますが、現実的にはそう簡単にはいかないことはあると思います。ただ後者が使える環境であれば後者にすべきなのかな。



 

このページのトップヘ